Speedup raw / coinbase block fetch from API via index instead of archival usage
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
0b8823cdf3
commit
e32a5e5d74
|
@ -13,6 +13,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||
p2poolapi "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
|
@ -176,9 +177,7 @@ func main() {
|
|||
|
||||
var pplnsWeight types.Difficulty
|
||||
|
||||
var errord bool
|
||||
|
||||
for ps := range index.IterateSideBlocksInPPLNSWindow(tip, consensus, indexDb.GetDifficultyByHeight, indexDb.GetTipSideBlockByTemplateId, indexDb.GetSideBlocksByUncleOfId, func(b *index.SideBlock, weight types.Difficulty) {
|
||||
if err := index.IterateSideBlocksInPPLNSWindow(tip, consensus, indexDb.GetDifficultyByHeight, indexDb.GetTipSideBlockByTemplateId, indexDb.GetSideBlocksByUncleOfId, func(b *index.SideBlock, weight types.Difficulty) {
|
||||
miners[indexDb.GetMiner(b.Miner).Id()]++
|
||||
pplnsWeight = pplnsWeight.Add(weight)
|
||||
|
||||
|
@ -196,15 +195,12 @@ func main() {
|
|||
SoftwareString: fmt.Sprintf("%s %s", b.SoftwareId, b.SoftwareVersion),
|
||||
})
|
||||
}
|
||||
}, func(err error) {
|
||||
log.Printf("error scanning PPLNS window: %s", err)
|
||||
errord = true
|
||||
}) {
|
||||
}, func(slot index.SideBlockWindowSlot) {
|
||||
blockCount++
|
||||
uncleCount += len(ps.Uncles)
|
||||
}
|
||||
uncleCount += len(slot.Uncles)
|
||||
}); err != nil {
|
||||
log.Printf("error scanning PPLNS window: %s", err)
|
||||
|
||||
if errord {
|
||||
if oldPoolInfo != nil {
|
||||
// got error, just update last pool
|
||||
|
||||
|
@ -1377,7 +1373,8 @@ func main() {
|
|||
buf, _ := utils.MarshalJSON(raw)
|
||||
_, _ = writer.Write(buf)
|
||||
case "/raw":
|
||||
raw := p2api.ByMainIdWithHint(block.MainId, block.TemplateId)
|
||||
|
||||
raw := p2api.LightByMainIdWithHint(block.MainId, block.TemplateId)
|
||||
|
||||
if raw == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
@ -1391,6 +1388,29 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// Process block if needed
|
||||
if _, err := raw.PreProcessBlockWithOutputs(func(h types.Hash) *sidechain.PoolBlock {
|
||||
b := p2api.LightByTemplateId(h)
|
||||
if len(b) > 0 {
|
||||
return b[0]
|
||||
}
|
||||
return nil
|
||||
}, func() (outputs transaction.Outputs, bottomHeight uint64) {
|
||||
preAllocatedShares := sidechain.PreAllocateShares(consensus.ChainWindowSize * 2)
|
||||
preAllocatedRewards := make([]uint64, 0, len(preAllocatedShares))
|
||||
return Outputs(p2api, indexDb, block, indexDb.DerivationCache(), preAllocatedShares, preAllocatedRewards)
|
||||
}); err != nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: "could_not_process",
|
||||
})
|
||||
_, _ = writer.Write(buf)
|
||||
return
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "text/plain")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
buf, _ := raw.MarshalBinary()
|
||||
|
@ -1406,6 +1426,7 @@ func main() {
|
|||
if shares != nil {
|
||||
poolBlock := p2api.LightByMainId(block.MainId)
|
||||
if poolBlock != nil {
|
||||
|
||||
addresses := make(map[address.PackedAddress]*index.MainCoinbaseOutput, len(shares))
|
||||
for minerId, amount := range PayoutAmountHint(shares, poolBlock.Main.Coinbase.TotalReward) {
|
||||
miner := indexDb.GetMiner(minerId)
|
||||
|
|
|
@ -2,7 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"slices"
|
||||
)
|
||||
|
@ -26,6 +28,78 @@ func PayoutAmountHint(shares map[uint64]types.Difficulty, reward uint64) map[uin
|
|||
return amountShares
|
||||
}
|
||||
|
||||
func Outputs(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock, derivationCache sidechain.DerivationCacheInterface, preAllocatedShares sidechain.Shares, preAllocatedRewards []uint64) (outputs transaction.Outputs, bottomHeight uint64) {
|
||||
if tip == nil {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
poolBlock := p2api.LightByMainId(tip.MainId)
|
||||
if poolBlock != nil {
|
||||
window := index.ChanToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip))
|
||||
if len(window) == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
var hintIndex int
|
||||
|
||||
getByTemplateIdFull := func(h types.Hash) *index.SideBlock {
|
||||
if i := slices.IndexFunc(window, func(e *index.SideBlock) bool {
|
||||
return e.TemplateId == h
|
||||
}); i != -1 {
|
||||
hintIndex = i
|
||||
return window[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
getByTemplateId := func(h types.Hash) *index.SideBlock {
|
||||
//fast lookup first
|
||||
if i := slices.IndexFunc(window[hintIndex:], func(e *index.SideBlock) bool {
|
||||
return e.TemplateId == h
|
||||
}); i != -1 {
|
||||
hintIndex += i
|
||||
return window[hintIndex]
|
||||
}
|
||||
return getByTemplateIdFull(h)
|
||||
}
|
||||
getUnclesOf := func(h types.Hash) chan *index.SideBlock {
|
||||
result := make(chan *index.SideBlock)
|
||||
parentEffectiveHeight := window[hintIndex].EffectiveHeight
|
||||
if window[hintIndex].TemplateId != h {
|
||||
parentEffectiveHeight = 0
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(result)
|
||||
for _, b := range window[hintIndex:] {
|
||||
if b.UncleOf == h {
|
||||
result <- b
|
||||
}
|
||||
if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight {
|
||||
//early exit
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return result
|
||||
}
|
||||
|
||||
return index.CalculateOutputs(indexDb,
|
||||
tip,
|
||||
poolBlock.GetTransactionOutputType(),
|
||||
poolBlock.Main.Coinbase.TotalReward,
|
||||
&poolBlock.Side.CoinbasePrivateKey,
|
||||
poolBlock.Side.CoinbasePrivateKeySeed,
|
||||
p2api.MainDifficultyByHeight,
|
||||
getByTemplateId,
|
||||
getUnclesOf,
|
||||
derivationCache,
|
||||
preAllocatedShares,
|
||||
preAllocatedRewards,
|
||||
)
|
||||
} else {
|
||||
return nil, 0
|
||||
}
|
||||
}
|
||||
|
||||
func PayoutHint(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock) (shares map[uint64]types.Difficulty, blockDepth uint64) {
|
||||
if tip == nil {
|
||||
return nil, 0
|
||||
|
@ -78,19 +152,14 @@ func PayoutHint(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock
|
|||
return result
|
||||
}
|
||||
|
||||
var errorValue error
|
||||
for range index.IterateSideBlocksInPPLNSWindow(tip, indexDb.Consensus(), p2api.MainDifficultyByHeight, getByTemplateId, getUnclesOf, func(b *index.SideBlock, weight types.Difficulty) {
|
||||
blockDepth, err := index.BlocksInPPLNSWindow(tip, indexDb.Consensus(), p2api.MainDifficultyByHeight, getByTemplateId, getUnclesOf, func(b *index.SideBlock, weight types.Difficulty) {
|
||||
if _, ok := shares[b.Miner]; !ok {
|
||||
shares[b.Miner] = types.DifficultyFrom64(0)
|
||||
}
|
||||
shares[b.Miner] = shares[b.Miner].Add(weight)
|
||||
}, func(err error) {
|
||||
errorValue = err
|
||||
}) {
|
||||
blockDepth++
|
||||
}
|
||||
})
|
||||
|
||||
if errorValue != nil {
|
||||
if err != nil {
|
||||
return nil, blockDepth
|
||||
}
|
||||
|
||||
|
|
|
@ -1345,6 +1345,10 @@ func (i *Index) preProcessPoolBlock(b *sidechain.PoolBlock) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (i *Index) DerivationCache() sidechain.DerivationCacheInterface {
|
||||
return i.derivationCache
|
||||
}
|
||||
|
||||
func (i *Index) InsertOrUpdatePoolBlock(b *sidechain.PoolBlock, inclusion BlockInclusion) error {
|
||||
sideBlock, sideUncles, err := i.GetSideBlockFromPoolBlock(b, inclusion)
|
||||
if err != nil {
|
||||
|
|
|
@ -3,9 +3,13 @@ package index
|
|||
import (
|
||||
"fmt"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
|
||||
"git.gammaspectra.live/P2Pool/sha3"
|
||||
"slices"
|
||||
)
|
||||
|
||||
|
@ -19,113 +23,300 @@ type SideBlockWindowSlot struct {
|
|||
Uncles []*SideBlock
|
||||
}
|
||||
|
||||
func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, addWeightFunc SideBlockWindowAddWeightFunc, errorFunc func(err error)) (results chan SideBlockWindowSlot) {
|
||||
results = make(chan SideBlockWindowSlot)
|
||||
// IterateSideBlocksInPPLNSWindow
|
||||
// Copy of sidechain.IterateBlocksInPPLNSWindow
|
||||
func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, addWeightFunc SideBlockWindowAddWeightFunc, slotFunc func(slot SideBlockWindowSlot)) error {
|
||||
|
||||
go func() {
|
||||
defer close(results)
|
||||
cur := tip
|
||||
|
||||
cur := tip
|
||||
var blockDepth uint64
|
||||
|
||||
var blockDepth uint64
|
||||
var mainchainDiff types.Difficulty
|
||||
|
||||
var mainchainDiff types.Difficulty
|
||||
|
||||
if tip.ParentTemplateId != types.ZeroHash {
|
||||
seedHeight := randomx.SeedHeight(tip.MainHeight)
|
||||
mainchainDiff = difficultyByHeight(seedHeight)
|
||||
if mainchainDiff == types.ZeroDifficulty {
|
||||
if errorFunc != nil {
|
||||
errorFunc(fmt.Errorf("couldn't get mainchain difficulty for height = %d", seedHeight))
|
||||
}
|
||||
return
|
||||
}
|
||||
if tip.ParentTemplateId != types.ZeroHash {
|
||||
seedHeight := randomx.SeedHeight(tip.MainHeight)
|
||||
mainchainDiff = difficultyByHeight(seedHeight)
|
||||
if mainchainDiff == types.ZeroDifficulty {
|
||||
return fmt.Errorf("couldn't get mainchain difficulty for height = %d", seedHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic PPLNS window starting from v2
|
||||
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
|
||||
sidechainVersion := sidechain.P2PoolShareVersion(consensus, tip.Timestamp)
|
||||
// Dynamic PPLNS window starting from v2
|
||||
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
|
||||
sidechainVersion := sidechain.P2PoolShareVersion(consensus, tip.Timestamp)
|
||||
|
||||
maxPplnsWeight := types.MaxDifficulty
|
||||
maxPplnsWeight := types.MaxDifficulty
|
||||
|
||||
if sidechainVersion > sidechain.ShareVersion_V1 {
|
||||
maxPplnsWeight = mainchainDiff.Mul64(2)
|
||||
if sidechainVersion > sidechain.ShareVersion_V1 {
|
||||
maxPplnsWeight = mainchainDiff.Mul64(2)
|
||||
}
|
||||
|
||||
var pplnsWeight types.Difficulty
|
||||
|
||||
for {
|
||||
curEntry := SideBlockWindowSlot{
|
||||
Block: cur,
|
||||
}
|
||||
curWeight := types.DifficultyFrom64(cur.Difficulty)
|
||||
|
||||
var pplnsWeight types.Difficulty
|
||||
|
||||
for {
|
||||
curEntry := SideBlockWindowSlot{
|
||||
Block: cur,
|
||||
}
|
||||
curWeight := types.DifficultyFrom64(cur.Difficulty)
|
||||
|
||||
for uncle := range getUnclesByTemplateId(cur.TemplateId) {
|
||||
//Needs to be added regardless - for other consumers
|
||||
if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool {
|
||||
return sideBlock.TemplateId == uncle.TemplateId
|
||||
}) {
|
||||
curEntry.Uncles = append(curEntry.Uncles, uncle)
|
||||
}
|
||||
|
||||
// Skip uncles which are already out of PPLNS window
|
||||
if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize {
|
||||
continue
|
||||
}
|
||||
|
||||
// Take some % of uncle's weight into this share
|
||||
uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(uncle.Difficulty))
|
||||
newPplnsWeight := pplnsWeight.Add(uncleWeight)
|
||||
|
||||
// Skip uncles that push PPLNS weight above the limit
|
||||
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
continue
|
||||
}
|
||||
curWeight = curWeight.Add(unclePenalty)
|
||||
|
||||
if addWeightFunc != nil {
|
||||
addWeightFunc(uncle, uncleWeight)
|
||||
}
|
||||
|
||||
pplnsWeight = newPplnsWeight
|
||||
for uncle := range getUnclesByTemplateId(cur.TemplateId) {
|
||||
//Needs to be added regardless - for other consumers
|
||||
if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool {
|
||||
return sideBlock.TemplateId == uncle.TemplateId
|
||||
}) {
|
||||
curEntry.Uncles = append(curEntry.Uncles, uncle)
|
||||
}
|
||||
|
||||
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||
results <- curEntry
|
||||
// Skip uncles which are already out of PPLNS window
|
||||
if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize {
|
||||
continue
|
||||
}
|
||||
|
||||
// Take some % of uncle's weight into this share
|
||||
uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(uncle.Difficulty))
|
||||
newPplnsWeight := pplnsWeight.Add(uncleWeight)
|
||||
|
||||
// Skip uncles that push PPLNS weight above the limit
|
||||
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
continue
|
||||
}
|
||||
curWeight = curWeight.Add(unclePenalty)
|
||||
|
||||
if addWeightFunc != nil {
|
||||
addWeightFunc(cur, curWeight)
|
||||
addWeightFunc(uncle, uncleWeight)
|
||||
}
|
||||
|
||||
pplnsWeight = pplnsWeight.Add(curWeight)
|
||||
pplnsWeight = newPplnsWeight
|
||||
}
|
||||
|
||||
// One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty
|
||||
if pplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
break
|
||||
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||
slotFunc(curEntry)
|
||||
|
||||
if addWeightFunc != nil {
|
||||
addWeightFunc(cur, curWeight)
|
||||
}
|
||||
|
||||
pplnsWeight = pplnsWeight.Add(curWeight)
|
||||
|
||||
// One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty
|
||||
if pplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
blockDepth++
|
||||
|
||||
if blockDepth >= consensus.ChainWindowSize {
|
||||
break
|
||||
}
|
||||
|
||||
// Reached the genesis block so we're done
|
||||
if cur.SideHeight == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
parentId := cur.ParentTemplateId
|
||||
cur = getByTemplateId(parentId)
|
||||
|
||||
if cur == nil {
|
||||
return fmt.Errorf("could not find parent %s", parentId.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlocksInPPLNSWindow
|
||||
// Copy of sidechain.BlocksInPPLNSWindow
|
||||
func BlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, addWeightFunc SideBlockWindowAddWeightFunc) (bottomHeight uint64, err error) {
|
||||
|
||||
cur := tip
|
||||
|
||||
var blockDepth uint64
|
||||
|
||||
var mainchainDiff types.Difficulty
|
||||
|
||||
if tip.ParentTemplateId != types.ZeroHash {
|
||||
seedHeight := randomx.SeedHeight(tip.MainHeight)
|
||||
mainchainDiff = difficultyByHeight(seedHeight)
|
||||
if mainchainDiff == types.ZeroDifficulty {
|
||||
return 0, fmt.Errorf("couldn't get mainchain difficulty for height = %d", seedHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic PPLNS window starting from v2
|
||||
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
|
||||
sidechainVersion := sidechain.P2PoolShareVersion(consensus, tip.Timestamp)
|
||||
|
||||
maxPplnsWeight := types.MaxDifficulty
|
||||
|
||||
if sidechainVersion > sidechain.ShareVersion_V1 {
|
||||
maxPplnsWeight = mainchainDiff.Mul64(2)
|
||||
}
|
||||
|
||||
var pplnsWeight types.Difficulty
|
||||
|
||||
for {
|
||||
curEntry := SideBlockWindowSlot{
|
||||
Block: cur,
|
||||
}
|
||||
curWeight := types.DifficultyFrom64(cur.Difficulty)
|
||||
|
||||
for uncle := range getUnclesByTemplateId(cur.TemplateId) {
|
||||
//Needs to be added regardless - for other consumers
|
||||
if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool {
|
||||
return sideBlock.TemplateId == uncle.TemplateId
|
||||
}) {
|
||||
curEntry.Uncles = append(curEntry.Uncles, uncle)
|
||||
}
|
||||
|
||||
blockDepth++
|
||||
|
||||
if blockDepth >= consensus.ChainWindowSize {
|
||||
break
|
||||
// Skip uncles which are already out of PPLNS window
|
||||
if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize {
|
||||
continue
|
||||
}
|
||||
|
||||
// Reached the genesis block so we're done
|
||||
if cur.SideHeight == 0 {
|
||||
break
|
||||
// Take some % of uncle's weight into this share
|
||||
uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(uncle.Difficulty))
|
||||
newPplnsWeight := pplnsWeight.Add(uncleWeight)
|
||||
|
||||
// Skip uncles that push PPLNS weight above the limit
|
||||
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
continue
|
||||
}
|
||||
curWeight = curWeight.Add(unclePenalty)
|
||||
|
||||
if addWeightFunc != nil {
|
||||
addWeightFunc(uncle, uncleWeight)
|
||||
}
|
||||
|
||||
parentId := cur.ParentTemplateId
|
||||
cur = getByTemplateId(parentId)
|
||||
pplnsWeight = newPplnsWeight
|
||||
}
|
||||
|
||||
if cur == nil {
|
||||
if errorFunc != nil {
|
||||
errorFunc(fmt.Errorf("could not find parent %s", parentId.String()))
|
||||
}
|
||||
return
|
||||
}
|
||||
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||
bottomHeight = cur.SideHeight
|
||||
|
||||
if addWeightFunc != nil {
|
||||
addWeightFunc(cur, curWeight)
|
||||
}
|
||||
|
||||
pplnsWeight = pplnsWeight.Add(curWeight)
|
||||
|
||||
// One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty
|
||||
if pplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
blockDepth++
|
||||
|
||||
if blockDepth >= consensus.ChainWindowSize {
|
||||
break
|
||||
}
|
||||
|
||||
// Reached the genesis block so we're done
|
||||
if cur.SideHeight == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
parentId := cur.ParentTemplateId
|
||||
cur = getByTemplateId(parentId)
|
||||
|
||||
if cur == nil {
|
||||
return 0, fmt.Errorf("could not find parent %s", parentId.String())
|
||||
}
|
||||
}
|
||||
|
||||
return bottomHeight, nil
|
||||
}
|
||||
|
||||
// GetSharesOrdered
|
||||
// Copy of sidechain.GetSharesOrdered
|
||||
func GetSharesOrdered(indexDb *Index, tip *SideBlock, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, preAllocatedShares sidechain.Shares) (shares sidechain.Shares, bottomHeight uint64) {
|
||||
index := 0
|
||||
l := len(preAllocatedShares)
|
||||
|
||||
if bottomHeight, err := BlocksInPPLNSWindow(tip, indexDb.Consensus(), difficultyByHeight, getByTemplateId, getUnclesByTemplateId, func(b *SideBlock, weight types.Difficulty) {
|
||||
addr := indexDb.GetMiner(b.Miner).Address().ToPackedAddress()
|
||||
if index < l {
|
||||
preAllocatedShares[index].Address = addr
|
||||
|
||||
preAllocatedShares[index].Weight = weight
|
||||
} else {
|
||||
preAllocatedShares = append(preAllocatedShares, &sidechain.Share{
|
||||
Address: addr,
|
||||
Weight: weight,
|
||||
})
|
||||
}
|
||||
index++
|
||||
}); err != nil {
|
||||
return nil, 0
|
||||
} else {
|
||||
shares = preAllocatedShares[:index]
|
||||
|
||||
//remove dupes
|
||||
shares = shares.Compact()
|
||||
|
||||
return shares, bottomHeight
|
||||
}
|
||||
}
|
||||
|
||||
// GetShares
|
||||
// Copy of sidechain.GetShares
|
||||
func GetShares(indexDb *Index, tip *SideBlock, coinbasePrivateKeySeed types.Hash, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, preAllocatedShares sidechain.Shares) (shares sidechain.Shares, bottomHeight uint64) {
|
||||
shares, bottomHeight = GetSharesOrdered(indexDb, tip, difficultyByHeight, getByTemplateId, getUnclesByTemplateId, preAllocatedShares)
|
||||
if shares == nil {
|
||||
return
|
||||
}
|
||||
|
||||
//Shuffle shares
|
||||
sidechain.ShuffleShares(shares, sidechain.P2PoolShareVersion(indexDb.Consensus(), tip.Timestamp), coinbasePrivateKeySeed)
|
||||
|
||||
return shares, bottomHeight
|
||||
}
|
||||
|
||||
// CalculateOutputs
|
||||
// Copy of sidechain.CalculateOutputs
|
||||
func CalculateOutputs(indexDb *Index, block *SideBlock, transactionOutputType uint8, totalReward uint64, coinbasePrivateKey crypto.PrivateKey, coinbasePrivateKeySeed types.Hash, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, derivationCache sidechain.DerivationCacheInterface, preAllocatedShares sidechain.Shares, preAllocatedRewards []uint64) (outputs transaction.Outputs, bottomHeight uint64) {
|
||||
tmpShares, bottomHeight := GetShares(indexDb, block, coinbasePrivateKeySeed, difficultyByHeight, getByTemplateId, getUnclesByTemplateId, preAllocatedShares)
|
||||
if preAllocatedRewards == nil {
|
||||
preAllocatedRewards = make([]uint64, 0, len(tmpShares))
|
||||
}
|
||||
tmpRewards := sidechain.SplitReward(preAllocatedRewards, totalReward, tmpShares)
|
||||
|
||||
if tmpShares == nil || tmpRewards == nil || len(tmpRewards) != len(tmpShares) {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
n := uint64(len(tmpShares))
|
||||
|
||||
outputs = make(transaction.Outputs, n)
|
||||
|
||||
txType := transactionOutputType
|
||||
|
||||
txPrivateKeySlice := coinbasePrivateKey.AsSlice()
|
||||
txPrivateKeyScalar := coinbasePrivateKey.AsScalar()
|
||||
|
||||
var hashers []*sha3.HasherState
|
||||
|
||||
defer func() {
|
||||
for _, h := range hashers {
|
||||
crypto.PutKeccak256Hasher(h)
|
||||
}
|
||||
}()
|
||||
|
||||
return results
|
||||
utils.SplitWork(-2, n, func(workIndex uint64, workerIndex int) error {
|
||||
output := transaction.Output{
|
||||
Index: workIndex,
|
||||
Type: txType,
|
||||
}
|
||||
output.Reward = tmpRewards[output.Index]
|
||||
output.EphemeralPublicKey, output.ViewTag = derivationCache.GetEphemeralPublicKey(&tmpShares[output.Index].Address, txPrivateKeySlice, txPrivateKeyScalar, output.Index, hashers[workerIndex])
|
||||
|
||||
outputs[output.Index] = output
|
||||
|
||||
return nil
|
||||
}, func(routines, routineIndex int) error {
|
||||
hashers = append(hashers, crypto.GetKeccak256Hasher())
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
return outputs, bottomHeight
|
||||
}
|
||||
|
|
|
@ -771,8 +771,30 @@ func getServerMux(instance *P2Pool) *mux.Router {
|
|||
})
|
||||
|
||||
serveMux.HandleFunc("/archive/light_block_by_main_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
params := request.URL.Query()
|
||||
|
||||
var templateIdHint types.Hash
|
||||
if params.Has("templateIdHint") {
|
||||
if h, err := types.HashFromString(params.Get("templateIdHint")); err == nil {
|
||||
templateIdHint = h
|
||||
}
|
||||
}
|
||||
|
||||
if mainId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
if b := archiveCache.LoadByMainId(mainId); b != nil {
|
||||
var b *sidechain.PoolBlock
|
||||
if templateIdHint != types.ZeroHash {
|
||||
// Fast lookup on sidechain
|
||||
if b = instance.SideChain().GetPoolBlockByTemplateId(templateIdHint); b == nil || b.MainId() != mainId {
|
||||
b = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
if b == nil {
|
||||
b = archiveCache.LoadByMainId(mainId)
|
||||
}
|
||||
|
||||
if b != nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, b)
|
||||
|
|
|
@ -147,15 +147,15 @@
|
|||
{% if block.IsOrphan() %}
|
||||
0%
|
||||
{% elseif block.IsUncle() %}
|
||||
{%dul 100-consensus.UnclePenalty %}% (uncle)
|
||||
{%dul 100-consensus.UnclePenalty %}%{%s= ` ` %}(uncle)
|
||||
{% elseif len(block.Uncles) > 0 %}
|
||||
100% + {%dul consensus.UnclePenalty %}% of {%d len(block.Uncles) %} uncle(s)
|
||||
100%{%s= ` ` %}+{%s= ` ` %}{%dul consensus.UnclePenalty %}%{%s= ` ` %}of{%s= ` ` %}{%d len(block.Uncles) %}{%s= ` ` %}uncle(s)
|
||||
{% else %}
|
||||
100%
|
||||
{% endif %}
|
||||
{% case *sidechain.PoolBlock %}
|
||||
{% if len(block.Side.Uncles) > 0 %}
|
||||
100% + {%dul consensus.UnclePenalty %}% of {%d len(block.Side.Uncles) %} uncle(s)
|
||||
100%{%s= ` ` %}+{%s= ` ` %}{%dul consensus.UnclePenalty %}%{%s= ` ` %}of{%s= ` ` %}{%d len(block.Side.Uncles) %}{%s= ` ` %}uncle(s)
|
||||
{% else %}
|
||||
100%
|
||||
{% endif %}
|
||||
|
|
|
@ -116,6 +116,26 @@ func (p *P2PoolApi) LightByMainId(id types.Hash) *sidechain.PoolBlock {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) LightByMainIdWithHint(id, templateIdHint types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/light_block_by_main_id/" + id.String() + "?templateIdHint=" + templateIdHint.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
b := &sidechain.PoolBlock{}
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &b); err != nil || b.ShareVersion() == sidechain.ShareVersion_None {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) ByMainId(id types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/block_by_main_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
|
|
|
@ -537,6 +537,13 @@ func (b *PoolBlock) FromCompactReader(consensus *Consensus, derivationCache Deri
|
|||
|
||||
// PreProcessBlock processes and fills the block data from either pruned or compact modes
|
||||
func (b *PoolBlock) PreProcessBlock(consensus *Consensus, derivationCache DerivationCacheInterface, preAllocatedShares Shares, difficultyByHeight mainblock.GetDifficultyByHeightFunc, getTemplateById GetByTemplateIdFunc) (missingBlocks []types.Hash, err error) {
|
||||
return b.PreProcessBlockWithOutputs(getTemplateById, func() (outputs transaction.Outputs, bottomHeight uint64) {
|
||||
return CalculateOutputs(b, consensus, difficultyByHeight, getTemplateById, derivationCache, preAllocatedShares, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// PreProcessBlockWithOutputs processes and fills the block data from either pruned or compact modes
|
||||
func (b *PoolBlock) PreProcessBlockWithOutputs(getTemplateById GetByTemplateIdFunc, calculateOutputs func() (outputs transaction.Outputs, bottomHeight uint64)) (missingBlocks []types.Hash, err error) {
|
||||
|
||||
getTemplateByIdFillingTx := func(h types.Hash) *PoolBlock {
|
||||
chain := make(UniquePoolBlockSlice, 0, 1)
|
||||
|
@ -589,7 +596,7 @@ func (b *PoolBlock) PreProcessBlock(consensus *Consensus, derivationCache Deriva
|
|||
}
|
||||
|
||||
if len(b.Main.Coinbase.Outputs) == 0 {
|
||||
if outputs, _ := CalculateOutputs(b, consensus, difficultyByHeight, getTemplateById, derivationCache, preAllocatedShares, nil); outputs == nil {
|
||||
if outputs, _ := calculateOutputs(); outputs == nil {
|
||||
return nil, errors.New("error filling outputs for block: nil outputs")
|
||||
} else {
|
||||
b.Main.Coinbase.Outputs = outputs
|
||||
|
|
|
@ -479,7 +479,7 @@ func SplitReward(preAllocatedRewards []uint64, reward uint64, shares Shares) (re
|
|||
return nil
|
||||
}
|
||||
|
||||
rewards = preAllocatedRewards
|
||||
rewards = preAllocatedRewards[:0]
|
||||
|
||||
var w types.Difficulty
|
||||
var rewardGiven uint64
|
||||
|
|
Loading…
Reference in a new issue