From 7069d173c69241427e4a0b98103f82e874f4c6d5 Mon Sep 17 00:00:00 2001 From: Ori Newman Date: Tue, 28 May 2019 11:33:11 +0300 Subject: [PATCH] [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 --- blockdag/dag.go | 8 + blockdag/dag_test.go | 556 ++++--------------------------- blockdag/error.go | 4 + blockdag/example_test.go | 2 +- blockdag/testdata/blk_0_to_4.dat | Bin 1948 -> 2036 bytes blockdag/testdata/blk_3A.dat | Bin 457 -> 479 bytes blockdag/testdata/blk_3B.dat | Bin 344 -> 366 bytes blockdag/testdata/blk_3C.dat | Bin 376 -> 398 bytes blockdag/testdata/blk_3D.dat | Bin 502 -> 524 bytes blockdag/utxoset.go | 31 ++ btcec/ecmh.go | 7 +- dagconfig/genesis.go | 21 +- dagconfig/genesis_test.go | 58 ++-- mining/mining.go | 27 +- mining/test_utils.go | 12 +- 15 files changed, 191 insertions(+), 535 deletions(-) diff --git a/blockdag/dag.go b/blockdag/dag.go index e96734bc7..fe4a8a706 100644 --- a/blockdag/dag.go +++ b/blockdag/dag.go @@ -898,6 +898,14 @@ func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx if err != nil { 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 } diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index b1eed3e4c..18d6bfd2e 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -7,7 +7,6 @@ package blockdag import ( "errors" "fmt" - "math" "os" "path/filepath" "reflect" @@ -25,7 +24,6 @@ import ( "github.com/daglabs/btcd/util" "github.com/daglabs/btcd/util/daghash" "github.com/daglabs/btcd/util/subnetworkid" - "github.com/daglabs/btcd/util/txsort" "github.com/daglabs/btcd/wire" ) @@ -191,7 +189,7 @@ func TestHaveBlock(t *testing.T) { {hash: dagconfig.SimNetParams.GenesisHash.String(), want: true}, // 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). {hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true}, @@ -803,7 +801,7 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF } 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. dag.TestSetBlockRewardMaturity(1) @@ -819,7 +817,6 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF "is an orphan\n", i) } if err != nil { - fmt.Printf("ERROR %v\n", err) 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: ¶ms, - }) - 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, ¶ms) - 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) { // Create a new database and DAG instance to run tests against. + params := dagconfig.SimNetParams + params.K = 1 dag, teardownFunc, err := DAGSetup("TestBlockCount", Config{ - DAGParams: &dagconfig.SimNetParams, + DAGParams: ¶ms, }) if err != nil { 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) } - 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 - loadedBlocks, err := loadBlocks("blk_0_to_4.dat") - if err != nil { - t.Fatalf("Error loading file: %v\n", err) + chainBlocks := make([]*blockNode, 5) + chainBlocks[0] = dag.genesis + 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 - for i, block := range chainBlocks { - node := dag.index.LookupNode(block.Hash()) + for i, node := range chainBlocks { confirmations, err := dag.confirmations(node) if err != nil { t.Fatalf("TestConfirmations: confirmations for node 1 unexpectedly failed: %s", err) @@ -1310,48 +917,55 @@ func TestConfirmations(t *testing.T) { } } - // Add a branching block - loadedBlocks, err = loadBlocks("blk_3B.dat") - if err != nil { - t.Fatalf("Error loading file: %v\n", err) - } - processBlocks(loadedBlocks) + branchingBlocks := make([]*blockNode, 2) + // Add two branching blocks + branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1])) + dag.virtual.AddTip(branchingBlocks[0]) + branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0])) + 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) if err != nil { t.Fatalf("TestConfirmations: confirmations for genesis block unexpectedly failed: %s", err) } - expectedGenesisConfirmations := dag.blockCount + expectedGenesisConfirmations := uint64(len(chainBlocks)) if genesisConfirmations != expectedGenesisConfirmations { t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. "+ "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() for _, tip := range tips { tipConfirmations, err := dag.confirmations(tip) if err != nil { 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. "+ - "Want: 1, got: %d", tipConfirmations) + "Want: %d, got: %d", expectedConfirmations, tipConfirmations) } } - // Generate K blocks to force the "main" chain to become red - nodeGenerator := buildNodeGenerator(dag.dagParams.K, false) - branchingChainTip := dag.index.LookupNode(loadedBlocks[0].Hash()) - for i := uint32(0); i < dag.dagParams.K; i++ { - nextBranchingChainTip := nodeGenerator(setFromSlice(branchingChainTip)) + // Generate 100 blocks to force the "main" chain to become red + branchingChainTip := branchingBlocks[1] + for i := uint32(0); i < 100; i++ { + nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip)) dag.virtual.AddTip(nextBranchingChainTip) branchingChainTip = nextBranchingChainTip } // 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) if err != nil { 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 - redChainTip := dag.index.LookupNode(chainBlocks[len(chainBlocks)-1].Hash()) + redChainTip := chainBlocks[len(chainBlocks)-1] redChainTipConfirmations, err := dag.confirmations(redChainTip) if err != nil { 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) { // Create a new database and DAG instance to run tests against. + params := dagconfig.SimNetParams + params.K = 1 dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{ - DAGParams: &dagconfig.SimNetParams, + DAGParams: ¶ms, }) if err != nil { t.Fatalf("Failed to setup DAG instance: %v", err) @@ -1394,34 +1010,20 @@ func TestAcceptingBlock(t *testing.T) { "Want: virtual, got: %s", genesisAcceptingBlock.hash) } - 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()) - } - } + chainBlocks := make([]*blockNode, 5) + chainBlocks[0] = dag.genesis + buildNode := buildNodeGenerator(dag.dagParams.K, true) + for i := uint32(1); i <= 4; i++ { + chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1])) + dag.virtual.AddTip(chainBlocks[i]) } - // 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 - for i, chainBlock := range chainBlocks[:1] { - expectedAcceptingBlock := chainBlocks[i+1] - expectedAcceptingBlockNode := dag.index.LookupNode(expectedAcceptingBlock.Hash()) - - chainBlockNode := dag.index.LookupNode(chainBlock.Hash()) + for i, chainBlockNode := range chainBlocks[:len(chainBlocks)-1] { + expectedAcceptingBlockNode := chainBlocks[i+1] chainAcceptingBlockNode, err := dag.acceptingBlock(chainBlockNode) 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 { t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for chain block. "+ @@ -1429,16 +1031,16 @@ func TestAcceptingBlock(t *testing.T) { } } - // Add a branching block - branchingBlock, err := loadBlocks("blk_3B.dat") - if err != nil { - t.Fatalf("Error loading file: %v\n", err) - } - processBlocks(branchingBlock) + branchingBlocks := make([]*blockNode, 2) + // Add two branching blocks + branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1])) + dag.virtual.AddTip(branchingBlocks[0]) + branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0])) + dag.virtual.AddTip(branchingBlocks[1]) // Make sure that the accepting block of the parent of the branching block didn't change - expectedAcceptingBlock := dag.index.LookupNode(chainBlocks[3].Hash()) - intersectionBlock := dag.index.LookupNode(chainBlocks[2].Hash()) + expectedAcceptingBlock := chainBlocks[2] + intersectionBlock := chainBlocks[1] intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock) if err != nil { t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err) @@ -1448,29 +1050,26 @@ func TestAcceptingBlock(t *testing.T) { "Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash) } - // Make sure that the accepting block of all the tips in the virtual block - for _, tip := range dag.virtual.tips() { - tipAcceptingBlock, err := dag.acceptingBlock(tip) - if err != nil { - t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err) - } - if tipAcceptingBlock != &dag.virtual.blockNode { - t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+ - "Want: Virtual, got: %s", tipAcceptingBlock.hash) - } + // Make sure that the accepting block of the chain tips is the virtual block + tipAcceptingBlock, err := dag.acceptingBlock(chainBlocks[len(chainBlocks)-1]) + if err != nil { + t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err) + } + if tipAcceptingBlock != &dag.virtual.blockNode { + t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+ + "Want: Virtual, got: %s", tipAcceptingBlock.hash) } - // Generate K blocks to force the "main" chain to become red - nodeGenerator := buildNodeGenerator(dag.dagParams.K, false) - branchingChainTip := dag.index.LookupNode(branchingBlock[0].Hash()) - for i := uint32(0); i < dag.dagParams.K; i++ { - nextBranchingChainTip := nodeGenerator(setFromSlice(branchingChainTip)) + // Generate 100 blocks to force the "main" chain to become red + branchingChainTip := branchingBlocks[1] + for i := uint32(0); i < 100; i++ { + nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip)) dag.virtual.AddTip(nextBranchingChainTip) branchingChainTip = nextBranchingChainTip } // Make sure that a red block returns nil - redChainBlock := dag.index.LookupNode(chainBlocks[3].Hash()) + redChainBlock := chainBlocks[2] redChainBlockAcceptionBlock, err := dag.acceptingBlock(redChainBlock) if err != nil { 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 - redChainTip := dag.index.LookupNode(chainBlocks[len(chainBlocks)-1].Hash()) + redChainTip := chainBlocks[len(chainBlocks)-1] redChainTipAcceptingBlock, err := dag.acceptingBlock(redChainTip) if err != nil { 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) } } - -// 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() -} diff --git a/blockdag/error.go b/blockdag/error.go index 2cf053f53..75e994ce4 100644 --- a/blockdag/error.go +++ b/blockdag/error.go @@ -84,6 +84,10 @@ const ( // the expected value. 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 // checkpoint height does not match the expected one. ErrBadCheckpoint diff --git a/blockdag/example_test.go b/blockdag/example_test.go index a3f797e50..7e4339cb5 100644 --- a/blockdag/example_test.go +++ b/blockdag/example_test.go @@ -67,5 +67,5 @@ func ExampleBlockDAG_ProcessBlock() { fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // Output: - // Failed to process block: already have block 63bbcfdd699ffd8cb19878564b14d3af8ba4d7ee4d1dd54925a7c21d5b5b5fdc + // Failed to process block: already have block 09c081c17dbfb421fc015aa77ba17e278c1a1cf9a7311a2042ffb8932d2ff365 } diff --git a/blockdag/testdata/blk_0_to_4.dat b/blockdag/testdata/blk_0_to_4.dat index 10a82ca25783a0f298b7bb2d41c480050521ec9b..0116e307a5225bc4e525661d7ad5e9a822f63d9c 100644 GIT binary patch literal 2036 zcmeylZ_CYx3=9m6K+JIEPWO(o@VT`w=dQc`L+KLlq6fS8z1?to*J}x1C&vG8=YrJW z1A+4!cTfIXGSul3 z$vgf#DM%SE|0yHYqh7bLdU+J%AH^;EYY#Ra;EdkzYw<+ark64=9?Cxa81mz3e2e+q zsy)UMt8LA%_pSLrD4-&D-aEhH;IfCWGqbL@S#{f#{GB~B#!T_@>R)&BUpLR+yAdfM zV2)-YJ|F@E@dkwWN|9UQt}nNG?&ba^6*jZ*c-=08vr*pdFq^Q^V9N;w3n;jOkp}_T zOo5AmLi;)_wAGHOnNE0n_swArwjEUrrp&fK#icWQ-n{|qVw#Y`wRa+OrSb)pQK zx_2!@+sVT}?H5T*fE$mAnI1hZMg}u9mWFcmM_2YInDd#_arZSd+L#hg-dMXbkF~L;khw< zV`SW=m#XNWnJNu0eK%CXIc#XDNeSXNHWQI!ao^_4W6W)~G zj&ihr{rgh2(}gL)37aX4-r!)`L@%LWAu)Q@PwQ2zNQvTh$au`jUJ&(O(J7%-^P7Qy zMUDTQTZT*4(=2+~!(#Scp7Xo5WP+3FHHD)WW>*%vrOdgMs@wfo@aOkuI+N<+zch)N zv|jU`!&NYcsl)eH&q_s}=I51r?(n*PW-c@7xt`-L)06gA%%n&~`NHP&B|3Lk``ndC zsjI4&(U4)Xozt3kZ0puTY6SxI4hyb|`Fj7@%96nAc`4@b{*s=RURx-O-jER5L@%LW sAu)RQE&0klbJ>ya8(j~@3X}(=&S>1fO|t25*A0PZ+!kt0SE(7j0HT<>-~a#s literal 1948 zcmeylZ_CYx3=9m6K+JIEPWO(o@VT`w=dQc`L+KLlq6fS8z1?to*J}x1C&vG8=YrHw z0yZ4E6$8@$|9`y#%p69L&0yPMBG~BvK)}bqz^xx(6zrj&RFa&c4-x_aMutzUT3|y0 zvFU_~{RDb60Ah~-~|F-iB=Bn>X*#GYpM9j zHT|FYhq>RRj?C`Xo{rT8NP?S?!WH5s1_oy0!!;;~(r_gd0H6RRE?mK`p4+E&T}gIy9CL89}marHJi2_rg4zU*Y#%1x<>V z8S3p@_gS!G4N_2HFi|4XsnXIXZQ_e>{0G)-S+sre!-|`?*3JC0*SA=3vHijvx%#rd zlWwR#U*cYzz0SH|(~3{M9baBVoc-{Bg6#TV-xNCen8h>J<%w>{_nP0c$o9~pdgFPP zDw8rcv*om3ewtJ4br-{SG=a@X5ebtbAtHl=DUC=%0YG9zUgyX*Pg^~gjZ@-We$3_z zuZ}Ez`B#UZ-+IcO1qoU+f;NEtNQ;OxeD%B8#_=G>bHT;2bsOJrnq>0ZuTbt$^izjY zxfL(drNc!{t&7(=T>751*!0`CoQRE_63j*MXIc+2ExW#}J4@3$KUZ+Yw>9rJRe0rl z-tX9_AMsuK(T_dnANgQdjM07j^`x}=LFsF*c&nH={bJKc(s9PobIAV2v z2*?C%dYIDAq;Ob^yQ&~6ZRWOxaUus&f;j3qH->MFjJx#m)oI}jF^4jVvv)%|w%KqT z{+cR~bal4n-!$#p{v7xDCcM>r@*wTpY9ZJ2J_5a_?jKtBo3RGEma#-{K5{DtlHvW2rz>j2XP>nkDd4r1bo4v-1-4V!5;caCCMrJAR!Q7Wcb9Y1u{T?T4)!Il#W7LYAKH$wnE)4={;M1& literal 457 zcmeylZ_CYtj0_+k!01$I>613`#W(%~Yql)fzW8Cq&0Fhc{@LqWEV$TyVUApV%DeTy z6H7&l_RhJl`r?bT*3a9SXRoifKGkmVmhoc#+e&8fjCFaU8}hy8_bjqKw5Z;Ao~6p9 zjLmF0?U$eC6notTTZ4z#eB@RPNc#W(dIe^vI%bgLAP$6zV59y60bg(^w|;<8u!nw9 zNpgxlNC*TN89uRUfei`7rV}OxRzWBLK;{8m2LUjhSZT0v*EzDy(^k)A!_t#?>`LLV?VA=Z&bVfnZ@QMT^xQMwrBxflH%7)?dimELA3D zY-Y=8zx*_(*y}FX8a%}2Be!Be(*OV0D?r@Dz`z7zz&JSd{s#iS;4p6e0Ha_J{iKrQ i6n&5o2rx2yV$}j05{Oef)Cz(D05T8gItajL8Up}2nrBA< diff --git a/blockdag/testdata/blk_3C.dat b/blockdag/testdata/blk_3C.dat index 57ef5b56efc7087b5f074ac0a95db064cfd150d5..205046d2fda551ad581b4bea56ad7581f0b8e451 100644 GIT binary patch delta 231 zcmeyt)W=-^bKjPmZHx>cAi$({OwDw{+q-WLbFl5GVlZX4{V6V;+4JW0H;40zix@i? zBabd;nenY?dU}+o!p?7h<1zx~RB`zEBx`kUS}zcFt3>y2gv`xXqQ19&m;BYaB6GfZ z-=Pa<{u&RHh1YGeHMU_G8on E07;!^TmS$7 literal 376 zcmeylZ_CXBMg|ZNV2aV-5&gpbtJ7Yyi=S@j-7aiUi+cJ`{K5Z!t0fr2UVf`~s$`LfmSv3#=dyn@QV zUmxqYsH;8OqxDKQKY3auXG7lo`pa_H)NBM!y4jwYr#(+v+{>@K)r7hUk>L}o7TAzLEE?fL TU=@S{0AwD}br1m8ij4sPZZLZb diff --git a/blockdag/testdata/blk_3D.dat b/blockdag/testdata/blk_3D.dat index 8c856d15f9a841ec2725008a7f47ee85cbdae6f6..552c6cba707ae130648a8e53397fa6899a973bdb 100644 GIT binary patch literal 524 zcmeylZ_7;o9|-t@Be?YgjDkJ%lS-0P z^g%)(z{v24RSRT*_)3vm;;t{ZdhX@^B^5Ta@Oa%WgR@cI?U<$^M8K943KozpKsQ4G zLN68;$T%kT(|Q#vQlhvWG9GiX7eu{RbV_K|{AM6vQR6@7mf@21Abm`XfiMG!Q4Y7A HBF_Qo literal 502 zcmeylZ_CYhj0_+k!01$I>613`#W(%~Yql)fzW8Cq&0Fhc{@LqWEV$TyVUApVKet<} zu^L-gLQ8em)Q4@Cw->IT$ShZS;mozj?A-VD`T5DyDmfeS?$=+IyQXF%aMI28%slOR z(&Aoz-K{1ZZjZp$;2}02xfKJF{{O#TfeEUP8RR&K1EC_=sQ*B~7aYN@A7B*hp`TQe zoT3jB0s%&bPpn#CLjtksgo%Mw5DEa0c|g}e08A%V8e|;Pb&hQFwAFLjI3>>I$85gv z>d4ZUe|7lzt*6{skf1dqXaiU$%s7%1!#yQgwo=6QoqJ)P&9CtLu7W1T%MA7Qt@|w4 Ku?DNNpn?GKpQ2j; diff --git a/blockdag/utxoset.go b/blockdag/utxoset.go index 86e3fc33a..ffc4e3bc0 100644 --- a/blockdag/utxoset.go +++ b/blockdag/utxoset.go @@ -390,6 +390,7 @@ type UTXOSet interface { clone() UTXOSet Get(outPoint wire.OutPoint) (*UTXOEntry, bool) Multiset() *btcec.Multiset + WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error) } // diffFromTx is a common implementation for diffFromTx, that works @@ -550,6 +551,21 @@ func (fus *FullUTXOSet) removeAndUpdateMultiset(outPoint wire.OutPoint) error { 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 type DiffUTXOSet struct { base *FullUTXOSet @@ -698,6 +714,21 @@ func (dus *DiffUTXOSet) Multiset() *btcec.Multiset { 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) { w := &bytes.Buffer{} err := serializeUTXO(w, entry, outPoint) diff --git a/btcec/ecmh.go b/btcec/ecmh.go index 9f2e237be..8902cf619 100644 --- a/btcec/ecmh.go +++ b/btcec/ecmh.go @@ -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 // 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. -func (ms *Multiset) Hash() daghash.Hash { +func (ms *Multiset) Hash() *daghash.Hash { 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()...)) - 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. diff --git a/dagconfig/genesis.go b/dagconfig/genesis.go index 2d90e2def..2b5589dae 100644 --- a/dagconfig/genesis.go +++ b/dagconfig/genesis.go @@ -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 // network (genesis block). var genesisHash = daghash.Hash([daghash.HashSize]byte{ - 0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, - 0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b, - 0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1, - 0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63, + 0x65, 0xf3, 0x2f, 0x2d, 0x93, 0xb8, 0xff, 0x42, + 0x20, 0x1a, 0x31, 0xa7, 0xf9, 0x1c, 0x1a, 0x8c, + 0x27, 0x7e, 0xa1, 0x7b, 0xa7, 0x5a, 0x01, 0xfc, + 0x21, 0xb4, 0xbf, 0x7d, 0xc1, 0x81, 0xc0, 0x09, }) // genesisMerkleRoot is the hash of the first transaction in the genesis block @@ -64,10 +64,15 @@ var genesisBlock = wire.MsgBlock{ ParentHashes: []*daghash.Hash{}, HashMerkleRoot: &genesisMerkleRoot, AcceptedIDMerkleRoot: &daghash.Hash{}, - UTXOCommitment: &daghash.Hash{}, - Timestamp: time.Unix(0x5cdac4b0, 0), - Bits: 0x207fffff, - Nonce: 0x0, + 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), + Bits: 0x207fffff, + Nonce: 0x3, }, Transactions: []*wire.MsgTx{genesisCoinbaseTx}, } diff --git a/dagconfig/genesis_test.go b/dagconfig/genesis_test.go index b98fb2c12..792ad6369 100644 --- a/dagconfig/genesis_test.go +++ b/dagconfig/genesis_test.go @@ -121,35 +121,35 @@ func TestSimNetGenesisBlock(t *testing.T) { // genesisBlockBytes are the wire encoded bytes for the genesis block of the // main network as of protocol version 60002. var genesisBlockBytes = []byte{ - 0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b, /* |........| */ - 0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae, /* |.vW.}...| */ - 0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb, /* |..".....| */ - 0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d, /* |.......M| */ - 0x42, 0x01, 0xff, 0xed, 0x9d, 0x00, 0x00, 0x00, /* |B.......| */ - 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, 0xb0, 0xc4, 0xda, /* |........| */ - 0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, /* |\.......| */ - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0xff, /* |........| */ - 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f, /* |......./| */ - 0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, /* |P2SH/btc| */ - 0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* |d/......| */ - 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01, /* |......*.| */ - 0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00, /* |....Q...| */ - 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, /* |.| */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b, + 0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae, + 0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb, + 0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d, + 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, 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, 0xb0, 0xc4, 0xda, + 0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0xff, + 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f, + 0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, + 0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01, + 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, } // regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of diff --git a/mining/mining.go b/mining/mining.go index 483b3ac4c..6a6e5ba47 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -660,18 +660,22 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe return nil, err } 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{ Version: nextBlockVersion, ParentHashes: g.dag.TipHashes(), HashMerkleRoot: hashMerkleTree.Root(), AcceptedIDMerkleRoot: acceptedIDMerkleRoot, - UTXOCommitment: &daghash.ZeroHash, + UTXOCommitment: utxoCommitment, Timestamp: ts, Bits: reqDifficulty, } - for _, tx := range blockTxns { - msgBlock.AddTransaction(tx.MsgTx()) - } // Finally, perform a full check on the created block against the chain // consensus rules to ensure it properly connects to the current best @@ -697,6 +701,14 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe }, 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 // 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 @@ -749,6 +761,13 @@ func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions()) msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root() + utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, blockHeight) + if err != nil { + return err + } + + msgBlock.Header.UTXOCommitment = utxoCommitment + return nil } diff --git a/mining/test_utils.go b/mining/test_utils.go index b5271273d..92fb0ff58 100644 --- a/mining/test_utils.go +++ b/mining/test_utils.go @@ -70,7 +70,10 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren } // 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) 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) } } - updateMerkleRoots := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0) - if updateMerkleRoots { + updateHeaderFields := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0) + if updateHeaderFields { utilTxs := make([]*util.Tx, len(template.Block.Transactions)) for i, tx := range template.Block.Transactions { utilTxs[i] = util.NewTx(tx) } 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 { return nil, err } - template.Block.Header.AcceptedIDMerkleRoot = acceptedIDMerkleRoot } return template.Block, nil }