DataHoarder
71c27726be
All checks were successful
continuous-integration/drone/push Build is passing
242 lines
8.3 KiB
Go
242 lines
8.3 KiB
Go
package index
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
|
|
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
|
|
p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
|
"slices"
|
|
"unsafe"
|
|
)
|
|
|
|
type BlockInclusion int
|
|
|
|
const (
|
|
// InclusionOrphan orphan (was not included in-verified-chain)
|
|
InclusionOrphan = BlockInclusion(iota)
|
|
// InclusionInVerifiedChain in-verified-chain (uncle or main)
|
|
InclusionInVerifiedChain
|
|
// InclusionAlternateInVerifiedChain alternate in-verified-chain (uncle or main), for example when duplicate nonce happens
|
|
InclusionAlternateInVerifiedChain
|
|
|
|
InclusionCount
|
|
)
|
|
|
|
const SideBlockSelectFields = "main_id, main_height, template_id, side_height, parent_template_id, miner, uncle_of, effective_height, nonce, extra_nonce, timestamp, software_id, software_version, window_depth, window_outputs, transaction_count, difficulty, cumulative_difficulty, pow_difficulty, pow_hash, inclusion"
|
|
|
|
type SideBlock struct {
|
|
// MainId mainchain id, on Monero network
|
|
MainId types.Hash `json:"main_id"`
|
|
MainHeight uint64 `json:"main_height"`
|
|
|
|
// TemplateId -- sidechain template id. Note multiple blocks can exist per template id, see Inclusion
|
|
TemplateId types.Hash `json:"template_id"`
|
|
SideHeight uint64 `json:"side_height"`
|
|
// ParentTemplateId previous sidechain template id
|
|
ParentTemplateId types.Hash `json:"parent_template_id"`
|
|
|
|
// Miner internal id of the miner who contributed the block
|
|
Miner uint64 `json:"miner"`
|
|
|
|
// Uncle inclusion information
|
|
|
|
// UncleOf has been included under this parent block TemplateId as an uncle
|
|
UncleOf types.Hash `json:"uncle_of,omitempty"`
|
|
// EffectiveHeight has been included under this parent block height as an uncle, or is this height
|
|
EffectiveHeight uint64 `json:"effective_height"`
|
|
|
|
// Nonce data
|
|
Nonce uint32 `json:"nonce"`
|
|
ExtraNonce uint32 `json:"extra_nonce"`
|
|
|
|
Timestamp uint64 `json:"timestamp"`
|
|
SoftwareId p2pooltypes.SoftwareId `json:"software_id"`
|
|
SoftwareVersion p2pooltypes.SoftwareVersion `json:"software_version"`
|
|
// WindowDepth PPLNS window depth, in blocks including this one
|
|
WindowDepth uint32 `json:"window_depth"`
|
|
WindowOutputs uint32 `json:"window_outputs"`
|
|
|
|
// Difficulty sidechain difficulty at height
|
|
Difficulty uint64 `json:"difficulty"`
|
|
CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"`
|
|
PowDifficulty uint64 `json:"pow_difficulty"`
|
|
// PowHash result of PoW function as a hash (all 0x00 = not known)
|
|
PowHash types.Hash `json:"pow_hash"`
|
|
|
|
Inclusion BlockInclusion `json:"inclusion"`
|
|
|
|
TransactionCount uint32 `json:"transaction_count"`
|
|
|
|
// Extra information filled just for JSON purposes
|
|
|
|
MinedMainAtHeight bool `json:"mined_main_at_height,omitempty"`
|
|
MinerAddress *address.Address `json:"miner_address,omitempty"`
|
|
MinerAlias string `json:"miner_alias,omitempty"`
|
|
Uncles []SideBlockUncleEntry `json:"uncles,omitempty"`
|
|
MainDifficulty uint64 `json:"main_difficulty,omitempty"`
|
|
}
|
|
|
|
type SideBlockUncleEntry struct {
|
|
TemplateId types.Hash `json:"template_id"`
|
|
Miner uint64 `json:"miner"`
|
|
SideHeight uint64 `json:"side_height"`
|
|
Difficulty uint64 `json:"difficulty"`
|
|
}
|
|
|
|
// FromPoolBlock block needs to be pre-processed for ids to be correct
|
|
// These fields need to be filled by caller to match needs:
|
|
// SideBlock.UncleOf
|
|
// SideBlock.EffectiveHeight
|
|
// SideBlock.WindowDepth
|
|
// SideBlock.Inclusion
|
|
func (b *SideBlock) FromPoolBlock(i *Index, block *sidechain.PoolBlock, getSeedByHeight mainblock.GetSeedByHeightFunc) error {
|
|
b.MainId = block.MainId()
|
|
b.TemplateId = block.SideTemplateId(i.consensus)
|
|
|
|
if b.MainId == types.ZeroHash {
|
|
return errors.New("invalid main id")
|
|
}
|
|
if b.TemplateId == types.ZeroHash || bytes.Compare(b.TemplateId[:], block.CoinbaseExtra(sidechain.SideTemplateId)) != 0 {
|
|
return errors.New("invalid template id")
|
|
}
|
|
b.MainHeight = block.Main.Coinbase.GenHeight
|
|
b.SideHeight = block.Side.Height
|
|
b.ParentTemplateId = block.Side.Parent
|
|
b.Miner = i.GetOrCreateMinerPackedAddress(block.GetAddress()).id
|
|
b.Nonce = block.Main.Nonce
|
|
b.ExtraNonce = block.ExtraNonce()
|
|
b.Timestamp = block.Main.Timestamp
|
|
b.SoftwareId = block.Side.ExtraBuffer.SoftwareId
|
|
b.SoftwareVersion = block.Side.ExtraBuffer.SoftwareVersion
|
|
b.WindowOutputs = uint32(len(block.Main.Coinbase.Outputs))
|
|
b.TransactionCount = uint32(len(block.Main.Transactions))
|
|
b.Difficulty = block.Side.Difficulty.Lo
|
|
b.CumulativeDifficulty = block.Side.CumulativeDifficulty
|
|
b.PowHash = block.PowHash(i.Consensus().GetHasher(), getSeedByHeight)
|
|
if b.PowHash == types.ZeroHash {
|
|
return errors.New("invalid pow hash")
|
|
}
|
|
b.PowDifficulty = types.DifficultyFromPoW(b.PowHash).Lo
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *SideBlock) SetUncleOf(block *SideBlock) error {
|
|
if block == nil {
|
|
b.UncleOf = types.ZeroHash
|
|
b.EffectiveHeight = b.SideHeight
|
|
return nil
|
|
}
|
|
if block.IsUncle() {
|
|
return errors.New("parent cannot be uncle")
|
|
}
|
|
if block.SideHeight <= b.SideHeight {
|
|
return errors.New("parent side height cannot be lower or equal")
|
|
}
|
|
b.UncleOf = block.TemplateId
|
|
b.EffectiveHeight = block.EffectiveHeight
|
|
return nil
|
|
}
|
|
|
|
// IsTipOfHeight whether this block is considered to be the "main" block within this height, not an uncle, or alternate
|
|
func (b *SideBlock) IsTipOfHeight() bool {
|
|
return b.SideHeight == b.EffectiveHeight && b.Inclusion == InclusionInVerifiedChain
|
|
}
|
|
|
|
func (b *SideBlock) FullId() sidechain.FullId {
|
|
var buf sidechain.FullId
|
|
copy(buf[:], b.TemplateId[:])
|
|
binary.LittleEndian.PutUint32(buf[types.HashSize:], b.Nonce)
|
|
binary.LittleEndian.PutUint32(buf[types.HashSize+unsafe.Sizeof(b.Nonce):], b.ExtraNonce)
|
|
return buf
|
|
}
|
|
|
|
func (b *SideBlock) IsUncle() bool {
|
|
return b.SideHeight != b.EffectiveHeight && b.UncleOf != types.ZeroHash
|
|
}
|
|
|
|
func (b *SideBlock) IsOrphan() bool {
|
|
return b.Inclusion == InclusionOrphan
|
|
}
|
|
|
|
func (b *SideBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error {
|
|
if err := row.Scan(&b.MainId, &b.MainHeight, &b.TemplateId, &b.SideHeight, &b.ParentTemplateId, &b.Miner, &b.UncleOf, &b.EffectiveHeight, &b.Nonce, &b.ExtraNonce, &b.Timestamp, &b.SoftwareId, &b.SoftwareVersion, &b.WindowDepth, &b.WindowOutputs, &b.TransactionCount, &b.Difficulty, &b.CumulativeDifficulty, &b.PowDifficulty, &b.PowHash, &b.Inclusion); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//Utilities to be used by JSON decoded blocks
|
|
|
|
func (b *SideBlock) Weight(tipHeight, windowSize, consensusUnclePenalty uint64) (weight, parentWeight uint64) {
|
|
if (tipHeight - b.SideHeight) >= windowSize {
|
|
return 0, 0
|
|
}
|
|
if b.IsUncle() {
|
|
unclePenalty := types.DifficultyFrom64(b.Difficulty).Mul64(consensusUnclePenalty).Div64(100)
|
|
uncleWeight := b.Difficulty - unclePenalty.Lo
|
|
|
|
return uncleWeight, unclePenalty.Lo
|
|
} else {
|
|
weight = b.Difficulty
|
|
for _, u := range b.Uncles {
|
|
if (tipHeight - u.SideHeight) >= windowSize {
|
|
continue
|
|
}
|
|
weight += types.DifficultyFrom64(u.Difficulty).Mul64(consensusUnclePenalty).Div64(100).Lo
|
|
}
|
|
return weight, 0
|
|
}
|
|
}
|
|
|
|
func HashRatioSideBlocks(shares []*SideBlock, latest *SideBlock, consensusUnclePenalty uint64) (hashrate uint64, ratio float64, weight, total types.Difficulty) {
|
|
if len(shares) == 0 || latest == nil {
|
|
return 0, 0, types.ZeroDifficulty, types.ZeroDifficulty
|
|
}
|
|
|
|
shares = slices.Clone(shares)
|
|
slices.SortFunc(shares, SortSideBlock)
|
|
|
|
var totalWeight types.Difficulty
|
|
for _, s := range shares {
|
|
w, _ := s.Weight(s.SideHeight, uint64(s.WindowDepth), consensusUnclePenalty)
|
|
totalWeight = totalWeight.Add64(w)
|
|
}
|
|
|
|
cumWeight := latest.CumulativeDifficulty.Sub(shares[0].CumulativeDifficulty)
|
|
|
|
return totalWeight.Div64(latest.Timestamp - shares[0].Timestamp).Lo,
|
|
totalWeight.Float64() / cumWeight.Float64(),
|
|
totalWeight,
|
|
cumWeight
|
|
}
|
|
|
|
func SortSideBlock(a, b *SideBlock) int {
|
|
if a == b {
|
|
return 0
|
|
}
|
|
|
|
if a == nil {
|
|
return -1
|
|
} else if b == nil {
|
|
return 1
|
|
}
|
|
|
|
if a.EffectiveHeight < b.EffectiveHeight {
|
|
return -1
|
|
} else if a.EffectiveHeight > b.EffectiveHeight {
|
|
return 1
|
|
}
|
|
if a.SideHeight < b.SideHeight {
|
|
return -1
|
|
} else if a.SideHeight > b.SideHeight {
|
|
return 1
|
|
}
|
|
//same height, sort by main id
|
|
return a.MainId.Compare(b.MainId)
|
|
}
|