[NOD-180] Add validation of utxo commitments (#310)

* [NOD-172] Port EMCH from bchd

* [NOD-172] Fix hdkeychain.TestErrors and add btcec.TestRecoverCompact

* [NOD-172] Make ECMH immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add TestMultiset_NewMultisetFromDataSlice and fix Point to be immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add test for checking that the Union of a multiset and its inverse is zero

* [NOD-179] Add ECMH Point to all UTXO-structs

* [NOD-179] Fix utxo set tests

* [NOD-179] Fix mempool tests

* [NOD-179] Remove RemoveTxOuts

* [NOD-179] Move serializeBlockUTXODiffData to the top of the file

* [NOD-179] Fix serializeBlockUTXODiffData comment format

* [NOD-179] Fix AddTx comment and name return values

* [NOD-180] Validate utxo commitments

* [NOD-179] Fix TestAcceptingBlock and TestConfirmations to not use the block hash as phantom break even

* [NOD-180] Fix typo

* [NOD-180] move most of the logic in calcUTXOCommitment to UTXOSet.WithTransactions

* [NOD-180] Optionally return error when a transaction in WithTransactions is double spent

* [NOD-180] Rename allowDoubleSpends to ignoreDoubleSpends
This commit is contained in:
Ori Newman 2019-05-28 11:33:11 +03:00 committed by Svarog
parent aa51b5f071
commit 7069d173c6
15 changed files with 191 additions and 535 deletions

View File

@ -898,6 +898,14 @@ func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
calculatedMultisetHash := utxo.Multiset().Hash()
if !calculatedMultisetHash.IsEqual(node.utxoCommitment) {
str := fmt.Sprintf("block UTXO commitment is invalid - block "+
"header indicates %s, but calculated value is %s",
node.utxoCommitment, calculatedMultisetHash)
return nil, nil, nil, ruleError(ErrBadUTXOCommitment, str)
}
return utxo, txsAcceptanceData, feeData, nil return utxo, txsAcceptanceData, feeData, nil
} }

View File

@ -7,7 +7,6 @@ package blockdag
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -25,7 +24,6 @@ import (
"github.com/daglabs/btcd/util" "github.com/daglabs/btcd/util"
"github.com/daglabs/btcd/util/daghash" "github.com/daglabs/btcd/util/daghash"
"github.com/daglabs/btcd/util/subnetworkid" "github.com/daglabs/btcd/util/subnetworkid"
"github.com/daglabs/btcd/util/txsort"
"github.com/daglabs/btcd/wire" "github.com/daglabs/btcd/wire"
) )
@ -191,7 +189,7 @@ func TestHaveBlock(t *testing.T) {
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true}, {hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
// Block 3b should be present (as a second child of Block 2). // Block 3b should be present (as a second child of Block 2).
{hash: "3f2ded16b7115e69a48cee5f4be743ff23ad8d41da16d059c38cc83d14459863", want: true}, {hash: "4bb2e2f55fabd67e217126dbc41e7101d0d6058800368c428cd6e397c111ee47", want: true},
// Block 100000 should be present (as an orphan). // Block 100000 should be present (as an orphan).
{hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true}, {hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true},
@ -803,7 +801,7 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
} }
defer teardownFunc() defer teardownFunc()
// Since we're not dealing with the real block chain, set the block reward // Since we're not dealing with the real block DAG, set the block reward
// maturity to 1. // maturity to 1.
dag.TestSetBlockRewardMaturity(1) dag.TestSetBlockRewardMaturity(1)
@ -819,7 +817,6 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
"is an orphan\n", i) "is an orphan\n", i)
} }
if err != nil { if err != nil {
fmt.Printf("ERROR %v\n", err)
break break
} }
} }
@ -875,390 +872,12 @@ func TestNew(t *testing.T) {
} }
} }
func TestValidateFeeTransaction(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("TestValidateFeeTransaction", Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
extraNonce := int64(0)
createCoinbase := func(pkScript []byte) *wire.MsgTx {
extraNonce++
cbTx, err := createCoinbaseTxForTest(dag.Height()+1, 2, extraNonce, &params)
if err != nil {
t.Fatalf("createCoinbaseTxForTest: %v", err)
}
if pkScript != nil {
cbTx.TxOut[0].PkScript = pkScript
}
return cbTx
}
var flags BehaviorFlags
flags |= BFFastAdd | BFNoPoWCheck
buildBlock := func(blockName string, parentHashes []*daghash.Hash, transactions []*wire.MsgTx, expectedErrorCode ErrorCode) *wire.MsgBlock {
utilTxs := make([]*util.Tx, len(transactions))
for i, tx := range transactions {
utilTxs[i] = util.NewTx(tx)
}
newVirtual, err := GetVirtualFromParentsForTest(dag, parentHashes)
if err != nil {
t.Fatalf("block %v: unexpected error when setting virtual for test: %v", blockName, err)
}
oldVirtual := SetVirtualForTest(dag, newVirtual)
acceptedIDMerkleRoot, err := dag.NextAcceptedIDMerkleRoot()
if err != nil {
t.Fatalf("block %v: unexpected error when getting next acceptIDMerkleRoot: %v", blockName, err)
}
SetVirtualForTest(dag, oldVirtual)
daghash.Sort(parentHashes)
msgBlock := &wire.MsgBlock{
Header: wire.BlockHeader{
Bits: dag.genesis.Header().Bits,
ParentHashes: parentHashes,
HashMerkleRoot: BuildHashMerkleTreeStore(utilTxs).Root(),
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
UTXOCommitment: &daghash.ZeroHash,
},
Transactions: transactions,
}
block := util.NewBlock(msgBlock)
isOrphan, err := dag.ProcessBlock(block, flags)
if expectedErrorCode != 0 {
checkResult := checkRuleError(err, RuleError{
ErrorCode: expectedErrorCode,
})
if checkResult != nil {
t.Errorf("block %v: unexpected error code: %v", blockName, checkResult)
}
} else {
if err != nil {
t.Fatalf("block %v: unexpected error: %v", blockName, err)
}
if isOrphan {
t.Errorf("block %v unexpectely got orphaned", blockName)
}
}
return msgBlock
}
cb1 := createCoinbase(nil)
blockWithExtraFeeTxTransactions := []*wire.MsgTx{
cb1,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*dag.genesis.hash),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
{ // Extra Fee Transaction
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*dag.genesis.hash),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
buildBlock("blockWithExtraFeeTx", []*daghash.Hash{dag.genesis.hash}, blockWithExtraFeeTxTransactions, ErrMultipleFeeTransactions)
block1Txs := []*wire.MsgTx{
cb1,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*dag.genesis.hash),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block1 := buildBlock("block1", []*daghash.Hash{dag.genesis.hash}, block1Txs, 0)
cb1A := createCoinbase(nil)
block1ATxs := []*wire.MsgTx{
cb1A,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*dag.genesis.hash),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block1A := buildBlock("block1A", []*daghash.Hash{dag.genesis.hash}, block1ATxs, 0)
block1AChildCbPkScript, err := payToPubKeyHashScript((&[20]byte{0x1A, 0xC0})[:])
if err != nil {
t.Fatalf("payToPubKeyHashScript: %v", err)
}
cb1AChild := createCoinbase(block1AChildCbPkScript)
block1AChildTxs := []*wire.MsgTx{
cb1AChild,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block1A.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
{
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: *cb1A.TxID(),
Index: 0,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
TxOut: []*wire.TxOut{
{
PkScript: OpTrueScript,
Value: 1,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block1AChild := buildBlock("block1AChild", []*daghash.Hash{block1A.BlockHash()}, block1AChildTxs, 0)
cb2 := createCoinbase(nil)
block2Txs := []*wire.MsgTx{
cb2,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block1.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block2 := buildBlock("block2", []*daghash.Hash{block1.BlockHash()}, block2Txs, 0)
cb3 := createCoinbase(nil)
block3Txs := []*wire.MsgTx{
cb3,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block2.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block3 := buildBlock("block3", []*daghash.Hash{block2.BlockHash()}, block3Txs, 0)
block4CbPkScript, err := payToPubKeyHashScript((&[20]byte{0x40})[:])
if err != nil {
t.Fatalf("payToPubKeyHashScript: %v", err)
}
cb4 := createCoinbase(block4CbPkScript)
block4Txs := []*wire.MsgTx{
cb4,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block3.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
{
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: *cb3.TxID(),
Index: 0,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
TxOut: []*wire.TxOut{
{
PkScript: OpTrueScript,
Value: 1,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block4 := buildBlock("block4", []*daghash.Hash{block3.BlockHash()}, block4Txs, 0)
block4ACbPkScript, err := payToPubKeyHashScript((&[20]byte{0x4A})[:])
if err != nil {
t.Fatalf("payToPubKeyHashScript: %v", err)
}
cb4A := createCoinbase(block4ACbPkScript)
block4ATxs := []*wire.MsgTx{
cb4A,
{ // Fee Transaction
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block3.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
{
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
TxID: *cb3.TxID(),
Index: 1,
},
Sequence: wire.MaxTxInSequenceNum,
},
},
TxOut: []*wire.TxOut{
{
PkScript: OpTrueScript,
Value: 1,
},
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
}
block4A := buildBlock("block4A", []*daghash.Hash{block3.BlockHash()}, block4ATxs, 0)
cb5 := createCoinbase(nil)
feeInOuts := map[daghash.Hash]*struct {
txIn *wire.TxIn
txOut *wire.TxOut
}{
*block4.BlockHash(): {
txIn: &wire.TxIn{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block4.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
txOut: &wire.TxOut{
PkScript: block4CbPkScript,
Value: cb3.TxOut[0].Value - 1,
},
},
*block4A.BlockHash(): {
txIn: &wire.TxIn{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block4A.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
},
txOut: &wire.TxOut{
PkScript: block4ACbPkScript,
Value: cb3.TxOut[1].Value - 1,
},
},
}
txIns := []*wire.TxIn{}
txOuts := []*wire.TxOut{}
for hash := range feeInOuts {
txIns = append(txIns, feeInOuts[hash].txIn)
txOuts = append(txOuts, feeInOuts[hash].txOut)
}
block5FeeTx := wire.NewNativeMsgTx(1, txIns, txOuts)
sortedBlock5FeeTx := txsort.Sort(block5FeeTx)
block5Txs := []*wire.MsgTx{cb5, sortedBlock5FeeTx}
block5ParentHashes := []*daghash.Hash{block4.BlockHash(), block4A.BlockHash()}
buildBlock("block5", block5ParentHashes, block5Txs, 0)
sortedBlock5FeeTx.TxIn[0], sortedBlock5FeeTx.TxIn[1] = sortedBlock5FeeTx.TxIn[1], sortedBlock5FeeTx.TxIn[0]
buildBlock("block5WrongOrder", block5ParentHashes, block5Txs, ErrBadFeeTransaction)
block5FeeTxWith1Achild := block5FeeTx.Copy()
block5FeeTxWith1Achild.AddTxIn(&wire.TxIn{
PreviousOutPoint: wire.OutPoint{
TxID: daghash.TxID(*block1AChild.BlockHash()),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
})
block5FeeTxWith1Achild.AddTxOut(&wire.TxOut{
PkScript: block1AChildCbPkScript,
Value: cb1AChild.TxOut[0].Value - 1,
})
sortedBlock5FeeTxWith1Achild := txsort.Sort(block5FeeTxWith1Achild)
block5Txs[1] = sortedBlock5FeeTxWith1Achild
buildBlock("block5WithRedBlockFees", block5ParentHashes, block5Txs, ErrBadFeeTransaction)
block5FeeTxWithWrongFees := block5FeeTx.Copy()
block5FeeTxWithWrongFees.TxOut[0].Value--
sortedBlock5FeeTxWithWrongFees := txsort.Sort(block5FeeTxWithWrongFees)
block5Txs[1] = sortedBlock5FeeTxWithWrongFees
buildBlock("block5WithRedBlockFees", block5ParentHashes, block5Txs, ErrBadFeeTransaction)
}
func TestConfirmations(t *testing.T) { func TestConfirmations(t *testing.T) {
// Create a new database and DAG instance to run tests against. // Create a new database and DAG instance to run tests against.
params := dagconfig.SimNetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("TestBlockCount", Config{ dag, teardownFunc, err := DAGSetup("TestBlockCount", Config{
DAGParams: &dagconfig.SimNetParams, DAGParams: &params,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err) t.Fatalf("Failed to setup DAG instance: %v", err)
@ -1275,29 +894,17 @@ func TestConfirmations(t *testing.T) {
t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. Want: 1, got: %d", genesisConfirmations) t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. Want: 1, got: %d", genesisConfirmations)
} }
processBlocks := func(blocks []*util.Block) {
for _, block := range blocks {
isOrphan, err := dag.ProcessBlock(block, BFNone)
if err != nil {
t.Fatalf("ProcessBlock fail on block %s: %v\n", block.Hash(), err)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block %s is an orphan\n", block.Hash())
}
}
}
// Add a chain of blocks // Add a chain of blocks
loadedBlocks, err := loadBlocks("blk_0_to_4.dat") chainBlocks := make([]*blockNode, 5)
if err != nil { chainBlocks[0] = dag.genesis
t.Fatalf("Error loading file: %v\n", err) buildNode := buildNodeGenerator(dag.dagParams.K, true)
for i := uint32(1); i < 5; i++ {
chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1]))
dag.virtual.AddTip(chainBlocks[i])
} }
chainBlocks := loadedBlocks[1:]
processBlocks(chainBlocks)
// Make sure that each one of the chain blocks has the expected confirmations number // Make sure that each one of the chain blocks has the expected confirmations number
for i, block := range chainBlocks { for i, node := range chainBlocks {
node := dag.index.LookupNode(block.Hash())
confirmations, err := dag.confirmations(node) confirmations, err := dag.confirmations(node)
if err != nil { if err != nil {
t.Fatalf("TestConfirmations: confirmations for node 1 unexpectedly failed: %s", err) t.Fatalf("TestConfirmations: confirmations for node 1 unexpectedly failed: %s", err)
@ -1310,48 +917,55 @@ func TestConfirmations(t *testing.T) {
} }
} }
// Add a branching block branchingBlocks := make([]*blockNode, 2)
loadedBlocks, err = loadBlocks("blk_3B.dat") // Add two branching blocks
if err != nil { branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1]))
t.Fatalf("Error loading file: %v\n", err) dag.virtual.AddTip(branchingBlocks[0])
} branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0]))
processBlocks(loadedBlocks) dag.virtual.AddTip(branchingBlocks[1])
// Check that the genesis has a confirmations number == blockCount // Check that the genesis has a confirmations number == len(chainBlocks)
genesisConfirmations, err = dag.confirmations(dag.genesis) genesisConfirmations, err = dag.confirmations(dag.genesis)
if err != nil { if err != nil {
t.Fatalf("TestConfirmations: confirmations for genesis block unexpectedly failed: %s", err) t.Fatalf("TestConfirmations: confirmations for genesis block unexpectedly failed: %s", err)
} }
expectedGenesisConfirmations := dag.blockCount expectedGenesisConfirmations := uint64(len(chainBlocks))
if genesisConfirmations != expectedGenesisConfirmations { if genesisConfirmations != expectedGenesisConfirmations {
t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. "+ t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. "+
"Want: %d, got: %d", expectedGenesisConfirmations, genesisConfirmations) "Want: %d, got: %d", expectedGenesisConfirmations, genesisConfirmations)
} }
// Check that each of the tips had a confirmation number of 1. // Check that each of the blue tips has a confirmation number of 1, and each of the red tips has 0 confirmations.
tips := dag.virtual.tips() tips := dag.virtual.tips()
for _, tip := range tips { for _, tip := range tips {
tipConfirmations, err := dag.confirmations(tip) tipConfirmations, err := dag.confirmations(tip)
if err != nil { if err != nil {
t.Fatalf("TestConfirmations: confirmations for tip unexpectedly failed: %s", err) t.Fatalf("TestConfirmations: confirmations for tip unexpectedly failed: %s", err)
} }
if tipConfirmations != 1 { acceptingBlock, err := dag.acceptingBlock(tip)
if err != nil {
t.Fatalf("TestConfirmations: dag.acceptingBlock unexpectedly failed: %s", err)
}
expectedConfirmations := uint64(1)
if acceptingBlock == nil {
expectedConfirmations = 0
}
if tipConfirmations != expectedConfirmations {
t.Fatalf("TestConfirmations: unexpected confirmations for tip. "+ t.Fatalf("TestConfirmations: unexpected confirmations for tip. "+
"Want: 1, got: %d", tipConfirmations) "Want: %d, got: %d", expectedConfirmations, tipConfirmations)
} }
} }
// Generate K blocks to force the "main" chain to become red // Generate 100 blocks to force the "main" chain to become red
nodeGenerator := buildNodeGenerator(dag.dagParams.K, false) branchingChainTip := branchingBlocks[1]
branchingChainTip := dag.index.LookupNode(loadedBlocks[0].Hash()) for i := uint32(0); i < 100; i++ {
for i := uint32(0); i < dag.dagParams.K; i++ { nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip))
nextBranchingChainTip := nodeGenerator(setFromSlice(branchingChainTip))
dag.virtual.AddTip(nextBranchingChainTip) dag.virtual.AddTip(nextBranchingChainTip)
branchingChainTip = nextBranchingChainTip branchingChainTip = nextBranchingChainTip
} }
// Make sure that a red block has confirmation number = 0 // Make sure that a red block has confirmation number = 0
redChainBlock := dag.index.LookupNode(chainBlocks[3].Hash()) redChainBlock := chainBlocks[3]
redChainBlockConfirmations, err := dag.confirmations(redChainBlock) redChainBlockConfirmations, err := dag.confirmations(redChainBlock)
if err != nil { if err != nil {
t.Fatalf("TestConfirmations: confirmations for red chain block unexpectedly failed: %s", err) t.Fatalf("TestConfirmations: confirmations for red chain block unexpectedly failed: %s", err)
@ -1362,7 +976,7 @@ func TestConfirmations(t *testing.T) {
} }
// Make sure that the red tip has confirmation number = 0 // Make sure that the red tip has confirmation number = 0
redChainTip := dag.index.LookupNode(chainBlocks[len(chainBlocks)-1].Hash()) redChainTip := chainBlocks[len(chainBlocks)-1]
redChainTipConfirmations, err := dag.confirmations(redChainTip) redChainTipConfirmations, err := dag.confirmations(redChainTip)
if err != nil { if err != nil {
t.Fatalf("TestConfirmations: confirmations for red chain tip unexpectedly failed: %s", err) t.Fatalf("TestConfirmations: confirmations for red chain tip unexpectedly failed: %s", err)
@ -1375,8 +989,10 @@ func TestConfirmations(t *testing.T) {
func TestAcceptingBlock(t *testing.T) { func TestAcceptingBlock(t *testing.T) {
// Create a new database and DAG instance to run tests against. // Create a new database and DAG instance to run tests against.
params := dagconfig.SimNetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{ dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{
DAGParams: &dagconfig.SimNetParams, DAGParams: &params,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err) t.Fatalf("Failed to setup DAG instance: %v", err)
@ -1394,34 +1010,20 @@ func TestAcceptingBlock(t *testing.T) {
"Want: virtual, got: %s", genesisAcceptingBlock.hash) "Want: virtual, got: %s", genesisAcceptingBlock.hash)
} }
processBlocks := func(blocks []*util.Block) { chainBlocks := make([]*blockNode, 5)
for _, block := range blocks { chainBlocks[0] = dag.genesis
isOrphan, err := dag.ProcessBlock(block, BFNone) buildNode := buildNodeGenerator(dag.dagParams.K, true)
if err != nil { for i := uint32(1); i <= 4; i++ {
t.Fatalf("ProcessBlock fail on block %s: %v\n", block.Hash(), err) chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1]))
dag.virtual.AddTip(chainBlocks[i])
} }
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block %s is an orphan\n", block.Hash())
}
}
}
// Add a chain of blocks
chainBlocks, err := loadBlocks("blk_0_to_4.dat")
if err != nil {
t.Fatalf("Error loading file: %v\n", err)
}
processBlocks(chainBlocks[1:])
// Make sure that each chain block (including the genesis) is accepted by its child // Make sure that each chain block (including the genesis) is accepted by its child
for i, chainBlock := range chainBlocks[:1] { for i, chainBlockNode := range chainBlocks[:len(chainBlocks)-1] {
expectedAcceptingBlock := chainBlocks[i+1] expectedAcceptingBlockNode := chainBlocks[i+1]
expectedAcceptingBlockNode := dag.index.LookupNode(expectedAcceptingBlock.Hash())
chainBlockNode := dag.index.LookupNode(chainBlock.Hash())
chainAcceptingBlockNode, err := dag.acceptingBlock(chainBlockNode) chainAcceptingBlockNode, err := dag.acceptingBlock(chainBlockNode)
if err != nil { if err != nil {
t.Fatalf("TestAcceptingBlock: acceptingBlock for chain block unexpectedly failed: %s", err) t.Fatalf("TestAcceptingBlock: acceptingBlock for chain block %d unexpectedly failed: %s", i, err)
} }
if expectedAcceptingBlockNode != chainAcceptingBlockNode { if expectedAcceptingBlockNode != chainAcceptingBlockNode {
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for chain block. "+ t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for chain block. "+
@ -1429,16 +1031,16 @@ func TestAcceptingBlock(t *testing.T) {
} }
} }
// Add a branching block branchingBlocks := make([]*blockNode, 2)
branchingBlock, err := loadBlocks("blk_3B.dat") // Add two branching blocks
if err != nil { branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1]))
t.Fatalf("Error loading file: %v\n", err) dag.virtual.AddTip(branchingBlocks[0])
} branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0]))
processBlocks(branchingBlock) dag.virtual.AddTip(branchingBlocks[1])
// Make sure that the accepting block of the parent of the branching block didn't change // Make sure that the accepting block of the parent of the branching block didn't change
expectedAcceptingBlock := dag.index.LookupNode(chainBlocks[3].Hash()) expectedAcceptingBlock := chainBlocks[2]
intersectionBlock := dag.index.LookupNode(chainBlocks[2].Hash()) intersectionBlock := chainBlocks[1]
intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock) intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock)
if err != nil { if err != nil {
t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err) t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err)
@ -1448,9 +1050,8 @@ func TestAcceptingBlock(t *testing.T) {
"Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash) "Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash)
} }
// Make sure that the accepting block of all the tips in the virtual block // Make sure that the accepting block of the chain tips is the virtual block
for _, tip := range dag.virtual.tips() { tipAcceptingBlock, err := dag.acceptingBlock(chainBlocks[len(chainBlocks)-1])
tipAcceptingBlock, err := dag.acceptingBlock(tip)
if err != nil { if err != nil {
t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err) t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err)
} }
@ -1458,19 +1059,17 @@ func TestAcceptingBlock(t *testing.T) {
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+ t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+
"Want: Virtual, got: %s", tipAcceptingBlock.hash) "Want: Virtual, got: %s", tipAcceptingBlock.hash)
} }
}
// Generate K blocks to force the "main" chain to become red // Generate 100 blocks to force the "main" chain to become red
nodeGenerator := buildNodeGenerator(dag.dagParams.K, false) branchingChainTip := branchingBlocks[1]
branchingChainTip := dag.index.LookupNode(branchingBlock[0].Hash()) for i := uint32(0); i < 100; i++ {
for i := uint32(0); i < dag.dagParams.K; i++ { nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip))
nextBranchingChainTip := nodeGenerator(setFromSlice(branchingChainTip))
dag.virtual.AddTip(nextBranchingChainTip) dag.virtual.AddTip(nextBranchingChainTip)
branchingChainTip = nextBranchingChainTip branchingChainTip = nextBranchingChainTip
} }
// Make sure that a red block returns nil // Make sure that a red block returns nil
redChainBlock := dag.index.LookupNode(chainBlocks[3].Hash()) redChainBlock := chainBlocks[2]
redChainBlockAcceptionBlock, err := dag.acceptingBlock(redChainBlock) redChainBlockAcceptionBlock, err := dag.acceptingBlock(redChainBlock)
if err != nil { if err != nil {
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain block unexpectedly failed: %s", err) t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain block unexpectedly failed: %s", err)
@ -1481,7 +1080,7 @@ func TestAcceptingBlock(t *testing.T) {
} }
// Make sure that a red tip returns nil // Make sure that a red tip returns nil
redChainTip := dag.index.LookupNode(chainBlocks[len(chainBlocks)-1].Hash()) redChainTip := chainBlocks[len(chainBlocks)-1]
redChainTipAcceptingBlock, err := dag.acceptingBlock(redChainTip) redChainTipAcceptingBlock, err := dag.acceptingBlock(redChainTip)
if err != nil { if err != nil {
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain tip unexpectedly failed: %s", err) t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain tip unexpectedly failed: %s", err)
@ -1491,16 +1090,3 @@ func TestAcceptingBlock(t *testing.T) {
"Want: nil, got: %s", redChainTipAcceptingBlock.hash) "Want: nil, got: %s", redChainTipAcceptingBlock.hash)
} }
} }
// payToPubKeyHashScript creates a new script to pay a transaction
// output to a 20-byte pubkey hash. It is expected that the input is a valid
// hash.
func payToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
return txscript.NewScriptBuilder().
AddOp(txscript.OpDup).
AddOp(txscript.OpHash160).
AddData(pubKeyHash).
AddOp(txscript.OpEqualVerify).
AddOp(txscript.OpCheckSig).
Script()
}

View File

@ -84,6 +84,10 @@ const (
// the expected value. // the expected value.
ErrBadMerkleRoot ErrBadMerkleRoot
// ErrBadUTXOCommitment indicates the calculated UTXO commitment does not match
// the expected value.
ErrBadUTXOCommitment
// ErrBadCheckpoint indicates a block that is expected to be at a // ErrBadCheckpoint indicates a block that is expected to be at a
// checkpoint height does not match the expected one. // checkpoint height does not match the expected one.
ErrBadCheckpoint ErrBadCheckpoint

View File

@ -67,5 +67,5 @@ func ExampleBlockDAG_ProcessBlock() {
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
// Output: // Output:
// Failed to process block: already have block 63bbcfdd699ffd8cb19878564b14d3af8ba4d7ee4d1dd54925a7c21d5b5b5fdc // Failed to process block: already have block 09c081c17dbfb421fc015aa77ba17e278c1a1cf9a7311a2042ffb8932d2ff365
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -390,6 +390,7 @@ type UTXOSet interface {
clone() UTXOSet clone() UTXOSet
Get(outPoint wire.OutPoint) (*UTXOEntry, bool) Get(outPoint wire.OutPoint) (*UTXOEntry, bool)
Multiset() *btcec.Multiset Multiset() *btcec.Multiset
WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error)
} }
// diffFromTx is a common implementation for diffFromTx, that works // diffFromTx is a common implementation for diffFromTx, that works
@ -550,6 +551,21 @@ func (fus *FullUTXOSet) removeAndUpdateMultiset(outPoint wire.OutPoint) error {
return nil return nil
} }
// WithTransactions returns a new UTXO Set with the added transactions
func (fus *FullUTXOSet) WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error) {
diffSet := NewDiffUTXOSet(fus, NewUTXODiff())
for _, tx := range transactions {
isAccepted, err := diffSet.AddTx(tx, blockHeight)
if err != nil {
return nil, err
}
if !ignoreDoubleSpends && !isAccepted {
return nil, fmt.Errorf("Transaction %s is not valid with the current UTXO set", tx.TxID())
}
}
return UTXOSet(diffSet), nil
}
// DiffUTXOSet represents a utxoSet with a base fullUTXOSet and a UTXODiff // DiffUTXOSet represents a utxoSet with a base fullUTXOSet and a UTXODiff
type DiffUTXOSet struct { type DiffUTXOSet struct {
base *FullUTXOSet base *FullUTXOSet
@ -698,6 +714,21 @@ func (dus *DiffUTXOSet) Multiset() *btcec.Multiset {
return dus.base.UTXOMultiset.Union(dus.UTXODiff.diffMultiset) return dus.base.UTXOMultiset.Union(dus.UTXODiff.diffMultiset)
} }
// WithTransactions returns a new UTXO Set with the added transactions
func (dus *DiffUTXOSet) WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error) {
diffSet := NewDiffUTXOSet(dus.base, dus.UTXODiff.clone())
for _, tx := range transactions {
isAccepted, err := diffSet.AddTx(tx, blockHeight)
if err != nil {
return nil, err
}
if !ignoreDoubleSpends && !isAccepted {
return nil, fmt.Errorf("Transaction %s is not valid with the current UTXO set", tx.TxID())
}
}
return UTXOSet(diffSet), nil
}
func addUTXOToMultiset(ms *btcec.Multiset, entry *UTXOEntry, outPoint *wire.OutPoint) (*btcec.Multiset, error) { func addUTXOToMultiset(ms *btcec.Multiset, entry *UTXOEntry, outPoint *wire.OutPoint) (*btcec.Multiset, error) {
w := &bytes.Buffer{} w := &bytes.Buffer{}
err := serializeUTXO(w, entry, outPoint) err := serializeUTXO(w, entry, outPoint)

View File

@ -109,13 +109,14 @@ func (ms *Multiset) Subtract(otherMultiset *Multiset) *Multiset {
// Hash serializes and returns the hash of the multiset. The hash of an empty // Hash serializes and returns the hash of the multiset. The hash of an empty
// set is the 32 byte value of zero. The hash of a non-empty multiset is the // set is the 32 byte value of zero. The hash of a non-empty multiset is the
// sha256 hash of the 32 byte x value concatenated with the 32 byte y value. // sha256 hash of the 32 byte x value concatenated with the 32 byte y value.
func (ms *Multiset) Hash() daghash.Hash { func (ms *Multiset) Hash() *daghash.Hash {
if ms.x.Sign() == 0 && ms.y.Sign() == 0 { if ms.x.Sign() == 0 && ms.y.Sign() == 0 {
return daghash.Hash{} return &daghash.Hash{}
} }
hash := sha256.Sum256(append(ms.x.Bytes(), ms.y.Bytes()...)) hash := sha256.Sum256(append(ms.x.Bytes(), ms.y.Bytes()...))
return daghash.Hash(hash) castHash := daghash.Hash(hash)
return &castHash
} }
// Point returns a copy of the x and y coordinates of the current multiset state. // Point returns a copy of the x and y coordinates of the current multiset state.

View File

@ -41,10 +41,10 @@ var genesisCoinbaseTx = wire.NewNativeMsgTx(1, genesisTxIns, genesisTxOuts)
// genesisHash is the hash of the first block in the block chain for the main // genesisHash is the hash of the first block in the block chain for the main
// network (genesis block). // network (genesis block).
var genesisHash = daghash.Hash([daghash.HashSize]byte{ var genesisHash = daghash.Hash([daghash.HashSize]byte{
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, 0x65, 0xf3, 0x2f, 0x2d, 0x93, 0xb8, 0xff, 0x42,
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b, 0x20, 0x1a, 0x31, 0xa7, 0xf9, 0x1c, 0x1a, 0x8c,
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1, 0x27, 0x7e, 0xa1, 0x7b, 0xa7, 0x5a, 0x01, 0xfc,
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63, 0x21, 0xb4, 0xbf, 0x7d, 0xc1, 0x81, 0xc0, 0x09,
}) })
// genesisMerkleRoot is the hash of the first transaction in the genesis block // genesisMerkleRoot is the hash of the first transaction in the genesis block
@ -64,10 +64,15 @@ var genesisBlock = wire.MsgBlock{
ParentHashes: []*daghash.Hash{}, ParentHashes: []*daghash.Hash{},
HashMerkleRoot: &genesisMerkleRoot, HashMerkleRoot: &genesisMerkleRoot,
AcceptedIDMerkleRoot: &daghash.Hash{}, AcceptedIDMerkleRoot: &daghash.Hash{},
UTXOCommitment: &daghash.Hash{}, UTXOCommitment: &daghash.Hash{
0x51, 0x9f, 0x81, 0xbb, 0x93, 0xfd, 0x72, 0x9d,
0x9d, 0xe0, 0x60, 0x80, 0xfa, 0x01, 0xe6, 0x34,
0x93, 0x22, 0xed, 0x67, 0x69, 0x14, 0x52, 0xca,
0x95, 0xd1, 0x9b, 0x77, 0xdb, 0xb8, 0x12, 0x80,
},
Timestamp: time.Unix(0x5cdac4b0, 0), Timestamp: time.Unix(0x5cdac4b0, 0),
Bits: 0x207fffff, Bits: 0x207fffff,
Nonce: 0x0, Nonce: 0x3,
}, },
Transactions: []*wire.MsgTx{genesisCoinbaseTx}, Transactions: []*wire.MsgTx{genesisCoinbaseTx},
} }

View File

@ -121,35 +121,35 @@ func TestSimNetGenesisBlock(t *testing.T) {
// genesisBlockBytes are the wire encoded bytes for the genesis block of the // genesisBlockBytes are the wire encoded bytes for the genesis block of the
// main network as of protocol version 60002. // main network as of protocol version 60002.
var genesisBlockBytes = []byte{ var genesisBlockBytes = []byte{
0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b, /* |........| */ 0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b,
0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae, /* |.vW.}...| */ 0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae,
0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb, /* |..".....| */ 0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb,
0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d, /* |.......M| */ 0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d,
0x42, 0x01, 0xff, 0xed, 0x9d, 0x00, 0x00, 0x00, /* |B.......| */ 0x42, 0x01, 0xff, 0xed, 0x9d, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x9f, 0x81,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0xbb, 0x93, 0xfd, 0x72, 0x9d, 0x9d, 0xe0, 0x60,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x80, 0xfa, 0x01, 0xe6, 0x34, 0x93, 0x22, 0xed,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x67, 0x69, 0x14, 0x52, 0xca, 0x95, 0xd1, 0x9b,
0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xc4, 0xda, /* |........| */ 0x77, 0xdb, 0xb8, 0x12, 0x80, 0xb0, 0xc4, 0xda,
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, /* |\.......| */ 0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* | .......| */ 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, /* |........| */ 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f, /* |......./| */ 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f,
0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, /* |P2SH/btc| */ 0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63,
0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* |d/......| */ 0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01, /* |......*.| */ 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01,
0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00, /* |....Q...| */ 0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, /* |.| */ 0x00,
} }
// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of // regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of

View File

@ -660,18 +660,22 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
return nil, err return nil, err
} }
var msgBlock wire.MsgBlock var msgBlock wire.MsgBlock
for _, tx := range blockTxns {
msgBlock.AddTransaction(tx.MsgTx())
}
utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, nextBlockHeight)
if err != nil {
return nil, err
}
msgBlock.Header = wire.BlockHeader{ msgBlock.Header = wire.BlockHeader{
Version: nextBlockVersion, Version: nextBlockVersion,
ParentHashes: g.dag.TipHashes(), ParentHashes: g.dag.TipHashes(),
HashMerkleRoot: hashMerkleTree.Root(), HashMerkleRoot: hashMerkleTree.Root(),
AcceptedIDMerkleRoot: acceptedIDMerkleRoot, AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
UTXOCommitment: &daghash.ZeroHash, UTXOCommitment: utxoCommitment,
Timestamp: ts, Timestamp: ts,
Bits: reqDifficulty, Bits: reqDifficulty,
} }
for _, tx := range blockTxns {
msgBlock.AddTransaction(tx.MsgTx())
}
// Finally, perform a full check on the created block against the chain // Finally, perform a full check on the created block against the chain
// consensus rules to ensure it properly connects to the current best // consensus rules to ensure it properly connects to the current best
@ -697,6 +701,14 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
}, nil }, nil
} }
func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx, nextBlockHeight uint64) (*daghash.Hash, error) {
utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, nextBlockHeight, false)
if err != nil {
return nil, err
}
return utxoWithTransactions.Multiset().Hash(), nil
}
// UpdateBlockTime updates the timestamp in the header of the passed block to // UpdateBlockTime updates the timestamp in the header of the passed block to
// the current time while taking into account the median time of the last // the current time while taking into account the median time of the last
// several blocks to ensure the new time is after that time per the chain // several blocks to ensure the new time is after that time per the chain
@ -749,6 +761,13 @@ func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight
hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions()) hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions())
msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root() msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root()
utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, blockHeight)
if err != nil {
return err
}
msgBlock.Header.UTXOCommitment = utxoCommitment
return nil return nil
} }

View File

@ -70,7 +70,10 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
} }
// In order of creating deterministic coinbase tx ids. // In order of creating deterministic coinbase tx ids.
blockTemplateGenerator.UpdateExtraNonce(template.Block, dag.Height()+1, GenerateDeterministicExtraNonceForTest()) err = blockTemplateGenerator.UpdateExtraNonce(template.Block, dag.Height()+1, GenerateDeterministicExtraNonceForTest())
if err != nil {
return nil, err
}
txsToAdd := make([]*wire.MsgTx, 0) txsToAdd := make([]*wire.MsgTx, 0)
for _, tx := range transactions { for _, tx := range transactions {
@ -108,19 +111,18 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
template.Block.Transactions = append(template.Block.Transactions, tx) template.Block.Transactions = append(template.Block.Transactions, tx)
} }
} }
updateMerkleRoots := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0) updateHeaderFields := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0)
if updateMerkleRoots { if updateHeaderFields {
utilTxs := make([]*util.Tx, len(template.Block.Transactions)) utilTxs := make([]*util.Tx, len(template.Block.Transactions))
for i, tx := range template.Block.Transactions { for i, tx := range template.Block.Transactions {
utilTxs[i] = util.NewTx(tx) utilTxs[i] = util.NewTx(tx)
} }
template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root() template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root()
acceptedIDMerkleRoot, err := dag.NextAcceptedIDMerkleRoot() template.Block.Header.UTXOCommitment, err = blockTemplateGenerator.buildUTXOCommitment(template.Block.Transactions, dag.Height()+1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
template.Block.Header.AcceptedIDMerkleRoot = acceptedIDMerkleRoot
} }
return template.Block, nil return template.Block, nil
} }