Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blockchain, fullblocktests, workmath, testhelper: add InvalidateBlock() method to BlockChain #2155

Merged
merged 10 commits into from
May 22, 2024
Merged
Prev Previous commit
Next Next commit
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.
  • Loading branch information
kcalvinalvin committed Apr 22, 2024
commit d4644dff10faf9fea7de02f219c0490723985394
68 changes: 1 addition & 67 deletions blockchain/fullblocktests/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"encoding/binary"
"errors"
"fmt"
"math"
"runtime"
"time"

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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))
}
Expand Down
69 changes: 69 additions & 0 deletions blockchain/internal/testhelper/common.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand Down Expand Up @@ -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
}