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