fullblocktests, testhelper: move solveBlock to testhelper

solveBlock is moved to testhelper and is exported.  This is done so that
the code can be reused without introducing import cycles.  The testing
code to be added in alter commits for invalidateblock and reconsider
block will use SolveBlock.
This commit is contained in:
Calvin Kim 2024-04-02 14:01:05 +09:00
parent 337d7f6be8
commit d4644dff10
2 changed files with 70 additions and 67 deletions

View File

@ -14,7 +14,6 @@ import (
"encoding/binary"
"errors"
"fmt"
"math"
"runtime"
"time"
@ -303,71 +302,6 @@ func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash {
return blockchain.CalcMerkleRoot(utilTxns, false)
}
// solveBlock attempts to find a nonce which makes the passed block header hash
// to a value less than the target difficulty. When a successful solution is
// found true is returned and the nonce field of the passed header is updated
// with the solution. False is returned if no solution exists.
//
// NOTE: This function will never solve blocks with a nonce of 0. This is done
// so the 'nextBlock' function can properly detect when a nonce was modified by
// a munge function.
func solveBlock(header *wire.BlockHeader) bool {
// sbResult is used by the solver goroutines to send results.
type sbResult struct {
found bool
nonce uint32
}
// solver accepts a block header and a nonce range to test. It is
// intended to be run as a goroutine.
targetDifficulty := blockchain.CompactToBig(header.Bits)
quit := make(chan bool)
results := make(chan sbResult)
solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) {
// We need to modify the nonce field of the header, so make sure
// we work with a copy of the original header.
for i := startNonce; i >= startNonce && i <= stopNonce; i++ {
select {
case <-quit:
return
default:
hdr.Nonce = i
hash := hdr.BlockHash()
if blockchain.HashToBig(&hash).Cmp(
targetDifficulty) <= 0 {
results <- sbResult{true, i}
return
}
}
}
results <- sbResult{false, 0}
}
startNonce := uint32(1)
stopNonce := uint32(math.MaxUint32)
numCores := uint32(runtime.NumCPU())
noncesPerCore := (stopNonce - startNonce) / numCores
for i := uint32(0); i < numCores; i++ {
rangeStart := startNonce + (noncesPerCore * i)
rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1
if i == numCores-1 {
rangeStop = stopNonce
}
go solver(*header, rangeStart, rangeStop)
}
for i := uint32(0); i < numCores; i++ {
result := <-results
if result.found {
close(quit)
header.Nonce = result.nonce
return true
}
}
return false
}
// additionalCoinbase returns a function that itself takes a block and
// modifies it by adding the provided amount to coinbase subsidy.
func additionalCoinbase(amount btcutil.Amount) func(*wire.MsgBlock) {
@ -523,7 +457,7 @@ func (g *testGenerator) nextBlock(blockName string, spend *testhelper.SpendableO
// Only solve the block if the nonce wasn't manually changed by a munge
// function.
if block.Header.Nonce == curNonce && !solveBlock(&block.Header) {
if block.Header.Nonce == curNonce && !testhelper.SolveBlock(&block.Header) {
panic(fmt.Sprintf("Unable to solve block at height %d",
nextHeight))
}

View File

@ -1,6 +1,10 @@
package testhelper
import (
"math"
"runtime"
"github.com/btcsuite/btcd/blockchain/internal/workmath"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
)
@ -29,3 +33,68 @@ func MakeSpendableOutForTx(tx *wire.MsgTx, txOutIndex uint32) SpendableOut {
func MakeSpendableOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) SpendableOut {
return MakeSpendableOutForTx(block.Transactions[txIndex], txOutIndex)
}
// SolveBlock attempts to find a nonce which makes the passed block header hash
// to a value less than the target difficulty. When a successful solution is
// found true is returned and the nonce field of the passed header is updated
// with the solution. False is returned if no solution exists.
//
// NOTE: This function will never solve blocks with a nonce of 0. This is done
// so the 'nextBlock' function can properly detect when a nonce was modified by
// a munge function.
func SolveBlock(header *wire.BlockHeader) bool {
// sbResult is used by the solver goroutines to send results.
type sbResult struct {
found bool
nonce uint32
}
// solver accepts a block header and a nonce range to test. It is
// intended to be run as a goroutine.
targetDifficulty := workmath.CompactToBig(header.Bits)
quit := make(chan bool)
results := make(chan sbResult)
solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) {
// We need to modify the nonce field of the header, so make sure
// we work with a copy of the original header.
for i := startNonce; i >= startNonce && i <= stopNonce; i++ {
select {
case <-quit:
return
default:
hdr.Nonce = i
hash := hdr.BlockHash()
if workmath.HashToBig(&hash).Cmp(
targetDifficulty) <= 0 {
results <- sbResult{true, i}
return
}
}
}
results <- sbResult{false, 0}
}
startNonce := uint32(1)
stopNonce := uint32(math.MaxUint32)
numCores := uint32(runtime.NumCPU())
noncesPerCore := (stopNonce - startNonce) / numCores
for i := uint32(0); i < numCores; i++ {
rangeStart := startNonce + (noncesPerCore * i)
rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1
if i == numCores-1 {
rangeStop = stopNonce
}
go solver(*header, rangeStart, rangeStop)
}
for i := uint32(0); i < numCores; i++ {
result := <-results
if result.found {
close(quit)
header.Nonce = result.nonce
return true
}
}
return false
}