git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/p2pool/sidechain/poolblock.go (about) 1 package sidechain 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "git.gammaspectra.live/P2Pool/consensus/v3/merge_mining" 9 "git.gammaspectra.live/P2Pool/consensus/v3/monero" 10 "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" 11 mainblock "git.gammaspectra.live/P2Pool/consensus/v3/monero/block" 12 "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" 13 "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" 14 "git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction" 15 p2poolcrypto "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/crypto" 16 "git.gammaspectra.live/P2Pool/consensus/v3/types" 17 "git.gammaspectra.live/P2Pool/consensus/v3/utils" 18 fasthex "github.com/tmthrgd/go-hex" 19 "io" 20 "net/netip" 21 "slices" 22 "sync/atomic" 23 "time" 24 "unsafe" 25 ) 26 27 type CoinbaseExtraTag int 28 29 const SideExtraNonceSize = 4 30 const SideExtraNonceMaxSize = SideExtraNonceSize + 10 31 32 const ( 33 SideCoinbasePublicKey = transaction.TxExtraTagPubKey 34 SideExtraNonce = transaction.TxExtraTagNonce 35 36 // SideIdentifierHash Depending on version, this can be a PoolBlock TemplateId or Merkle Root Hash 37 SideIdentifierHash = transaction.TxExtraTagMergeMining 38 ) 39 40 // PoolBlockMaxTemplateSize Max P2P message size (128 KB) minus BLOCK_RESPONSE header (5 bytes) 41 const PoolBlockMaxTemplateSize = 128*1024 - (1 + 4) 42 43 type UniquePoolBlockSlice []*PoolBlock 44 45 func (s UniquePoolBlockSlice) Get(consensus *Consensus, id types.Hash) *PoolBlock { 46 if i := slices.IndexFunc(s, func(p *PoolBlock) bool { 47 return p.FastSideTemplateId(consensus) == id 48 }); i != -1 { 49 return s[i] 50 } 51 return nil 52 } 53 54 func (s UniquePoolBlockSlice) GetHeight(height uint64) (result UniquePoolBlockSlice) { 55 for _, b := range s { 56 if b.Side.Height == height { 57 result = append(result, b) 58 } 59 } 60 return result 61 } 62 63 // IterationCache Used for fast scan backwards, and of uncles 64 // Only maybe available in verified blocks 65 type IterationCache struct { 66 Parent *PoolBlock 67 Uncles []*PoolBlock 68 } 69 70 type PoolBlock struct { 71 Main mainblock.Block `json:"main"` 72 73 Side SideData `json:"side"` 74 75 //Temporary data structures 76 mergeMiningTag merge_mining.Tag 77 78 cache poolBlockCache 79 Depth atomic.Uint64 `json:"-"` 80 Verified atomic.Bool `json:"-"` 81 Invalid atomic.Bool `json:"-"` 82 83 WantBroadcast atomic.Bool `json:"-"` 84 Broadcasted atomic.Bool `json:"-"` 85 86 Metadata PoolBlockReceptionMetadata `json:"metadata"` 87 88 CachedShareVersion ShareVersion `json:"share_version"` 89 90 iterationCache *IterationCache 91 } 92 93 type PoolBlockReceptionMetadata struct { 94 // LocalTime Moment the block was received from a source 95 LocalTime time.Time `json:"local_time,omitempty"` 96 // AddressPort The address and port of the peer who broadcasted or sent us this block 97 // If the peer specified a listen port, the port will be that instead of current connection one 98 AddressPort netip.AddrPort `json:"address_port,omitempty"` 99 // PeerId The peer id of the peer who broadcasted or sent us this block 100 PeerId uint64 `json:"peer_id,omitempty"` 101 102 SoftwareId uint32 `json:"software_id"` 103 SoftwareVersion uint32 `json:"software_version"` 104 } 105 106 func (m *PoolBlockReceptionMetadata) UnmarshalBinary(buf []byte) error { 107 r := bytes.NewReader(buf) 108 s, err := binary.ReadUvarint(r) 109 if err != nil { 110 return err 111 } 112 ns, err := binary.ReadUvarint(r) 113 if err != nil { 114 return err 115 } 116 117 m.LocalTime = time.Unix(int64(s), int64(ns)).UTC() 118 119 l, err := binary.ReadUvarint(r) 120 if err != nil { 121 return err 122 } 123 ip := make([]byte, l) 124 _, err = io.ReadFull(r, ip) 125 if err != nil { 126 return err 127 } 128 129 err = m.AddressPort.UnmarshalBinary(ip) 130 if err != nil { 131 return err 132 } 133 134 m.PeerId, err = binary.ReadUvarint(r) 135 if err != nil { 136 return err 137 } 138 139 sId, err := binary.ReadUvarint(r) 140 if err != nil { 141 return err 142 } 143 144 sVer, err := binary.ReadUvarint(r) 145 if err != nil { 146 return err 147 } 148 149 m.SoftwareId = uint32(sId) 150 m.SoftwareVersion = uint32(sVer) 151 152 return nil 153 } 154 155 func (m *PoolBlockReceptionMetadata) MarshalBinary() ([]byte, error) { 156 s := uint64(m.LocalTime.Unix()) 157 ns := uint64(m.LocalTime.Nanosecond()) 158 159 ip, _ := m.AddressPort.MarshalBinary() 160 out := make([]byte, 0, 161 utils.UVarInt64Size(s)+utils.UVarInt64Size(ns)+ 162 utils.UVarInt64Size(len(ip))+len(ip)+ 163 utils.UVarInt64Size(m.PeerId)+ 164 utils.UVarInt64Size(uint64(m.SoftwareId))+ 165 utils.UVarInt64Size(uint64(m.SoftwareVersion))) 166 167 out = binary.AppendUvarint(out, s) 168 out = binary.AppendUvarint(out, ns) 169 out = binary.AppendUvarint(out, uint64(len(ip))) 170 out = append(out, ip...) 171 out = binary.AppendUvarint(out, m.PeerId) 172 out = binary.AppendUvarint(out, uint64(m.SoftwareId)) 173 out = binary.AppendUvarint(out, uint64(m.SoftwareVersion)) 174 return out, nil 175 } 176 177 func (b *PoolBlock) iteratorGetParent(getByTemplateId GetByTemplateIdFunc) *PoolBlock { 178 if b.iterationCache == nil { 179 return getByTemplateId(b.Side.Parent) 180 } 181 return b.iterationCache.Parent 182 } 183 184 func (b *PoolBlock) iteratorUncles(getByTemplateId GetByTemplateIdFunc, uncleFunc func(uncle *PoolBlock)) error { 185 if len(b.Side.Uncles) == 0 { 186 return nil 187 } 188 if b.iterationCache == nil { 189 for _, uncleId := range b.Side.Uncles { 190 uncle := getByTemplateId(uncleId) 191 if uncle == nil { 192 return fmt.Errorf("could not find uncle %s", uncleId) 193 } 194 uncleFunc(uncle) 195 } 196 } else { 197 for _, uncle := range b.iterationCache.Uncles { 198 uncleFunc(uncle) 199 } 200 } 201 202 return nil 203 } 204 205 func (b *PoolBlock) NeedsCompactTransactionFilling() bool { 206 return len(b.Main.TransactionParentIndices) > 0 && len(b.Main.TransactionParentIndices) == len(b.Main.Transactions) && slices.Index(b.Main.Transactions, types.ZeroHash) != -1 207 } 208 209 func (b *PoolBlock) FillTransactionsFromTransactionParentIndices(consensus *Consensus, parent *PoolBlock) error { 210 if b.NeedsCompactTransactionFilling() { 211 if parent != nil && parent.FastSideTemplateId(consensus) == b.Side.Parent { 212 for i, parentIndex := range b.Main.TransactionParentIndices { 213 if parentIndex != 0 { 214 // p2pool stores coinbase transaction hash as well, decrease 215 actualIndex := parentIndex - 1 216 if actualIndex > uint64(len(parent.Main.Transactions)) { 217 return errors.New("index of parent transaction out of bounds") 218 } 219 b.Main.Transactions[i] = parent.Main.Transactions[actualIndex] 220 } 221 } 222 } else if parent == nil { 223 return errors.New("parent is nil") 224 } 225 } 226 227 return nil 228 } 229 230 func (b *PoolBlock) FillTransactionParentIndices(consensus *Consensus, parent *PoolBlock) bool { 231 if len(b.Main.Transactions) != len(b.Main.TransactionParentIndices) { 232 if parent != nil && parent.FastSideTemplateId(consensus) == b.Side.Parent { 233 b.Main.TransactionParentIndices = make([]uint64, len(b.Main.Transactions)) 234 //do not fail if not found 235 for i, txHash := range b.Main.Transactions { 236 if parentIndex := slices.Index(parent.Main.Transactions, txHash); parentIndex != -1 { 237 //increase as p2pool stores tx hash as well 238 b.Main.TransactionParentIndices[i] = uint64(parentIndex + 1) 239 } 240 } 241 return true 242 } 243 244 return false 245 } 246 247 return true 248 } 249 250 func (b *PoolBlock) CalculateShareVersion(consensus *Consensus) ShareVersion { 251 return P2PoolShareVersion(consensus, b.Main.Timestamp) 252 } 253 254 func (b *PoolBlock) ShareVersion() ShareVersion { 255 return b.CachedShareVersion 256 } 257 258 func (b *PoolBlock) ShareVersionSignaling() ShareVersion { 259 if b.ShareVersion() == ShareVersion_V1 && (b.ExtraNonce()&0xFF000000 == 0xFF000000) { 260 return ShareVersion_V2 261 } 262 return ShareVersion_None 263 } 264 265 func (b *PoolBlock) ExtraNonce() uint32 { 266 extraNonce := b.CoinbaseExtra(SideExtraNonce) 267 if len(extraNonce) < SideExtraNonceSize { 268 return 0 269 } 270 return binary.LittleEndian.Uint32(extraNonce) 271 } 272 273 // FastSideTemplateId Returns SideTemplateId from either coinbase extra tags or pruned data, or main block if not pruned 274 func (b *PoolBlock) FastSideTemplateId(consensus *Consensus) types.Hash { 275 if b.ShareVersion() > ShareVersion_V2 { 276 if b.Main.Coinbase.AuxiliaryData.WasPruned { 277 return b.Main.Coinbase.AuxiliaryData.TemplateId 278 } else { 279 //fallback to full calculation 280 return b.SideTemplateId(consensus) 281 } 282 } else { 283 return types.HashFromBytes(b.CoinbaseExtra(SideIdentifierHash)) 284 } 285 } 286 287 func (b *PoolBlock) CoinbaseExtra(tag CoinbaseExtraTag) []byte { 288 switch tag { 289 case SideExtraNonce: 290 if t := b.Main.Coinbase.Extra.GetTag(uint8(tag)); t != nil { 291 if len(t.Data) < SideExtraNonceSize || len(t.Data) > SideExtraNonceMaxSize { 292 return nil 293 } 294 return t.Data 295 } 296 case SideIdentifierHash: 297 if t := b.Main.Coinbase.Extra.GetTag(uint8(tag)); t != nil { 298 if b.ShareVersion() > ShareVersion_V2 { 299 mergeMineReader := bytes.NewReader(t.Data) 300 var mergeMiningTag merge_mining.Tag 301 if err := mergeMiningTag.FromReader(mergeMineReader); err != nil || mergeMineReader.Len() != 0 { 302 return nil 303 } 304 305 return mergeMiningTag.RootHash[:] 306 } else { 307 if t.VarInt != types.HashSize || len(t.Data) != types.HashSize { 308 return nil 309 } 310 return t.Data 311 } 312 } 313 case SideCoinbasePublicKey: 314 if t := b.Main.Coinbase.Extra.GetTag(uint8(tag)); t != nil { 315 if len(t.Data) != crypto.PublicKeySize { 316 return nil 317 } 318 return t.Data 319 } 320 } 321 322 return nil 323 } 324 325 func (b *PoolBlock) MainId() types.Hash { 326 return b.Main.Id() 327 } 328 329 func (b *PoolBlock) FullId(consensus *Consensus) FullId { 330 var buf FullId 331 332 sidechainId := b.FastSideTemplateId(consensus) 333 copy(buf[:], sidechainId[:]) 334 binary.LittleEndian.PutUint32(buf[types.HashSize:], b.Main.Nonce) 335 copy(buf[types.HashSize+unsafe.Sizeof(b.Main.Nonce):], b.CoinbaseExtra(SideExtraNonce)[:SideExtraNonceSize]) 336 return buf 337 } 338 339 const FullIdSize = int(types.HashSize + unsafe.Sizeof(uint32(0)) + SideExtraNonceSize) 340 341 var zeroFullId FullId 342 343 type FullId [FullIdSize]byte 344 345 func FullIdFromString(s string) (FullId, error) { 346 var h FullId 347 if buf, err := fasthex.DecodeString(s); err != nil { 348 return h, err 349 } else { 350 if len(buf) != FullIdSize { 351 return h, errors.New("wrong hash size") 352 } 353 copy(h[:], buf) 354 return h, nil 355 } 356 } 357 358 func (id FullId) TemplateId() (h types.Hash) { 359 return types.Hash(id[:types.HashSize]) 360 } 361 362 func (id FullId) Nonce() uint32 { 363 return binary.LittleEndian.Uint32(id[types.HashSize:]) 364 } 365 366 func (id FullId) ExtraNonce() uint32 { 367 return binary.LittleEndian.Uint32(id[types.HashSize+unsafe.Sizeof(uint32(0)):]) 368 } 369 370 func (id FullId) String() string { 371 return fasthex.EncodeToString(id[:]) 372 } 373 374 func (b *PoolBlock) CalculateFullId(consensus *Consensus) FullId { 375 var buf FullId 376 sidechainId := b.SideTemplateId(consensus) 377 copy(buf[:], sidechainId[:]) 378 binary.LittleEndian.PutUint32(buf[types.HashSize:], b.Main.Nonce) 379 copy(buf[types.HashSize+unsafe.Sizeof(b.Main.Nonce):], b.CoinbaseExtra(SideExtraNonce)[:SideExtraNonceSize]) 380 return buf 381 } 382 383 func (b *PoolBlock) MainDifficulty(f mainblock.GetDifficultyByHeightFunc) types.Difficulty { 384 return b.Main.Difficulty(f) 385 } 386 387 func (b *PoolBlock) SideTemplateId(consensus *Consensus) types.Hash { 388 if h := b.cache.templateId.Load(); h != nil { 389 return *h 390 } else { 391 hash := consensus.CalculateSideTemplateId(b) 392 if hash == types.ZeroHash { 393 return types.ZeroHash 394 } 395 b.cache.templateId.Store(&hash) 396 return hash 397 } 398 } 399 400 func (b *PoolBlock) CoinbaseId() types.Hash { 401 if h := b.cache.coinbaseId.Load(); h != nil { 402 return *h 403 } else { 404 hash := b.Main.Coinbase.CalculateId() 405 if hash == types.ZeroHash { 406 return types.ZeroHash 407 } 408 b.cache.coinbaseId.Store(&hash) 409 return hash 410 } 411 } 412 413 func (b *PoolBlock) MergeMiningTag() merge_mining.Tag { 414 return b.mergeMiningTag 415 } 416 417 func (b *PoolBlock) PowHash(hasher randomx.Hasher, f mainblock.GetSeedByHeightFunc) types.Hash { 418 h, _ := b.PowHashWithError(hasher, f) 419 return h 420 } 421 422 func (b *PoolBlock) PowHashWithError(hasher randomx.Hasher, f mainblock.GetSeedByHeightFunc) (powHash types.Hash, err error) { 423 if h := b.cache.powHash.Load(); h != nil { 424 powHash = *h 425 } else { 426 powHash, err = b.Main.PowHashWithError(hasher, f) 427 if powHash == types.ZeroHash { 428 return types.ZeroHash, err 429 } 430 b.cache.powHash.Store(&powHash) 431 } 432 433 return powHash, nil 434 } 435 436 func (b *PoolBlock) UnmarshalBinary(consensus *Consensus, derivationCache DerivationCacheInterface, data []byte) error { 437 if len(data) > PoolBlockMaxTemplateSize { 438 return errors.New("buffer too large") 439 } 440 reader := bytes.NewReader(data) 441 return b.FromReader(consensus, derivationCache, reader) 442 } 443 444 func (b *PoolBlock) BufferLength() int { 445 return b.Main.BufferLength() + b.Side.BufferLength(b.ShareVersion()) 446 } 447 448 func (b *PoolBlock) MarshalBinary() ([]byte, error) { 449 return b.MarshalBinaryFlags(false, false) 450 } 451 452 func (b *PoolBlock) MarshalBinaryFlags(pruned, compact bool) ([]byte, error) { 453 return b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), pruned, compact) 454 } 455 456 func (b *PoolBlock) AppendBinaryFlags(preAllocatedBuf []byte, pruned, compact bool) (buf []byte, err error) { 457 buf = preAllocatedBuf 458 459 if buf, err = b.Main.AppendBinaryFlags(buf, compact, pruned, b.ShareVersion() > ShareVersion_V2); err != nil { 460 return nil, err 461 } else if buf, err = b.Side.AppendBinary(buf, b.ShareVersion()); err != nil { 462 return nil, err 463 } else { 464 if len(buf) > PoolBlockMaxTemplateSize { 465 return nil, errors.New("buffer too large") 466 } 467 return buf, nil 468 } 469 } 470 471 func (b *PoolBlock) FromReader(consensus *Consensus, derivationCache DerivationCacheInterface, reader utils.ReaderAndByteReader) (err error) { 472 if err = b.Main.FromReader(reader, true, func() (containsAuxiliaryTemplateId bool) { 473 return b.CalculateShareVersion(consensus) > ShareVersion_V2 474 }); err != nil { 475 return err 476 } 477 478 return b.consensusDecode(consensus, derivationCache, reader) 479 } 480 481 // FromCompactReader used in Protocol 1.1 and above 482 func (b *PoolBlock) FromCompactReader(consensus *Consensus, derivationCache DerivationCacheInterface, reader utils.ReaderAndByteReader) (err error) { 483 if err = b.Main.FromCompactReader(reader, true, func() (containsAuxiliaryTemplateId bool) { 484 return b.CalculateShareVersion(consensus) > ShareVersion_V2 485 }); err != nil { 486 return err 487 } 488 489 return b.consensusDecode(consensus, derivationCache, reader) 490 } 491 492 func (b *PoolBlock) consensusDecode(consensus *Consensus, derivationCache DerivationCacheInterface, reader utils.ReaderAndByteReader) (err error) { 493 if expectedMajorVersion := monero.NetworkMajorVersion(consensus.NetworkType.MustAddressNetwork(), b.Main.Coinbase.GenHeight); expectedMajorVersion != b.Main.MajorVersion { 494 return fmt.Errorf("expected major version %d at height %d, got %d", expectedMajorVersion, b.Main.Coinbase.GenHeight, b.Main.MajorVersion) 495 } 496 497 if b.CachedShareVersion == ShareVersion_None { 498 b.CachedShareVersion = b.CalculateShareVersion(consensus) 499 } 500 501 mergeMineTag := b.Main.Coinbase.Extra.GetTag(transaction.TxExtraTagMergeMining) 502 503 if mergeMineTag == nil { 504 return errors.New("missing merge mining tag") 505 } 506 507 if b.ShareVersion() < ShareVersion_V3 { 508 //TODO: this is to comply with non-standard p2pool serialization, see https://github.com/SChernykh/p2pool/issues/249 509 if mergeMineTag.VarInt != types.HashSize { 510 return errors.New("wrong merge mining tag depth") 511 } 512 } else { 513 //properly decode merge mining tag 514 mergeMineReader := bytes.NewReader(mergeMineTag.Data) 515 if err = b.mergeMiningTag.FromReader(mergeMineReader); err != nil { 516 return err 517 } 518 if mergeMineReader.Len() != 0 { 519 return errors.New("wrong merge mining tag len") 520 } 521 } 522 523 if err = b.Side.FromReader(reader, b.ShareVersion()); err != nil { 524 return err 525 } 526 527 b.FillPrivateKeys(derivationCache) 528 529 return nil 530 } 531 532 // PreProcessBlock processes and fills the block data from either pruned or compact modes 533 func (b *PoolBlock) PreProcessBlock(consensus *Consensus, derivationCache DerivationCacheInterface, preAllocatedShares Shares, difficultyByHeight mainblock.GetDifficultyByHeightFunc, getTemplateById GetByTemplateIdFunc) (missingBlocks []types.Hash, err error) { 534 return b.PreProcessBlockWithOutputs(consensus, getTemplateById, func() (outputs transaction.Outputs, bottomHeight uint64) { 535 return CalculateOutputs(b, consensus, difficultyByHeight, getTemplateById, derivationCache, preAllocatedShares, nil) 536 }) 537 } 538 539 // PreProcessBlockWithOutputs processes and fills the block data from either pruned or compact modes 540 func (b *PoolBlock) PreProcessBlockWithOutputs(consensus *Consensus, getTemplateById GetByTemplateIdFunc, calculateOutputs func() (outputs transaction.Outputs, bottomHeight uint64)) (missingBlocks []types.Hash, err error) { 541 542 getTemplateByIdFillingTx := func(h types.Hash) *PoolBlock { 543 chain := make(UniquePoolBlockSlice, 0, 1) 544 545 cur := getTemplateById(h) 546 for ; cur != nil; cur = getTemplateById(cur.Side.Parent) { 547 chain = append(chain, cur) 548 if !cur.NeedsCompactTransactionFilling() { 549 break 550 } 551 if len(chain) > 1 { 552 if chain[len(chain)-2].FillTransactionsFromTransactionParentIndices(consensus, chain[len(chain)-1]) == nil { 553 if !chain[len(chain)-2].NeedsCompactTransactionFilling() { 554 //early abort if it can all be filled 555 chain = chain[:len(chain)-1] 556 break 557 } 558 } 559 } 560 } 561 if len(chain) == 0 { 562 return nil 563 } 564 //skips last entry 565 for i := len(chain) - 2; i >= 0; i-- { 566 if err := chain[i].FillTransactionsFromTransactionParentIndices(consensus, chain[i+1]); err != nil { 567 return nil 568 } 569 } 570 return chain[0] 571 } 572 573 var parent *PoolBlock 574 if b.NeedsCompactTransactionFilling() { 575 parent = getTemplateByIdFillingTx(b.Side.Parent) 576 if parent == nil { 577 missingBlocks = append(missingBlocks, b.Side.Parent) 578 return missingBlocks, errors.New("parent does not exist in compact block") 579 } 580 if err := b.FillTransactionsFromTransactionParentIndices(consensus, parent); err != nil { 581 return nil, fmt.Errorf("error filling transactions for block: %w", err) 582 } 583 } 584 585 if len(b.Main.Transactions) != len(b.Main.TransactionParentIndices) { 586 if parent == nil { 587 parent = getTemplateByIdFillingTx(b.Side.Parent) 588 } 589 b.FillTransactionParentIndices(consensus, parent) 590 } 591 592 if len(b.Main.Coinbase.Outputs) == 0 { 593 if outputs, _ := calculateOutputs(); outputs == nil { 594 return nil, errors.New("error filling outputs for block: nil outputs") 595 } else { 596 b.Main.Coinbase.Outputs = outputs 597 } 598 599 if outputBlob, err := b.Main.Coinbase.Outputs.AppendBinary(make([]byte, 0, b.Main.Coinbase.Outputs.BufferLength())); err != nil { 600 return nil, fmt.Errorf("error filling outputs for block: %s", err) 601 } else if uint64(len(outputBlob)) != b.Main.Coinbase.AuxiliaryData.OutputsBlobSize { 602 return nil, fmt.Errorf("error filling outputs for block: invalid output blob size, got %d, expected %d", b.Main.Coinbase.AuxiliaryData.OutputsBlobSize, len(outputBlob)) 603 } 604 } 605 606 return nil, nil 607 } 608 609 func (b *PoolBlock) NeedsPreProcess() bool { 610 return b.NeedsCompactTransactionFilling() || len(b.Main.Transactions) != len(b.Main.TransactionParentIndices) || len(b.Main.Coinbase.Outputs) == 0 611 } 612 613 func (b *PoolBlock) FillPrivateKeys(derivationCache DerivationCacheInterface) { 614 if b.ShareVersion() > ShareVersion_V1 { 615 if b.Side.CoinbasePrivateKey == crypto.ZeroPrivateKeyBytes { 616 //Fill Private Key 617 kP := derivationCache.GetDeterministicTransactionKey(b.GetPrivateKeySeed(), b.Main.PreviousId) 618 b.Side.CoinbasePrivateKey = kP.PrivateKey.AsBytes() 619 } 620 } else { 621 b.Side.CoinbasePrivateKeySeed = b.GetPrivateKeySeed() 622 } 623 } 624 625 func (b *PoolBlock) IsProofHigherThanMainDifficulty(hasher randomx.Hasher, difficultyFunc mainblock.GetDifficultyByHeightFunc, seedFunc mainblock.GetSeedByHeightFunc) bool { 626 r, _ := b.IsProofHigherThanMainDifficultyWithError(hasher, difficultyFunc, seedFunc) 627 return r 628 } 629 630 func (b *PoolBlock) IsProofHigherThanMainDifficultyWithError(hasher randomx.Hasher, difficultyFunc mainblock.GetDifficultyByHeightFunc, seedFunc mainblock.GetSeedByHeightFunc) (bool, error) { 631 if mainDifficulty := b.MainDifficulty(difficultyFunc); mainDifficulty == types.ZeroDifficulty { 632 return false, errors.New("could not get main difficulty") 633 } else if powHash, err := b.PowHashWithError(hasher, seedFunc); err != nil { 634 return false, err 635 } else { 636 return mainDifficulty.CheckPoW(powHash), nil 637 } 638 } 639 640 func (b *PoolBlock) IsProofHigherThanDifficulty(hasher randomx.Hasher, f mainblock.GetSeedByHeightFunc) bool { 641 r, _ := b.IsProofHigherThanDifficultyWithError(hasher, f) 642 return r 643 } 644 645 func (b *PoolBlock) IsProofHigherThanDifficultyWithError(hasher randomx.Hasher, f mainblock.GetSeedByHeightFunc) (bool, error) { 646 if powHash, err := b.PowHashWithError(hasher, f); err != nil { 647 return false, err 648 } else { 649 return b.Side.Difficulty.CheckPoW(powHash), nil 650 } 651 } 652 653 func (b *PoolBlock) GetPrivateKeySeed() types.Hash { 654 if b.ShareVersion() > ShareVersion_V1 { 655 return b.Side.CoinbasePrivateKeySeed 656 } 657 658 oldSeed := types.Hash(b.Side.PublicKey[address.PackedAddressSpend]) 659 if b.Main.MajorVersion < monero.HardForkViewTagsVersion && p2poolcrypto.GetDeterministicTransactionPrivateKey(oldSeed, b.Main.PreviousId).AsBytes() != b.Side.CoinbasePrivateKey { 660 return types.ZeroHash 661 } 662 663 return oldSeed 664 } 665 666 func (b *PoolBlock) CalculateTransactionPrivateKeySeed() types.Hash { 667 if b.ShareVersion() > ShareVersion_V1 { 668 preAllocatedMainData := make([]byte, 0, b.Main.BufferLength()) 669 preAllocatedSideData := make([]byte, 0, b.Side.BufferLength(b.ShareVersion())) 670 mainData, _ := b.Main.SideChainHashingBlob(preAllocatedMainData, false) 671 sideData, _ := b.Side.AppendBinary(preAllocatedSideData, b.ShareVersion()) 672 return p2poolcrypto.CalculateTransactionPrivateKeySeed( 673 mainData, 674 sideData, 675 ) 676 } 677 678 return types.Hash(b.Side.PublicKey[address.PackedAddressSpend]) 679 } 680 681 func (b *PoolBlock) GetAddress() address.PackedAddress { 682 return b.Side.PublicKey 683 } 684 685 func (b *PoolBlock) GetTransactionOutputType() uint8 { 686 // Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions, 687 // but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15 688 expectedTxType := uint8(transaction.TxOutToKey) 689 if b.Main.MajorVersion >= monero.HardForkViewTagsVersion { 690 expectedTxType = transaction.TxOutToTaggedKey 691 } 692 693 return expectedTxType 694 } 695 696 type poolBlockCache struct { 697 templateId atomic.Pointer[types.Hash] 698 coinbaseId atomic.Pointer[types.Hash] 699 powHash atomic.Pointer[types.Hash] 700 }