mirror of
https://github.com/btcsuite/btcd.git
synced 2025-02-22 22:25:45 +01:00
Merge pull request #2155 from kcalvinalvin/2024-04-02-invalidate-block
blockchain, fullblocktests, workmath, testhelper: add InvalidateBlock() method to BlockChain
This commit is contained in:
commit
c4677255bd
10 changed files with 972 additions and 302 deletions
|
@ -1798,6 +1798,144 @@ func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Has
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvalidateBlock invalidates the requested block and all its descedents. If a block
|
||||||
|
// in the best chain is invalidated, the active chain tip will be the parent of the
|
||||||
|
// invalidated block.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (b *BlockChain) InvalidateBlock(hash *chainhash.Hash) error {
|
||||||
|
b.chainLock.Lock()
|
||||||
|
defer b.chainLock.Unlock()
|
||||||
|
|
||||||
|
node := b.index.LookupNode(hash)
|
||||||
|
if node == nil {
|
||||||
|
// Return an error if the block doesn't exist.
|
||||||
|
return fmt.Errorf("Requested block hash of %s is not found "+
|
||||||
|
"and thus cannot be invalidated.", hash)
|
||||||
|
}
|
||||||
|
if node.height == 0 {
|
||||||
|
return fmt.Errorf("Requested block hash of %s is a at height 0 "+
|
||||||
|
"and is thus a genesis block and cannot be invalidated.",
|
||||||
|
node.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to do if the given block is already invalid.
|
||||||
|
if node.status.KnownInvalid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the status of the block being invalidated.
|
||||||
|
b.index.SetStatusFlags(node, statusValidateFailed)
|
||||||
|
b.index.UnsetStatusFlags(node, statusValid)
|
||||||
|
|
||||||
|
// If the block we're invalidating is not on the best chain, we simply
|
||||||
|
// mark the block and all its descendants as invalid and return.
|
||||||
|
if !b.bestChain.Contains(node) {
|
||||||
|
// Grab all the tips excluding the active tip.
|
||||||
|
tips := b.index.InactiveTips(b.bestChain)
|
||||||
|
for _, tip := range tips {
|
||||||
|
// Continue if the given inactive tip is not a descendant of the block
|
||||||
|
// being invalidated.
|
||||||
|
if !tip.IsAncestor(node) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep going back until we get to the block being invalidated.
|
||||||
|
// For each of the parent, we'll unset valid status and set invalid
|
||||||
|
// ancestor status.
|
||||||
|
for n := tip; n != nil && n != node; n = n.parent {
|
||||||
|
// Continue if it's already invalid.
|
||||||
|
if n.status.KnownInvalid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.index.SetStatusFlags(n, statusInvalidAncestor)
|
||||||
|
b.index.UnsetStatusFlags(n, statusValid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeErr := b.index.flushToDB(); writeErr != nil {
|
||||||
|
return fmt.Errorf("Error flushing block index "+
|
||||||
|
"changes to disk: %v", writeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return since the block being invalidated is on a side branch.
|
||||||
|
// Nothing else left to do.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're here, it means a block from the active chain tip is getting
|
||||||
|
// invalidated.
|
||||||
|
//
|
||||||
|
// Grab all the nodes to detach from the active chain.
|
||||||
|
detachNodes := list.New()
|
||||||
|
for n := b.bestChain.Tip(); n != nil && n != node; n = n.parent {
|
||||||
|
// Continue if it's already invalid.
|
||||||
|
if n.status.KnownInvalid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the status of the block node.
|
||||||
|
b.index.SetStatusFlags(n, statusInvalidAncestor)
|
||||||
|
b.index.UnsetStatusFlags(n, statusValid)
|
||||||
|
detachNodes.PushBack(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push back the block node being invalidated.
|
||||||
|
detachNodes.PushBack(node)
|
||||||
|
|
||||||
|
// Reorg back to the parent of the block being invalidated.
|
||||||
|
// Nothing to attach so just pass an empty list.
|
||||||
|
err := b.reorganizeChain(detachNodes, list.New())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeErr := b.index.flushToDB(); writeErr != nil {
|
||||||
|
log.Warnf("Error flushing block index changes to disk: %v", writeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab all the tips.
|
||||||
|
tips := b.index.InactiveTips(b.bestChain)
|
||||||
|
tips = append(tips, b.bestChain.Tip())
|
||||||
|
|
||||||
|
// Here we'll check if the invalidation of the block in the active tip
|
||||||
|
// changes the status of the chain tips. If a side branch now has more
|
||||||
|
// worksum, it becomes the active chain tip.
|
||||||
|
var bestTip *blockNode
|
||||||
|
for _, tip := range tips {
|
||||||
|
// Skip invalid tips as they cannot become the active tip.
|
||||||
|
if tip.status.KnownInvalid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have no best tips, then set this tip as the best tip.
|
||||||
|
if bestTip == nil {
|
||||||
|
bestTip = tip
|
||||||
|
} else {
|
||||||
|
// If there is an existing best tip, then compare it
|
||||||
|
// against the current tip.
|
||||||
|
if tip.workSum.Cmp(bestTip.workSum) == 1 {
|
||||||
|
bestTip = tip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the best tip is the current tip.
|
||||||
|
if bestTip == b.bestChain.Tip() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reorganize to the best tip if a side branch is now the most work tip.
|
||||||
|
detachNodes, attachNodes := b.getReorganizeNodes(bestTip)
|
||||||
|
err = b.reorganizeChain(detachNodes, attachNodes)
|
||||||
|
|
||||||
|
if writeErr := b.index.flushToDB(); writeErr != nil {
|
||||||
|
log.Warnf("Error flushing block index changes to disk: %v", writeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// IndexManager provides a generic interface that the is called when blocks are
|
// IndexManager provides a generic interface that the is called when blocks are
|
||||||
// connected and disconnected to and from the tip of the main chain for the
|
// connected and disconnected to and from the tip of the main chain for the
|
||||||
// purpose of supporting optional indexes.
|
// purpose of supporting optional indexes.
|
||||||
|
|
|
@ -6,10 +6,12 @@ package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/blockchain/internal/testhelper"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
@ -1311,3 +1313,312 @@ func TestIsAncestor(t *testing.T) {
|
||||||
branch2Nodes[0].hash.String())
|
branch2Nodes[0].hash.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// randomSelect selects random amount of random elements from a slice and returns a
|
||||||
|
// new slice. The selected elements are removed.
|
||||||
|
func randomSelect(input []*testhelper.SpendableOut) (
|
||||||
|
[]*testhelper.SpendableOut, []*testhelper.SpendableOut) {
|
||||||
|
|
||||||
|
selected := []*testhelper.SpendableOut{}
|
||||||
|
|
||||||
|
// Select random elements from the input slice
|
||||||
|
amount := rand.Intn(len(input))
|
||||||
|
for i := 0; i < amount; i++ {
|
||||||
|
// Generate a random index
|
||||||
|
randIdx := rand.Intn(len(input))
|
||||||
|
|
||||||
|
// Append the selected element to the new slice
|
||||||
|
selected = append(selected, input[randIdx])
|
||||||
|
|
||||||
|
// Remove the selected element from the input slice.
|
||||||
|
// This ensures that each selected element is unique.
|
||||||
|
input = append(input[:randIdx], input[randIdx+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return input, selected
|
||||||
|
}
|
||||||
|
|
||||||
|
// addBlocks generates new blocks and adds them to the chain. The newly generated
|
||||||
|
// blocks will spend from the spendable outputs passed in. The returned hases are
|
||||||
|
// the hashes of the newly generated blocks.
|
||||||
|
func addBlocks(count int, chain *BlockChain, prevBlock *btcutil.Block,
|
||||||
|
allSpendableOutputs []*testhelper.SpendableOut) (
|
||||||
|
[]*chainhash.Hash, [][]*testhelper.SpendableOut, error) {
|
||||||
|
|
||||||
|
blockHashes := make([]*chainhash.Hash, 0, count)
|
||||||
|
spendablesOuts := make([][]*testhelper.SpendableOut, 0, count)
|
||||||
|
|
||||||
|
// Always spend everything on the first block. This ensures we get unique blocks
|
||||||
|
// every time. The random select may choose not to spend any and that results
|
||||||
|
// in getting the same block.
|
||||||
|
nextSpends := allSpendableOutputs
|
||||||
|
allSpendableOutputs = allSpendableOutputs[:0]
|
||||||
|
for b := 0; b < count; b++ {
|
||||||
|
newBlock, newSpendableOuts, err := addBlock(chain, prevBlock, nextSpends)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
prevBlock = newBlock
|
||||||
|
|
||||||
|
blockHashes = append(blockHashes, newBlock.Hash())
|
||||||
|
spendablesOuts = append(spendablesOuts, newSpendableOuts)
|
||||||
|
allSpendableOutputs = append(allSpendableOutputs, newSpendableOuts...)
|
||||||
|
|
||||||
|
// Grab utxos to be spent in the next block.
|
||||||
|
allSpendableOutputs, nextSpends = randomSelect(allSpendableOutputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockHashes, spendablesOuts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidateBlock(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
chainGen func() (*BlockChain, []*chainhash.Hash, func())
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "one branch, invalidate once",
|
||||||
|
chainGen: func() (*BlockChain, []*chainhash.Hash, func()) {
|
||||||
|
chain, params, tearDown := utxoCacheTestChain(
|
||||||
|
"TestInvalidateBlock-one-branch-" +
|
||||||
|
"invalidate-once")
|
||||||
|
// Grab the tip of the chain.
|
||||||
|
tip := btcutil.NewBlock(params.GenesisBlock)
|
||||||
|
|
||||||
|
// Create a chain with 11 blocks.
|
||||||
|
_, _, err := addBlocks(11, chain, tip, []*testhelper.SpendableOut{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate block 5.
|
||||||
|
block, err := chain.BlockByHeight(5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
invalidateHash := block.Hash()
|
||||||
|
|
||||||
|
return chain, []*chainhash.Hash{invalidateHash}, tearDown
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalidate twice",
|
||||||
|
chainGen: func() (*BlockChain, []*chainhash.Hash, func()) {
|
||||||
|
chain, params, tearDown := utxoCacheTestChain("TestInvalidateBlock-invalidate-twice")
|
||||||
|
// Grab the tip of the chain.
|
||||||
|
tip := btcutil.NewBlock(params.GenesisBlock)
|
||||||
|
|
||||||
|
// Create a chain with 11 blocks.
|
||||||
|
_, spendableOuts, err := addBlocks(11, chain, tip, []*testhelper.SpendableOut{})
|
||||||
|
//_, _, err := addBlocks(11, chain, tip, []*testhelper.SpendableOut{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set invalidateHash as block 5.
|
||||||
|
block, err := chain.BlockByHeight(5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
invalidateHash := block.Hash()
|
||||||
|
|
||||||
|
// Create a side chain with 7 blocks that builds on block 1.
|
||||||
|
b1, err := chain.BlockByHeight(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
altBlockHashes, _, err := addBlocks(6, chain, b1, spendableOuts[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab block at height 5:
|
||||||
|
//
|
||||||
|
// b2, b3, b4, b5
|
||||||
|
// 0, 1, 2, 3
|
||||||
|
invalidateHash2 := altBlockHashes[3]
|
||||||
|
|
||||||
|
// Sanity checking that we grabbed the correct hash.
|
||||||
|
node := chain.index.LookupNode(invalidateHash)
|
||||||
|
if node == nil || node.height != 5 {
|
||||||
|
t.Fatalf("wanted to grab block at height 5 but got height %v",
|
||||||
|
node.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain, []*chainhash.Hash{invalidateHash, invalidateHash2}, tearDown
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalidate a side branch",
|
||||||
|
chainGen: func() (*BlockChain, []*chainhash.Hash, func()) {
|
||||||
|
chain, params, tearDown := utxoCacheTestChain("TestInvalidateBlock-invalidate-side-branch")
|
||||||
|
tip := btcutil.NewBlock(params.GenesisBlock)
|
||||||
|
|
||||||
|
// Grab the tip of the chain.
|
||||||
|
tip, err := chain.BlockByHash(&chain.bestChain.Tip().hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a chain with 11 blocks.
|
||||||
|
_, spendableOuts, err := addBlocks(11, chain, tip, []*testhelper.SpendableOut{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a side chain with 7 blocks that builds on block 1.
|
||||||
|
b1, err := chain.BlockByHeight(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
altBlockHashes, _, err := addBlocks(6, chain, b1, spendableOuts[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab block at height 4:
|
||||||
|
//
|
||||||
|
// b2, b3, b4
|
||||||
|
// 0, 1, 2
|
||||||
|
invalidateHash := altBlockHashes[2]
|
||||||
|
|
||||||
|
// Sanity checking that we grabbed the correct hash.
|
||||||
|
node := chain.index.LookupNode(invalidateHash)
|
||||||
|
if node == nil || node.height != 4 {
|
||||||
|
t.Fatalf("wanted to grab block at height 4 but got height %v",
|
||||||
|
node.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain, []*chainhash.Hash{invalidateHash}, tearDown
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
chain, invalidateHashes, tearDown := test.chainGen()
|
||||||
|
func() {
|
||||||
|
defer tearDown()
|
||||||
|
for _, invalidateHash := range invalidateHashes {
|
||||||
|
chainTipsBefore := chain.ChainTips()
|
||||||
|
|
||||||
|
// Mark if we're invalidating a block that's a part of the best chain.
|
||||||
|
var bestChainBlock bool
|
||||||
|
node := chain.index.LookupNode(invalidateHash)
|
||||||
|
if chain.bestChain.Contains(node) {
|
||||||
|
bestChainBlock = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual invalidation.
|
||||||
|
err := chain.InvalidateBlock(invalidateHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chainTipsAfter := chain.ChainTips()
|
||||||
|
|
||||||
|
// Create a map for easy lookup.
|
||||||
|
chainTipMap := make(map[chainhash.Hash]ChainTip, len(chainTipsAfter))
|
||||||
|
activeTipCount := 0
|
||||||
|
for _, chainTip := range chainTipsAfter {
|
||||||
|
chainTipMap[chainTip.BlockHash] = chainTip
|
||||||
|
|
||||||
|
if chainTip.Status == StatusActive {
|
||||||
|
activeTipCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if activeTipCount != 1 {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected "+
|
||||||
|
"1 active chain tip but got %d", activeTipCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
bestTip := chain.bestChain.Tip()
|
||||||
|
|
||||||
|
validForkCount := 0
|
||||||
|
for _, tip := range chainTipsBefore {
|
||||||
|
// If the chaintip was an active tip and we invalidated a block
|
||||||
|
// in the active tip, assert that it's invalid now.
|
||||||
|
if bestChainBlock && tip.Status == StatusActive {
|
||||||
|
gotTip, found := chainTipMap[tip.BlockHash]
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected "+
|
||||||
|
"block %s not found in chaintips after "+
|
||||||
|
"invalidateblock", tip.BlockHash.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotTip.Status != StatusInvalid {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. "+
|
||||||
|
"Expected block %s to be invalid, got status: %s",
|
||||||
|
gotTip.BlockHash.String(), gotTip.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bestChainBlock && tip.Status != StatusActive {
|
||||||
|
gotTip, found := chainTipMap[tip.BlockHash]
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected "+
|
||||||
|
"block %s not found in chaintips after "+
|
||||||
|
"invalidateblock", tip.BlockHash.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotTip.BlockHash == *invalidateHash && gotTip.Status != StatusInvalid {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. "+
|
||||||
|
"Expected block %s to be invalid, got status: %s",
|
||||||
|
gotTip.BlockHash.String(), gotTip.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're not invalidating the branch with an active tip,
|
||||||
|
// we expect the active tip to remain the same.
|
||||||
|
if !bestChainBlock && tip.Status == StatusActive && tip.BlockHash != bestTip.hash {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected block %s as the tip but got %s",
|
||||||
|
tip.BlockHash.String(), bestTip.hash.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this tip is not invalid and not active, it should be
|
||||||
|
// lighter than the current best tip.
|
||||||
|
if tip.Status != StatusActive && tip.Status != StatusInvalid &&
|
||||||
|
tip.Height > bestTip.height {
|
||||||
|
|
||||||
|
tipNode := chain.index.LookupNode(&tip.BlockHash)
|
||||||
|
if bestTip.workSum.Cmp(tipNode.workSum) == -1 {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected "+
|
||||||
|
"block %s to be the active tip but block %s "+
|
||||||
|
"was", tipNode.hash.String(), bestTip.hash.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tip.Status == StatusValidFork {
|
||||||
|
validForkCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no other valid chain tips besides the active chaintip,
|
||||||
|
// we expect to have one more chain tip after the invalidate.
|
||||||
|
if validForkCount == 0 && len(chainTipsAfter) != len(chainTipsBefore)+1 {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected %d chaintips but got %d",
|
||||||
|
len(chainTipsBefore)+1, len(chainTipsAfter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to invaliate the already invalidated hash.
|
||||||
|
err := chain.InvalidateBlock(invalidateHashes[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to invaliate a genesis block
|
||||||
|
err = chain.InvalidateBlock(chain.chainParams.GenesisHash)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected to err when trying to" +
|
||||||
|
"invalidate a genesis block.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to invaliate a block that doesn't exist.
|
||||||
|
err = chain.InvalidateBlock(chaincfg.MainNetParams.GenesisHash)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("TestInvalidateBlock fail. Expected to err when trying to" +
|
||||||
|
"invalidate a block that doesn't exist.")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/blockchain/internal/testhelper"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
@ -396,3 +397,96 @@ func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp t
|
||||||
}
|
}
|
||||||
return newBlockNode(header, parent)
|
return newBlockNode(header, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addBlock adds a block to the blockchain that succeeds the previous block.
|
||||||
|
// The blocks spends all the provided spendable outputs. The new block and
|
||||||
|
// the new spendable outputs created in the block are returned.
|
||||||
|
func addBlock(chain *BlockChain, prev *btcutil.Block, spends []*testhelper.SpendableOut) (
|
||||||
|
*btcutil.Block, []*testhelper.SpendableOut, error) {
|
||||||
|
|
||||||
|
block, outs, err := newBlock(chain, prev, spends)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = chain.ProcessBlock(block, BFNone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, outs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcMerkleRoot creates a merkle tree from the slice of transactions and
|
||||||
|
// returns the root of the tree.
|
||||||
|
func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash {
|
||||||
|
if len(txns) == 0 {
|
||||||
|
return chainhash.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
|
utilTxns := make([]*btcutil.Tx, 0, len(txns))
|
||||||
|
for _, tx := range txns {
|
||||||
|
utilTxns = append(utilTxns, btcutil.NewTx(tx))
|
||||||
|
}
|
||||||
|
return CalcMerkleRoot(utilTxns, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBlock creates a block to the blockchain that succeeds the previous block.
|
||||||
|
// The blocks spends all the provided spendable outputs. The new block and the
|
||||||
|
// newly spendable outputs created in the block are returned.
|
||||||
|
func newBlock(chain *BlockChain, prev *btcutil.Block,
|
||||||
|
spends []*testhelper.SpendableOut) (*btcutil.Block, []*testhelper.SpendableOut, error) {
|
||||||
|
|
||||||
|
blockHeight := prev.Height() + 1
|
||||||
|
txns := make([]*wire.MsgTx, 0, 1+len(spends))
|
||||||
|
|
||||||
|
// Create and add coinbase tx.
|
||||||
|
cb := testhelper.CreateCoinbaseTx(blockHeight, CalcBlockSubsidy(blockHeight, chain.chainParams))
|
||||||
|
txns = append(txns, cb)
|
||||||
|
|
||||||
|
// Spend all txs to be spent.
|
||||||
|
for _, spend := range spends {
|
||||||
|
cb.TxOut[0].Value += int64(testhelper.LowFee)
|
||||||
|
|
||||||
|
spendTx := testhelper.CreateSpendTx(spend, testhelper.LowFee)
|
||||||
|
txns = append(txns, spendTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a timestamp that is one second after the previous block unless
|
||||||
|
// this is the first block in which case the current time is used.
|
||||||
|
var ts time.Time
|
||||||
|
if blockHeight == 1 {
|
||||||
|
ts = time.Unix(time.Now().Unix(), 0)
|
||||||
|
} else {
|
||||||
|
ts = prev.MsgBlock().Header.Timestamp.Add(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the block. The nonce will be solved in the below code in
|
||||||
|
// SolveBlock.
|
||||||
|
block := btcutil.NewBlock(&wire.MsgBlock{
|
||||||
|
Header: wire.BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
PrevBlock: *prev.Hash(),
|
||||||
|
MerkleRoot: calcMerkleRoot(txns),
|
||||||
|
Bits: chain.chainParams.PowLimitBits,
|
||||||
|
Timestamp: ts,
|
||||||
|
Nonce: 0, // To be solved.
|
||||||
|
},
|
||||||
|
Transactions: txns,
|
||||||
|
})
|
||||||
|
block.SetHeight(blockHeight)
|
||||||
|
|
||||||
|
// Solve the block.
|
||||||
|
if !testhelper.SolveBlock(&block.MsgBlock().Header) {
|
||||||
|
return nil, nil, fmt.Errorf("Unable to solve block at height %d", blockHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create spendable outs to return.
|
||||||
|
outs := make([]*testhelper.SpendableOut, len(txns))
|
||||||
|
for i, tx := range txns {
|
||||||
|
out := testhelper.MakeSpendableOutForTx(tx, 0)
|
||||||
|
outs[i] = &out
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, outs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -8,31 +8,14 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/blockchain/internal/workmath"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// bigOne is 1 represented as a big.Int. It is defined here to avoid
|
|
||||||
// the overhead of creating it multiple times.
|
|
||||||
bigOne = big.NewInt(1)
|
|
||||||
|
|
||||||
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
|
|
||||||
// the overhead of creating it multiple times.
|
|
||||||
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
|
|
||||||
)
|
|
||||||
|
|
||||||
// HashToBig converts a chainhash.Hash into a big.Int that can be used to
|
// HashToBig converts a chainhash.Hash into a big.Int that can be used to
|
||||||
// perform math comparisons.
|
// perform math comparisons.
|
||||||
func HashToBig(hash *chainhash.Hash) *big.Int {
|
func HashToBig(hash *chainhash.Hash) *big.Int {
|
||||||
// A Hash is in little-endian, but the big package wants the bytes in
|
return workmath.HashToBig(hash)
|
||||||
// big-endian, so reverse them.
|
|
||||||
buf := *hash
|
|
||||||
blen := len(buf)
|
|
||||||
for i := 0; i < blen/2; i++ {
|
|
||||||
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return new(big.Int).SetBytes(buf[:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompactToBig converts a compact representation of a whole number N to an
|
// CompactToBig converts a compact representation of a whole number N to an
|
||||||
|
@ -60,31 +43,7 @@ func HashToBig(hash *chainhash.Hash) *big.Int {
|
||||||
// which represent difficulty targets, thus there really is not a need for a
|
// which represent difficulty targets, thus there really is not a need for a
|
||||||
// sign bit, but it is implemented here to stay consistent with bitcoind.
|
// sign bit, but it is implemented here to stay consistent with bitcoind.
|
||||||
func CompactToBig(compact uint32) *big.Int {
|
func CompactToBig(compact uint32) *big.Int {
|
||||||
// Extract the mantissa, sign bit, and exponent.
|
return workmath.CompactToBig(compact)
|
||||||
mantissa := compact & 0x007fffff
|
|
||||||
isNegative := compact&0x00800000 != 0
|
|
||||||
exponent := uint(compact >> 24)
|
|
||||||
|
|
||||||
// Since the base for the exponent is 256, the exponent can be treated
|
|
||||||
// as the number of bytes to represent the full 256-bit number. So,
|
|
||||||
// treat the exponent as the number of bytes and shift the mantissa
|
|
||||||
// right or left accordingly. This is equivalent to:
|
|
||||||
// N = mantissa * 256^(exponent-3)
|
|
||||||
var bn *big.Int
|
|
||||||
if exponent <= 3 {
|
|
||||||
mantissa >>= 8 * (3 - exponent)
|
|
||||||
bn = big.NewInt(int64(mantissa))
|
|
||||||
} else {
|
|
||||||
bn = big.NewInt(int64(mantissa))
|
|
||||||
bn.Lsh(bn, 8*(exponent-3))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make it negative if the sign bit is set.
|
|
||||||
if isNegative {
|
|
||||||
bn = bn.Neg(bn)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BigToCompact converts a whole number N to a compact representation using
|
// BigToCompact converts a whole number N to a compact representation using
|
||||||
|
@ -92,41 +51,7 @@ func CompactToBig(compact uint32) *big.Int {
|
||||||
// of precision, so values larger than (2^23 - 1) only encode the most
|
// of precision, so values larger than (2^23 - 1) only encode the most
|
||||||
// significant digits of the number. See CompactToBig for details.
|
// significant digits of the number. See CompactToBig for details.
|
||||||
func BigToCompact(n *big.Int) uint32 {
|
func BigToCompact(n *big.Int) uint32 {
|
||||||
// No need to do any work if it's zero.
|
return workmath.BigToCompact(n)
|
||||||
if n.Sign() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the base for the exponent is 256, the exponent can be treated
|
|
||||||
// as the number of bytes. So, shift the number right or left
|
|
||||||
// accordingly. This is equivalent to:
|
|
||||||
// mantissa = mantissa / 256^(exponent-3)
|
|
||||||
var mantissa uint32
|
|
||||||
exponent := uint(len(n.Bytes()))
|
|
||||||
if exponent <= 3 {
|
|
||||||
mantissa = uint32(n.Bits()[0])
|
|
||||||
mantissa <<= 8 * (3 - exponent)
|
|
||||||
} else {
|
|
||||||
// Use a copy to avoid modifying the caller's original number.
|
|
||||||
tn := new(big.Int).Set(n)
|
|
||||||
mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the mantissa already has the sign bit set, the number is too
|
|
||||||
// large to fit into the available 23-bits, so divide the number by 256
|
|
||||||
// and increment the exponent accordingly.
|
|
||||||
if mantissa&0x00800000 != 0 {
|
|
||||||
mantissa >>= 8
|
|
||||||
exponent++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit
|
|
||||||
// int and return it.
|
|
||||||
compact := uint32(exponent<<24) | mantissa
|
|
||||||
if n.Sign() < 0 {
|
|
||||||
compact |= 0x00800000
|
|
||||||
}
|
|
||||||
return compact
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalcWork calculates a work value from difficulty bits. Bitcoin increases
|
// CalcWork calculates a work value from difficulty bits. Bitcoin increases
|
||||||
|
@ -140,17 +65,7 @@ func BigToCompact(n *big.Int) uint32 {
|
||||||
// potential division by zero and really small floating point numbers, the
|
// potential division by zero and really small floating point numbers, the
|
||||||
// result adds 1 to the denominator and multiplies the numerator by 2^256.
|
// result adds 1 to the denominator and multiplies the numerator by 2^256.
|
||||||
func CalcWork(bits uint32) *big.Int {
|
func CalcWork(bits uint32) *big.Int {
|
||||||
// Return a work value of zero if the passed difficulty bits represent
|
return workmath.CalcWork(bits)
|
||||||
// a negative number. Note this should not happen in practice with valid
|
|
||||||
// blocks, but an invalid block could trigger it.
|
|
||||||
difficultyNum := CompactToBig(bits)
|
|
||||||
if difficultyNum.Sign() <= 0 {
|
|
||||||
return big.NewInt(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// (1 << 256) / (difficultyNum + 1)
|
|
||||||
denominator := new(big.Int).Add(difficultyNum, bigOne)
|
|
||||||
return new(big.Int).Div(oneLsh256, denominator)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calcEasiestDifficulty calculates the easiest possible difficulty that a block
|
// calcEasiestDifficulty calculates the easiest possible difficulty that a block
|
||||||
|
|
|
@ -14,11 +14,11 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
|
"github.com/btcsuite/btcd/blockchain/internal/testhelper"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
@ -43,16 +43,6 @@ const (
|
||||||
numLargeReorgBlocks = 1088
|
numLargeReorgBlocks = 1088
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// opTrueScript is simply a public key script that contains the OP_TRUE
|
|
||||||
// opcode. It is defined here to reduce garbage creation.
|
|
||||||
opTrueScript = []byte{txscript.OP_TRUE}
|
|
||||||
|
|
||||||
// lowFee is a single satoshi and exists to make the test code more
|
|
||||||
// readable.
|
|
||||||
lowFee = btcutil.Amount(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestInstance is an interface that describes a specific test instance returned
|
// TestInstance is an interface that describes a specific test instance returned
|
||||||
// by the tests generated in this package. It should be type asserted to one
|
// by the tests generated in this package. It should be type asserted to one
|
||||||
// of the concrete test instance types in order to test accordingly.
|
// of the concrete test instance types in order to test accordingly.
|
||||||
|
@ -165,31 +155,6 @@ type BlockDisconnectExpectUTXO struct {
|
||||||
// This implements the TestInstance interface.
|
// This implements the TestInstance interface.
|
||||||
func (b BlockDisconnectExpectUTXO) FullBlockTestInstance() {}
|
func (b BlockDisconnectExpectUTXO) FullBlockTestInstance() {}
|
||||||
|
|
||||||
// spendableOut represents a transaction output that is spendable along with
|
|
||||||
// additional metadata such as the block its in and how much it pays.
|
|
||||||
type spendableOut struct {
|
|
||||||
prevOut wire.OutPoint
|
|
||||||
amount btcutil.Amount
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeSpendableOutForTx returns a spendable output for the given transaction
|
|
||||||
// and transaction output index within the transaction.
|
|
||||||
func makeSpendableOutForTx(tx *wire.MsgTx, txOutIndex uint32) spendableOut {
|
|
||||||
return spendableOut{
|
|
||||||
prevOut: wire.OutPoint{
|
|
||||||
Hash: tx.TxHash(),
|
|
||||||
Index: txOutIndex,
|
|
||||||
},
|
|
||||||
amount: btcutil.Amount(tx.TxOut[txOutIndex].Value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeSpendableOut returns a spendable output for the given block, transaction
|
|
||||||
// index within the block, and transaction output index within the transaction.
|
|
||||||
func makeSpendableOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) spendableOut {
|
|
||||||
return makeSpendableOutForTx(block.Transactions[txIndex], txOutIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
// testGenerator houses state used to easy the process of generating test blocks
|
// testGenerator houses state used to easy the process of generating test blocks
|
||||||
// that build from one another along with housing other useful things such as
|
// that build from one another along with housing other useful things such as
|
||||||
// available spendable outputs used throughout the tests.
|
// available spendable outputs used throughout the tests.
|
||||||
|
@ -203,7 +168,7 @@ type testGenerator struct {
|
||||||
blockHeights map[string]int32
|
blockHeights map[string]int32
|
||||||
|
|
||||||
// Used for tracking spendable coinbase outputs.
|
// Used for tracking spendable coinbase outputs.
|
||||||
spendableOuts []spendableOut
|
spendableOuts []testhelper.SpendableOut
|
||||||
prevCollectedHash chainhash.Hash
|
prevCollectedHash chainhash.Hash
|
||||||
|
|
||||||
// Common key for any tests which require signed transactions.
|
// Common key for any tests which require signed transactions.
|
||||||
|
@ -255,62 +220,12 @@ func pushDataScript(items ...[]byte) []byte {
|
||||||
return script
|
return script
|
||||||
}
|
}
|
||||||
|
|
||||||
// standardCoinbaseScript returns a standard script suitable for use as the
|
|
||||||
// signature script of the coinbase transaction of a new block. In particular,
|
|
||||||
// it starts with the block height that is required by version 2 blocks.
|
|
||||||
func standardCoinbaseScript(blockHeight int32, extraNonce uint64) ([]byte, error) {
|
|
||||||
return txscript.NewScriptBuilder().AddInt64(int64(blockHeight)).
|
|
||||||
AddInt64(int64(extraNonce)).Script()
|
|
||||||
}
|
|
||||||
|
|
||||||
// opReturnScript returns a provably-pruneable OP_RETURN script with the
|
|
||||||
// provided data.
|
|
||||||
func opReturnScript(data []byte) []byte {
|
|
||||||
builder := txscript.NewScriptBuilder()
|
|
||||||
script, err := builder.AddOp(txscript.OP_RETURN).AddData(data).Script()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return script
|
|
||||||
}
|
|
||||||
|
|
||||||
// uniqueOpReturnScript returns a standard provably-pruneable OP_RETURN script
|
|
||||||
// with a random uint64 encoded as the data.
|
|
||||||
func uniqueOpReturnScript() []byte {
|
|
||||||
rand, err := wire.RandomUint64()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make([]byte, 8)
|
|
||||||
binary.LittleEndian.PutUint64(data[0:8], rand)
|
|
||||||
return opReturnScript(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// createCoinbaseTx returns a coinbase transaction paying an appropriate
|
// createCoinbaseTx returns a coinbase transaction paying an appropriate
|
||||||
// subsidy based on the passed block height. The coinbase signature script
|
// subsidy based on the passed block height. The coinbase signature script
|
||||||
// conforms to the requirements of version 2 blocks.
|
// conforms to the requirements of version 2 blocks.
|
||||||
func (g *testGenerator) createCoinbaseTx(blockHeight int32) *wire.MsgTx {
|
func (g *testGenerator) createCoinbaseTx(blockHeight int32) *wire.MsgTx {
|
||||||
extraNonce := uint64(0)
|
return testhelper.CreateCoinbaseTx(
|
||||||
coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce)
|
blockHeight, blockchain.CalcBlockSubsidy(blockHeight, g.params))
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tx := wire.NewMsgTx(1)
|
|
||||||
tx.AddTxIn(&wire.TxIn{
|
|
||||||
// Coinbase transactions have no inputs, so previous outpoint is
|
|
||||||
// zero hash and max index.
|
|
||||||
PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
|
|
||||||
wire.MaxPrevOutIndex),
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
SignatureScript: coinbaseScript,
|
|
||||||
})
|
|
||||||
tx.AddTxOut(&wire.TxOut{
|
|
||||||
Value: blockchain.CalcBlockSubsidy(blockHeight, g.params),
|
|
||||||
PkScript: opTrueScript,
|
|
||||||
})
|
|
||||||
return tx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calcMerkleRoot creates a merkle tree from the slice of transactions and
|
// calcMerkleRoot creates a merkle tree from the slice of transactions and
|
||||||
|
@ -327,71 +242,6 @@ func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash {
|
||||||
return blockchain.CalcMerkleRoot(utilTxns, false)
|
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
|
// additionalCoinbase returns a function that itself takes a block and
|
||||||
// modifies it by adding the provided amount to coinbase subsidy.
|
// modifies it by adding the provided amount to coinbase subsidy.
|
||||||
func additionalCoinbase(amount btcutil.Amount) func(*wire.MsgBlock) {
|
func additionalCoinbase(amount btcutil.Amount) func(*wire.MsgBlock) {
|
||||||
|
@ -444,33 +294,14 @@ func additionalTx(tx *wire.MsgTx) func(*wire.MsgBlock) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSpendTx creates a transaction that spends from the provided spendable
|
|
||||||
// output and includes an additional unique OP_RETURN output to ensure the
|
|
||||||
// transaction ends up with a unique hash. The script is a simple OP_TRUE
|
|
||||||
// script which avoids the need to track addresses and signature scripts in the
|
|
||||||
// tests.
|
|
||||||
func createSpendTx(spend *spendableOut, fee btcutil.Amount) *wire.MsgTx {
|
|
||||||
spendTx := wire.NewMsgTx(1)
|
|
||||||
spendTx.AddTxIn(&wire.TxIn{
|
|
||||||
PreviousOutPoint: spend.prevOut,
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
SignatureScript: nil,
|
|
||||||
})
|
|
||||||
spendTx.AddTxOut(wire.NewTxOut(int64(spend.amount-fee),
|
|
||||||
opTrueScript))
|
|
||||||
spendTx.AddTxOut(wire.NewTxOut(0, uniqueOpReturnScript()))
|
|
||||||
|
|
||||||
return spendTx
|
|
||||||
}
|
|
||||||
|
|
||||||
// createSpendTxForTx creates a transaction that spends from the first output of
|
// createSpendTxForTx creates a transaction that spends from the first output of
|
||||||
// the provided transaction and includes an additional unique OP_RETURN output
|
// the provided transaction and includes an additional unique OP_RETURN output
|
||||||
// to ensure the transaction ends up with a unique hash. The public key script
|
// to ensure the transaction ends up with a unique hash. The public key script
|
||||||
// is a simple OP_TRUE script which avoids the need to track addresses and
|
// is a simple OP_TRUE script which avoids the need to track addresses and
|
||||||
// signature scripts in the tests. The signature script is nil.
|
// signature scripts in the tests. The signature script is nil.
|
||||||
func createSpendTxForTx(tx *wire.MsgTx, fee btcutil.Amount) *wire.MsgTx {
|
func createSpendTxForTx(tx *wire.MsgTx, fee btcutil.Amount) *wire.MsgTx {
|
||||||
spend := makeSpendableOutForTx(tx, 0)
|
spend := testhelper.MakeSpendableOutForTx(tx, 0)
|
||||||
return createSpendTx(&spend, fee)
|
return testhelper.CreateSpendTx(&spend, fee)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextBlock builds a new block that extends the current tip associated with the
|
// nextBlock builds a new block that extends the current tip associated with the
|
||||||
|
@ -492,7 +323,7 @@ func createSpendTxForTx(tx *wire.MsgTx, fee btcutil.Amount) *wire.MsgTx {
|
||||||
// applied after all munge functions have been invoked:
|
// applied after all munge functions have been invoked:
|
||||||
// - The merkle root will be recalculated unless it was manually changed
|
// - The merkle root will be recalculated unless it was manually changed
|
||||||
// - The block will be solved unless the nonce was changed
|
// - The block will be solved unless the nonce was changed
|
||||||
func (g *testGenerator) nextBlock(blockName string, spend *spendableOut, mungers ...func(*wire.MsgBlock)) *wire.MsgBlock {
|
func (g *testGenerator) nextBlock(blockName string, spend *testhelper.SpendableOut, mungers ...func(*wire.MsgBlock)) *wire.MsgBlock {
|
||||||
// Create coinbase transaction for the block using any additional
|
// Create coinbase transaction for the block using any additional
|
||||||
// subsidy if specified.
|
// subsidy if specified.
|
||||||
nextHeight := g.tipHeight + 1
|
nextHeight := g.tipHeight + 1
|
||||||
|
@ -510,7 +341,7 @@ func (g *testGenerator) nextBlock(blockName string, spend *spendableOut, mungers
|
||||||
// add it to the list of transactions to include in the block.
|
// add it to the list of transactions to include in the block.
|
||||||
// The script is a simple OP_TRUE script in order to avoid the
|
// The script is a simple OP_TRUE script in order to avoid the
|
||||||
// need to track addresses and signature scripts in the tests.
|
// need to track addresses and signature scripts in the tests.
|
||||||
txns = append(txns, createSpendTx(spend, fee))
|
txns = append(txns, testhelper.CreateSpendTx(spend, fee))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a timestamp that is one second after the previous block unless
|
// Use a timestamp that is one second after the previous block unless
|
||||||
|
@ -547,7 +378,7 @@ func (g *testGenerator) nextBlock(blockName string, spend *spendableOut, mungers
|
||||||
|
|
||||||
// Only solve the block if the nonce wasn't manually changed by a munge
|
// Only solve the block if the nonce wasn't manually changed by a munge
|
||||||
// function.
|
// 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",
|
panic(fmt.Sprintf("Unable to solve block at height %d",
|
||||||
nextHeight))
|
nextHeight))
|
||||||
}
|
}
|
||||||
|
@ -594,7 +425,7 @@ func (g *testGenerator) setTip(blockName string) {
|
||||||
|
|
||||||
// oldestCoinbaseOuts removes the oldest coinbase output that was previously
|
// oldestCoinbaseOuts removes the oldest coinbase output that was previously
|
||||||
// saved to the generator and returns the set as a slice.
|
// saved to the generator and returns the set as a slice.
|
||||||
func (g *testGenerator) oldestCoinbaseOut() spendableOut {
|
func (g *testGenerator) oldestCoinbaseOut() testhelper.SpendableOut {
|
||||||
op := g.spendableOuts[0]
|
op := g.spendableOuts[0]
|
||||||
g.spendableOuts = g.spendableOuts[1:]
|
g.spendableOuts = g.spendableOuts[1:]
|
||||||
return op
|
return op
|
||||||
|
@ -603,7 +434,7 @@ func (g *testGenerator) oldestCoinbaseOut() spendableOut {
|
||||||
// saveTipCoinbaseOut adds the coinbase tx output in the current tip block to
|
// saveTipCoinbaseOut adds the coinbase tx output in the current tip block to
|
||||||
// the list of spendable outputs.
|
// the list of spendable outputs.
|
||||||
func (g *testGenerator) saveTipCoinbaseOut() {
|
func (g *testGenerator) saveTipCoinbaseOut() {
|
||||||
g.spendableOuts = append(g.spendableOuts, makeSpendableOut(g.tip, 0, 0))
|
g.spendableOuts = append(g.spendableOuts, testhelper.MakeSpendableOut(g.tip, 0, 0))
|
||||||
g.prevCollectedHash = g.tip.BlockHash()
|
g.prevCollectedHash = g.tip.BlockHash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,7 +778,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
tests = append(tests, testInstances)
|
tests = append(tests, testInstances)
|
||||||
|
|
||||||
// Collect spendable outputs. This simplifies the code below.
|
// Collect spendable outputs. This simplifies the code below.
|
||||||
var outs []*spendableOut
|
var outs []*testhelper.SpendableOut
|
||||||
for i := uint16(0); i < coinbaseMaturity; i++ {
|
for i := uint16(0); i < coinbaseMaturity; i++ {
|
||||||
op := g.oldestCoinbaseOut()
|
op := g.oldestCoinbaseOut()
|
||||||
outs = append(outs, &op)
|
outs = append(outs, &op)
|
||||||
|
@ -985,7 +816,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// \-> b3(1)
|
// \-> b3(1)
|
||||||
g.setTip("b1")
|
g.setTip("b1")
|
||||||
g.nextBlock("b3", outs[1])
|
g.nextBlock("b3", outs[1])
|
||||||
b3Tx1Out := makeSpendableOut(g.tip, 1, 0)
|
b3Tx1Out := testhelper.MakeSpendableOut(g.tip, 1, 0)
|
||||||
acceptedToSideChainWithExpectedTip("b2")
|
acceptedToSideChainWithExpectedTip("b2")
|
||||||
|
|
||||||
// Extend b3 fork to make the alternative chain longer and force reorg.
|
// Extend b3 fork to make the alternative chain longer and force reorg.
|
||||||
|
@ -1298,9 +1129,9 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// \-> b38(b37.tx[1])
|
// \-> b38(b37.tx[1])
|
||||||
//
|
//
|
||||||
g.setTip("b35")
|
g.setTip("b35")
|
||||||
doubleSpendTx := createSpendTx(outs[11], lowFee)
|
doubleSpendTx := testhelper.CreateSpendTx(outs[11], testhelper.LowFee)
|
||||||
g.nextBlock("b37", outs[11], additionalTx(doubleSpendTx))
|
g.nextBlock("b37", outs[11], additionalTx(doubleSpendTx))
|
||||||
b37Tx1Out := makeSpendableOut(g.tip, 1, 0)
|
b37Tx1Out := testhelper.MakeSpendableOut(g.tip, 1, 0)
|
||||||
rejected(blockchain.ErrMissingTxOut)
|
rejected(blockchain.ErrMissingTxOut)
|
||||||
|
|
||||||
g.setTip("b35")
|
g.setTip("b35")
|
||||||
|
@ -1336,7 +1167,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
txnsNeeded := (maxBlockSigOps / redeemScriptSigOps) + 1
|
txnsNeeded := (maxBlockSigOps / redeemScriptSigOps) + 1
|
||||||
prevTx := b.Transactions[1]
|
prevTx := b.Transactions[1]
|
||||||
for i := 0; i < txnsNeeded; i++ {
|
for i := 0; i < txnsNeeded; i++ {
|
||||||
prevTx = createSpendTxForTx(prevTx, lowFee)
|
prevTx = createSpendTxForTx(prevTx, testhelper.LowFee)
|
||||||
prevTx.TxOut[0].Value -= 2
|
prevTx.TxOut[0].Value -= 2
|
||||||
prevTx.AddTxOut(wire.NewTxOut(2, p2shScript))
|
prevTx.AddTxOut(wire.NewTxOut(2, p2shScript))
|
||||||
b.AddTransaction(prevTx)
|
b.AddTransaction(prevTx)
|
||||||
|
@ -1356,8 +1187,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
for i := 0; i < txnsNeeded; i++ {
|
for i := 0; i < txnsNeeded; i++ {
|
||||||
// Create a signed transaction that spends from the
|
// Create a signed transaction that spends from the
|
||||||
// associated p2sh output in b39.
|
// associated p2sh output in b39.
|
||||||
spend := makeSpendableOutForTx(b39.Transactions[i+2], 2)
|
spend := testhelper.MakeSpendableOutForTx(b39.Transactions[i+2], 2)
|
||||||
tx := createSpendTx(&spend, lowFee)
|
tx := testhelper.CreateSpendTx(&spend, testhelper.LowFee)
|
||||||
sig, err := txscript.RawTxInSignature(tx, 0,
|
sig, err := txscript.RawTxInSignature(tx, 0,
|
||||||
redeemScript, txscript.SigHashAll, g.privKey)
|
redeemScript, txscript.SigHashAll, g.privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1373,7 +1204,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// the block one over the max allowed.
|
// the block one over the max allowed.
|
||||||
fill := maxBlockSigOps - (txnsNeeded * redeemScriptSigOps) + 1
|
fill := maxBlockSigOps - (txnsNeeded * redeemScriptSigOps) + 1
|
||||||
finalTx := b.Transactions[len(b.Transactions)-1]
|
finalTx := b.Transactions[len(b.Transactions)-1]
|
||||||
tx := createSpendTxForTx(finalTx, lowFee)
|
tx := createSpendTxForTx(finalTx, testhelper.LowFee)
|
||||||
tx.TxOut[0].PkScript = repeatOpcode(txscript.OP_CHECKSIG, fill)
|
tx.TxOut[0].PkScript = repeatOpcode(txscript.OP_CHECKSIG, fill)
|
||||||
b.AddTransaction(tx)
|
b.AddTransaction(tx)
|
||||||
})
|
})
|
||||||
|
@ -1387,8 +1218,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
g.nextBlock("b41", outs[12], func(b *wire.MsgBlock) {
|
g.nextBlock("b41", outs[12], func(b *wire.MsgBlock) {
|
||||||
txnsNeeded := (maxBlockSigOps / redeemScriptSigOps)
|
txnsNeeded := (maxBlockSigOps / redeemScriptSigOps)
|
||||||
for i := 0; i < txnsNeeded; i++ {
|
for i := 0; i < txnsNeeded; i++ {
|
||||||
spend := makeSpendableOutForTx(b39.Transactions[i+2], 2)
|
spend := testhelper.MakeSpendableOutForTx(b39.Transactions[i+2], 2)
|
||||||
tx := createSpendTx(&spend, lowFee)
|
tx := testhelper.CreateSpendTx(&spend, testhelper.LowFee)
|
||||||
sig, err := txscript.RawTxInSignature(tx, 0,
|
sig, err := txscript.RawTxInSignature(tx, 0,
|
||||||
redeemScript, txscript.SigHashAll, g.privKey)
|
redeemScript, txscript.SigHashAll, g.privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1407,7 +1238,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
finalTx := b.Transactions[len(b.Transactions)-1]
|
finalTx := b.Transactions[len(b.Transactions)-1]
|
||||||
tx := createSpendTxForTx(finalTx, lowFee)
|
tx := createSpendTxForTx(finalTx, testhelper.LowFee)
|
||||||
tx.TxOut[0].PkScript = repeatOpcode(txscript.OP_CHECKSIG, fill)
|
tx.TxOut[0].PkScript = repeatOpcode(txscript.OP_CHECKSIG, fill)
|
||||||
b.AddTransaction(tx)
|
b.AddTransaction(tx)
|
||||||
})
|
})
|
||||||
|
@ -1437,7 +1268,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// ... -> b43(13)
|
// ... -> b43(13)
|
||||||
// \-> b44(14)
|
// \-> b44(14)
|
||||||
g.nextBlock("b44", nil, func(b *wire.MsgBlock) {
|
g.nextBlock("b44", nil, func(b *wire.MsgBlock) {
|
||||||
nonCoinbaseTx := createSpendTx(outs[14], lowFee)
|
nonCoinbaseTx := testhelper.CreateSpendTx(outs[14], testhelper.LowFee)
|
||||||
b.Transactions[0] = nonCoinbaseTx
|
b.Transactions[0] = nonCoinbaseTx
|
||||||
})
|
})
|
||||||
rejected(blockchain.ErrFirstTxNotCoinbase)
|
rejected(blockchain.ErrFirstTxNotCoinbase)
|
||||||
|
@ -1642,7 +1473,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
g.setTip("b55")
|
g.setTip("b55")
|
||||||
b57 := g.nextBlock("b57", outs[16], func(b *wire.MsgBlock) {
|
b57 := g.nextBlock("b57", outs[16], func(b *wire.MsgBlock) {
|
||||||
tx2 := b.Transactions[1]
|
tx2 := b.Transactions[1]
|
||||||
tx3 := createSpendTxForTx(tx2, lowFee)
|
tx3 := createSpendTxForTx(tx2, testhelper.LowFee)
|
||||||
b.AddTransaction(tx3)
|
b.AddTransaction(tx3)
|
||||||
})
|
})
|
||||||
g.assertTipBlockNumTxns(3)
|
g.assertTipBlockNumTxns(3)
|
||||||
|
@ -1687,7 +1518,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// in the block.
|
// in the block.
|
||||||
spendTx := b.Transactions[1]
|
spendTx := b.Transactions[1]
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
spendTx = createSpendTxForTx(spendTx, lowFee)
|
spendTx = createSpendTxForTx(spendTx, testhelper.LowFee)
|
||||||
b.AddTransaction(spendTx)
|
b.AddTransaction(spendTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1719,7 +1550,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// \-> b59(17)
|
// \-> b59(17)
|
||||||
g.setTip("b57")
|
g.setTip("b57")
|
||||||
g.nextBlock("b59", outs[17], func(b *wire.MsgBlock) {
|
g.nextBlock("b59", outs[17], func(b *wire.MsgBlock) {
|
||||||
b.Transactions[1].TxOut[0].Value = int64(outs[17].amount) + 1
|
b.Transactions[1].TxOut[0].Value = int64(outs[17].Amount) + 1
|
||||||
})
|
})
|
||||||
rejected(blockchain.ErrSpendTooHigh)
|
rejected(blockchain.ErrSpendTooHigh)
|
||||||
|
|
||||||
|
@ -1824,7 +1655,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// ... b64(18) -> b65(19)
|
// ... b64(18) -> b65(19)
|
||||||
g.setTip("b64")
|
g.setTip("b64")
|
||||||
g.nextBlock("b65", outs[19], func(b *wire.MsgBlock) {
|
g.nextBlock("b65", outs[19], func(b *wire.MsgBlock) {
|
||||||
tx3 := createSpendTxForTx(b.Transactions[1], lowFee)
|
tx3 := createSpendTxForTx(b.Transactions[1], testhelper.LowFee)
|
||||||
b.AddTransaction(tx3)
|
b.AddTransaction(tx3)
|
||||||
})
|
})
|
||||||
accepted()
|
accepted()
|
||||||
|
@ -1834,8 +1665,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// ... -> b65(19)
|
// ... -> b65(19)
|
||||||
// \-> b66(20)
|
// \-> b66(20)
|
||||||
g.nextBlock("b66", nil, func(b *wire.MsgBlock) {
|
g.nextBlock("b66", nil, func(b *wire.MsgBlock) {
|
||||||
tx2 := createSpendTx(outs[20], lowFee)
|
tx2 := testhelper.CreateSpendTx(outs[20], testhelper.LowFee)
|
||||||
tx3 := createSpendTxForTx(tx2, lowFee)
|
tx3 := createSpendTxForTx(tx2, testhelper.LowFee)
|
||||||
b.AddTransaction(tx3)
|
b.AddTransaction(tx3)
|
||||||
b.AddTransaction(tx2)
|
b.AddTransaction(tx2)
|
||||||
})
|
})
|
||||||
|
@ -1849,8 +1680,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
g.setTip("b65")
|
g.setTip("b65")
|
||||||
g.nextBlock("b67", outs[20], func(b *wire.MsgBlock) {
|
g.nextBlock("b67", outs[20], func(b *wire.MsgBlock) {
|
||||||
tx2 := b.Transactions[1]
|
tx2 := b.Transactions[1]
|
||||||
tx3 := createSpendTxForTx(tx2, lowFee)
|
tx3 := createSpendTxForTx(tx2, testhelper.LowFee)
|
||||||
tx4 := createSpendTxForTx(tx2, lowFee)
|
tx4 := createSpendTxForTx(tx2, testhelper.LowFee)
|
||||||
b.AddTransaction(tx3)
|
b.AddTransaction(tx3)
|
||||||
b.AddTransaction(tx4)
|
b.AddTransaction(tx4)
|
||||||
})
|
})
|
||||||
|
@ -1973,7 +1804,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
txscript.OP_ELSE, txscript.OP_TRUE, txscript.OP_ENDIF}
|
txscript.OP_ELSE, txscript.OP_TRUE, txscript.OP_ENDIF}
|
||||||
g.nextBlock("b74", outs[23], replaceSpendScript(script), func(b *wire.MsgBlock) {
|
g.nextBlock("b74", outs[23], replaceSpendScript(script), func(b *wire.MsgBlock) {
|
||||||
tx2 := b.Transactions[1]
|
tx2 := b.Transactions[1]
|
||||||
tx3 := createSpendTxForTx(tx2, lowFee)
|
tx3 := createSpendTxForTx(tx2, testhelper.LowFee)
|
||||||
tx3.TxIn[0].SignatureScript = []byte{txscript.OP_FALSE}
|
tx3.TxIn[0].SignatureScript = []byte{txscript.OP_FALSE}
|
||||||
b.AddTransaction(tx3)
|
b.AddTransaction(tx3)
|
||||||
})
|
})
|
||||||
|
@ -1994,7 +1825,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
const zeroCoin = int64(0)
|
const zeroCoin = int64(0)
|
||||||
spendTx := b.Transactions[1]
|
spendTx := b.Transactions[1]
|
||||||
for i := 0; i < numAdditionalOutputs; i++ {
|
for i := 0; i < numAdditionalOutputs; i++ {
|
||||||
spendTx.AddTxOut(wire.NewTxOut(zeroCoin, opTrueScript))
|
spendTx.AddTxOut(wire.NewTxOut(zeroCoin, testhelper.OpTrueScript))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add transactions spending from the outputs added above that
|
// Add transactions spending from the outputs added above that
|
||||||
|
@ -2003,14 +1834,14 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// NOTE: The createSpendTx func adds the OP_RETURN output.
|
// NOTE: The createSpendTx func adds the OP_RETURN output.
|
||||||
zeroFee := btcutil.Amount(0)
|
zeroFee := btcutil.Amount(0)
|
||||||
for i := uint32(0); i < numAdditionalOutputs; i++ {
|
for i := uint32(0); i < numAdditionalOutputs; i++ {
|
||||||
spend := makeSpendableOut(b, 1, i+2)
|
spend := testhelper.MakeSpendableOut(b, 1, i+2)
|
||||||
tx := createSpendTx(&spend, zeroFee)
|
tx := testhelper.CreateSpendTx(&spend, zeroFee)
|
||||||
b.AddTransaction(tx)
|
b.AddTransaction(tx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
g.assertTipBlockNumTxns(6)
|
g.assertTipBlockNumTxns(6)
|
||||||
g.assertTipBlockTxOutOpReturn(5, 1)
|
g.assertTipBlockTxOutOpReturn(5, 1)
|
||||||
b75OpReturnOut := makeSpendableOut(g.tip, 5, 1)
|
b75OpReturnOut := testhelper.MakeSpendableOut(g.tip, 5, 1)
|
||||||
accepted()
|
accepted()
|
||||||
|
|
||||||
// Reorg to a side chain that does not contain the OP_RETURNs.
|
// Reorg to a side chain that does not contain the OP_RETURNs.
|
||||||
|
@ -2043,7 +1874,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// An OP_RETURN output doesn't have any value and the default behavior
|
// An OP_RETURN output doesn't have any value and the default behavior
|
||||||
// of nextBlock is to assign a fee of one, so increment the amount here
|
// of nextBlock is to assign a fee of one, so increment the amount here
|
||||||
// to effective negate that behavior.
|
// to effective negate that behavior.
|
||||||
b75OpReturnOut.amount++
|
b75OpReturnOut.Amount++
|
||||||
g.nextBlock("b80", &b75OpReturnOut)
|
g.nextBlock("b80", &b75OpReturnOut)
|
||||||
rejected(blockchain.ErrMissingTxOut)
|
rejected(blockchain.ErrMissingTxOut)
|
||||||
|
|
||||||
|
@ -2059,7 +1890,10 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
const zeroCoin = int64(0)
|
const zeroCoin = int64(0)
|
||||||
spendTx := b.Transactions[1]
|
spendTx := b.Transactions[1]
|
||||||
for i := 0; i < numAdditionalOutputs; i++ {
|
for i := 0; i < numAdditionalOutputs; i++ {
|
||||||
opRetScript := uniqueOpReturnScript()
|
opRetScript, err := testhelper.UniqueOpReturnScript()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
spendTx.AddTxOut(wire.NewTxOut(zeroCoin, opRetScript))
|
spendTx.AddTxOut(wire.NewTxOut(zeroCoin, opRetScript))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -2075,7 +1909,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
g.nextBlock("b82a", outs[28])
|
g.nextBlock("b82a", outs[28])
|
||||||
accepted()
|
accepted()
|
||||||
|
|
||||||
b82aTx1Out0 := makeSpendableOut(g.tip, 1, 0)
|
b82aTx1Out0 := testhelper.MakeSpendableOut(g.tip, 1, 0)
|
||||||
g.nextBlock("b83a", &b82aTx1Out0)
|
g.nextBlock("b83a", &b82aTx1Out0)
|
||||||
accepted()
|
accepted()
|
||||||
|
|
||||||
|
@ -2097,17 +1931,17 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
// We expect b82a output to now be a utxo since b83a was spending it and it was
|
// We expect b82a output to now be a utxo since b83a was spending it and it was
|
||||||
// removed from the main chain.
|
// removed from the main chain.
|
||||||
blockDisconnectExpectUTXO("b82aTx1Out0",
|
blockDisconnectExpectUTXO("b82aTx1Out0",
|
||||||
true, b82aTx1Out0.prevOut, g.blocksByName["b83a"].BlockHash())
|
true, b82aTx1Out0.PrevOut, g.blocksByName["b83a"].BlockHash())
|
||||||
|
|
||||||
// We expect the output from b82 to not exist once b82a itself has been removed
|
// We expect the output from b82 to not exist once b82a itself has been removed
|
||||||
// from the main chain.
|
// from the main chain.
|
||||||
blockDisconnectExpectUTXO("b82aTx1Out0",
|
blockDisconnectExpectUTXO("b82aTx1Out0",
|
||||||
false, b82aTx1Out0.prevOut, g.blocksByName["b82a"].BlockHash())
|
false, b82aTx1Out0.PrevOut, g.blocksByName["b82a"].BlockHash())
|
||||||
|
|
||||||
// The output that was being spent in b82a should exist after the removal of
|
// The output that was being spent in b82a should exist after the removal of
|
||||||
// b82a.
|
// b82a.
|
||||||
blockDisconnectExpectUTXO("outs[28]",
|
blockDisconnectExpectUTXO("outs[28]",
|
||||||
true, outs[28].prevOut, g.blocksByName["b82a"].BlockHash())
|
true, outs[28].PrevOut, g.blocksByName["b82a"].BlockHash())
|
||||||
|
|
||||||
// Create block 84 and reorg out the sidechain with b83a as the tip.
|
// Create block 84 and reorg out the sidechain with b83a as the tip.
|
||||||
//
|
//
|
||||||
|
|
16
blockchain/internal/testhelper/README.md
Normal file
16
blockchain/internal/testhelper/README.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
testhelper
|
||||||
|
==========
|
||||||
|
|
||||||
|
[](https://github.com/btcsuite/btcd/actions)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/testhelper)
|
||||||
|
|
||||||
|
Package testhelper provides functions that are used internally in the
|
||||||
|
btcd/blockchain and btcd/blockchain/fullblocktests package to test consensus
|
||||||
|
validation rules. Mainly provided to avoid dependency cycles internally among
|
||||||
|
the different packages in btcd.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Package testhelper is licensed under the [copyfree](http://copyfree.org) ISC
|
||||||
|
License.
|
194
blockchain/internal/testhelper/common.go
Normal file
194
blockchain/internal/testhelper/common.go
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package testhelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/blockchain/internal/workmath"
|
||||||
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// OpTrueScript is simply a public key script that contains the OP_TRUE
|
||||||
|
// opcode. It is defined here to reduce garbage creation.
|
||||||
|
OpTrueScript = []byte{txscript.OP_TRUE}
|
||||||
|
|
||||||
|
// LowFee is a single satoshi and exists to make the test code more
|
||||||
|
// readable.
|
||||||
|
LowFee = btcutil.Amount(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateSpendTx creates a transaction that spends from the provided spendable
|
||||||
|
// output and includes an additional unique OP_RETURN output to ensure the
|
||||||
|
// transaction ends up with a unique hash. The script is a simple OP_TRUE
|
||||||
|
// script which avoids the need to track addresses and signature scripts in the
|
||||||
|
// tests.
|
||||||
|
func CreateSpendTx(spend *SpendableOut, fee btcutil.Amount) *wire.MsgTx {
|
||||||
|
spendTx := wire.NewMsgTx(1)
|
||||||
|
spendTx.AddTxIn(&wire.TxIn{
|
||||||
|
PreviousOutPoint: spend.PrevOut,
|
||||||
|
Sequence: wire.MaxTxInSequenceNum,
|
||||||
|
SignatureScript: nil,
|
||||||
|
})
|
||||||
|
spendTx.AddTxOut(wire.NewTxOut(int64(spend.Amount-fee),
|
||||||
|
OpTrueScript))
|
||||||
|
opRetScript, err := UniqueOpReturnScript()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
spendTx.AddTxOut(wire.NewTxOut(0, opRetScript))
|
||||||
|
|
||||||
|
return spendTx
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCoinbaseTx returns a coinbase transaction paying an appropriate
|
||||||
|
// subsidy based on the passed block height and the block subsidy. The
|
||||||
|
// coinbase signature script conforms to the requirements of version 2 blocks.
|
||||||
|
func CreateCoinbaseTx(blockHeight int32, blockSubsidy int64) *wire.MsgTx {
|
||||||
|
extraNonce := uint64(0)
|
||||||
|
coinbaseScript, err := StandardCoinbaseScript(blockHeight, extraNonce)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := wire.NewMsgTx(1)
|
||||||
|
tx.AddTxIn(&wire.TxIn{
|
||||||
|
// Coinbase transactions have no inputs, so previous outpoint is
|
||||||
|
// zero hash and max index.
|
||||||
|
PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
|
||||||
|
wire.MaxPrevOutIndex),
|
||||||
|
Sequence: wire.MaxTxInSequenceNum,
|
||||||
|
SignatureScript: coinbaseScript,
|
||||||
|
})
|
||||||
|
tx.AddTxOut(&wire.TxOut{
|
||||||
|
Value: blockSubsidy,
|
||||||
|
PkScript: OpTrueScript,
|
||||||
|
})
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardCoinbaseScript returns a standard script suitable for use as the
|
||||||
|
// signature script of the coinbase transaction of a new block. In particular,
|
||||||
|
// it starts with the block height that is required by version 2 blocks.
|
||||||
|
func StandardCoinbaseScript(blockHeight int32, extraNonce uint64) ([]byte, error) {
|
||||||
|
return txscript.NewScriptBuilder().AddInt64(int64(blockHeight)).
|
||||||
|
AddInt64(int64(extraNonce)).Script()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpReturnScript returns a provably-pruneable OP_RETURN script with the
|
||||||
|
// provided data.
|
||||||
|
func OpReturnScript(data []byte) ([]byte, error) {
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
script, err := builder.AddOp(txscript.OP_RETURN).AddData(data).Script()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return script, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueOpReturnScript returns a standard provably-pruneable OP_RETURN script
|
||||||
|
// with a random uint64 encoded as the data.
|
||||||
|
func UniqueOpReturnScript() ([]byte, error) {
|
||||||
|
rand, err := wire.RandomUint64()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(data[0:8], rand)
|
||||||
|
return OpReturnScript(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpendableOut represents a transaction output that is spendable along with
|
||||||
|
// additional metadata such as the block its in and how much it pays.
|
||||||
|
type SpendableOut struct {
|
||||||
|
PrevOut wire.OutPoint
|
||||||
|
Amount btcutil.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSpendableOutForTx returns a spendable output for the given transaction
|
||||||
|
// and transaction output index within the transaction.
|
||||||
|
func MakeSpendableOutForTx(tx *wire.MsgTx, txOutIndex uint32) SpendableOut {
|
||||||
|
return SpendableOut{
|
||||||
|
PrevOut: wire.OutPoint{
|
||||||
|
Hash: tx.TxHash(),
|
||||||
|
Index: txOutIndex,
|
||||||
|
},
|
||||||
|
Amount: btcutil.Amount(tx.TxOut[txOutIndex].Value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSpendableOut returns a spendable output for the given block, transaction
|
||||||
|
// index within the block, and transaction output index within the transaction.
|
||||||
|
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
|
||||||
|
}
|
15
blockchain/internal/workmath/README.md
Normal file
15
blockchain/internal/workmath/README.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
workmath
|
||||||
|
==========
|
||||||
|
|
||||||
|
[](https://github.com/btcsuite/btcd/actions)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](https://pkg.go.dev/github.com/btcsuite/btcd/workmath)
|
||||||
|
|
||||||
|
Package workmath provides utility functions that are related with calculating
|
||||||
|
the work from difficulty bits. This package was introduced to avoid import
|
||||||
|
cycles in btcd.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Package workmath is licensed under the [copyfree](http://copyfree.org) ISC
|
||||||
|
License.
|
153
blockchain/internal/workmath/difficulty.go
Normal file
153
blockchain/internal/workmath/difficulty.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright (c) 2013-2017 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package workmath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// bigOne is 1 represented as a big.Int. It is defined here to avoid
|
||||||
|
// the overhead of creating it multiple times.
|
||||||
|
bigOne = big.NewInt(1)
|
||||||
|
|
||||||
|
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
|
||||||
|
// the overhead of creating it multiple times.
|
||||||
|
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
|
||||||
|
)
|
||||||
|
|
||||||
|
// HashToBig converts a chainhash.Hash into a big.Int that can be used to
|
||||||
|
// perform math comparisons.
|
||||||
|
func HashToBig(hash *chainhash.Hash) *big.Int {
|
||||||
|
// A Hash is in little-endian, but the big package wants the bytes in
|
||||||
|
// big-endian, so reverse them.
|
||||||
|
buf := *hash
|
||||||
|
blen := len(buf)
|
||||||
|
for i := 0; i < blen/2; i++ {
|
||||||
|
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(big.Int).SetBytes(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompactToBig converts a compact representation of a whole number N to an
|
||||||
|
// unsigned 32-bit number. The representation is similar to IEEE754 floating
|
||||||
|
// point numbers.
|
||||||
|
//
|
||||||
|
// Like IEEE754 floating point, there are three basic components: the sign,
|
||||||
|
// the exponent, and the mantissa. They are broken out as follows:
|
||||||
|
//
|
||||||
|
// - the most significant 8 bits represent the unsigned base 256 exponent
|
||||||
|
// - bit 23 (the 24th bit) represents the sign bit
|
||||||
|
// - the least significant 23 bits represent the mantissa
|
||||||
|
//
|
||||||
|
// -------------------------------------------------
|
||||||
|
// | Exponent | Sign | Mantissa |
|
||||||
|
// -------------------------------------------------
|
||||||
|
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
|
||||||
|
// -------------------------------------------------
|
||||||
|
//
|
||||||
|
// The formula to calculate N is:
|
||||||
|
//
|
||||||
|
// N = (-1^sign) * mantissa * 256^(exponent-3)
|
||||||
|
//
|
||||||
|
// This compact form is only used in bitcoin to encode unsigned 256-bit numbers
|
||||||
|
// which represent difficulty targets, thus there really is not a need for a
|
||||||
|
// sign bit, but it is implemented here to stay consistent with bitcoind.
|
||||||
|
func CompactToBig(compact uint32) *big.Int {
|
||||||
|
// Extract the mantissa, sign bit, and exponent.
|
||||||
|
mantissa := compact & 0x007fffff
|
||||||
|
isNegative := compact&0x00800000 != 0
|
||||||
|
exponent := uint(compact >> 24)
|
||||||
|
|
||||||
|
// Since the base for the exponent is 256, the exponent can be treated
|
||||||
|
// as the number of bytes to represent the full 256-bit number. So,
|
||||||
|
// treat the exponent as the number of bytes and shift the mantissa
|
||||||
|
// right or left accordingly. This is equivalent to:
|
||||||
|
// N = mantissa * 256^(exponent-3)
|
||||||
|
var bn *big.Int
|
||||||
|
if exponent <= 3 {
|
||||||
|
mantissa >>= 8 * (3 - exponent)
|
||||||
|
bn = big.NewInt(int64(mantissa))
|
||||||
|
} else {
|
||||||
|
bn = big.NewInt(int64(mantissa))
|
||||||
|
bn.Lsh(bn, 8*(exponent-3))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make it negative if the sign bit is set.
|
||||||
|
if isNegative {
|
||||||
|
bn = bn.Neg(bn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bn
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigToCompact converts a whole number N to a compact representation using
|
||||||
|
// an unsigned 32-bit number. The compact representation only provides 23 bits
|
||||||
|
// of precision, so values larger than (2^23 - 1) only encode the most
|
||||||
|
// significant digits of the number. See CompactToBig for details.
|
||||||
|
func BigToCompact(n *big.Int) uint32 {
|
||||||
|
// No need to do any work if it's zero.
|
||||||
|
if n.Sign() == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the base for the exponent is 256, the exponent can be treated
|
||||||
|
// as the number of bytes. So, shift the number right or left
|
||||||
|
// accordingly. This is equivalent to:
|
||||||
|
// mantissa = mantissa / 256^(exponent-3)
|
||||||
|
var mantissa uint32
|
||||||
|
exponent := uint(len(n.Bytes()))
|
||||||
|
if exponent <= 3 {
|
||||||
|
mantissa = uint32(n.Bits()[0])
|
||||||
|
mantissa <<= 8 * (3 - exponent)
|
||||||
|
} else {
|
||||||
|
// Use a copy to avoid modifying the caller's original number.
|
||||||
|
tn := new(big.Int).Set(n)
|
||||||
|
mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the mantissa already has the sign bit set, the number is too
|
||||||
|
// large to fit into the available 23-bits, so divide the number by 256
|
||||||
|
// and increment the exponent accordingly.
|
||||||
|
if mantissa&0x00800000 != 0 {
|
||||||
|
mantissa >>= 8
|
||||||
|
exponent++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit
|
||||||
|
// int and return it.
|
||||||
|
compact := uint32(exponent<<24) | mantissa
|
||||||
|
if n.Sign() < 0 {
|
||||||
|
compact |= 0x00800000
|
||||||
|
}
|
||||||
|
return compact
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalcWork calculates a work value from difficulty bits. Bitcoin increases
|
||||||
|
// the difficulty for generating a block by decreasing the value which the
|
||||||
|
// generated hash must be less than. This difficulty target is stored in each
|
||||||
|
// block header using a compact representation as described in the documentation
|
||||||
|
// for CompactToBig. The main chain is selected by choosing the chain that has
|
||||||
|
// the most proof of work (highest difficulty). Since a lower target difficulty
|
||||||
|
// value equates to higher actual difficulty, the work value which will be
|
||||||
|
// accumulated must be the inverse of the difficulty. Also, in order to avoid
|
||||||
|
// potential division by zero and really small floating point numbers, the
|
||||||
|
// result adds 1 to the denominator and multiplies the numerator by 2^256.
|
||||||
|
func CalcWork(bits uint32) *big.Int {
|
||||||
|
// Return a work value of zero if the passed difficulty bits represent
|
||||||
|
// a negative number. Note this should not happen in practice with valid
|
||||||
|
// blocks, but an invalid block could trigger it.
|
||||||
|
difficultyNum := CompactToBig(bits)
|
||||||
|
if difficultyNum.Sign() <= 0 {
|
||||||
|
return big.NewInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// (1 << 256) / (difficultyNum + 1)
|
||||||
|
denominator := new(big.Int).Add(difficultyNum, bigOne)
|
||||||
|
return new(big.Int).Div(oneLsh256, denominator)
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package blockchain
|
package workmath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
Loading…
Add table
Reference in a new issue