Speedup raw / coinbase block fetch from API via index instead of archival usage
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
DataHoarder 2023-07-24 11:48:01 +02:00
parent 0b8823cdf3
commit e32a5e5d74
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
9 changed files with 441 additions and 107 deletions

View file

@ -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)

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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)

View file

@ -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 %}

View file

@ -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

View file

@ -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

View file

@ -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