diff --git a/blockdag/accept_test.go b/blockdag/accept_test.go index ff9183f59..1207c8f40 100644 --- a/blockdag/accept_test.go +++ b/blockdag/accept_test.go @@ -21,7 +21,7 @@ func TestMaybeAcceptBlockErrors(t *testing.T) { } defer teardownFunc() - dag.TestSetCoinbaseMaturity(1) + dag.TestSetCoinbaseMaturity(0) // Test rejecting the block if its parents are missing orphanBlockFile := "blk_3B.dat" diff --git a/blockdag/dag.go b/blockdag/dag.go index 645c486f9..235a09a39 100644 --- a/blockdag/dag.go +++ b/blockdag/dag.go @@ -396,7 +396,7 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util // assume the transaction makes it into the next block when // evaluating its sequence blocks. inputBlueScore := entry.BlockBlueScore() - if entry.IsUnmined() { + if entry.IsUnaccepted() { inputBlueScore = dag.virtual.blueScore } @@ -833,7 +833,8 @@ func (dag *BlockDAG) TxsAcceptedByVirtual() (MultiBlockTxsAcceptanceData, error) // 2. Adds the new block to the DAG's tips. // 3. Updates the DAG's full UTXO set. // 4. Updates each of the tips' utxoDiff. -// 5. Update the finality point of the DAG (if required). +// 5. Applies the new virtual's blue score to all the unaccepted UTXOs +// 6. Updates the finality point of the DAG (if required). // // It returns the diff in the virtual block's UTXO set. // @@ -849,11 +850,21 @@ func (dag *BlockDAG) applyDAGChanges(node *blockNode, block *util.Block, newBloc dag.virtual.AddTip(node) // Build a UTXO set for the new virtual block - newVirtualUTXO, virtualTxsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode) + newVirtualPastUTXO, virtualTxsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode) if err != nil { return nil, nil, fmt.Errorf("could not restore past UTXO for virtual %s: %s", dag.virtual, err) } + // Apply the new virtual's blue score to all the unaccepted UTXOs + diffFromAcceptanceData, err := dag.virtual.diffFromAcceptanceData(newVirtualPastUTXO, virtualTxsAcceptanceData) + if err != nil { + return nil, nil, err + } + newVirtualUTXO, err := newVirtualPastUTXO.WithDiff(diffFromAcceptanceData) + if err != nil { + return nil, nil, err + } + // Apply new utxoDiffs to all the tips err = updateTipsUTXO(dag, newVirtualUTXO) if err != nil { @@ -886,7 +897,7 @@ func (node *blockNode) diffFromTxs(pastUTXO UTXOSet, transactions []*util.Tx) (* diff := NewUTXODiff() for _, tx := range transactions { - txDiff, err := pastUTXO.diffFromTx(tx.MsgTx(), node) + txDiff, err := pastUTXO.diffFromTx(tx.MsgTx(), UnacceptedBlueScore) if err != nil { return nil, err } @@ -899,6 +910,29 @@ func (node *blockNode) diffFromTxs(pastUTXO UTXOSet, transactions []*util.Tx) (* return diff, nil } +// diffFromAccpetanceData creates a diff that "updates" the blue scores of the given +// UTXOSet with the node's blueScore according to the given acceptance data. +func (node *blockNode) diffFromAcceptanceData(pastUTXO UTXOSet, blockTxsAcceptanceDatas MultiBlockTxsAcceptanceData) (*UTXODiff, error) { + diff := NewUTXODiff() + + for _, blockTxsAcceptanceData := range blockTxsAcceptanceDatas { + for _, txAcceptanceData := range blockTxsAcceptanceData { + if txAcceptanceData.IsAccepted { + acceptanceDiff, err := pastUTXO.diffFromAcceptedTx(txAcceptanceData.Tx.MsgTx(), node.blueScore) + if err != nil { + return nil, err + } + diff, err = diff.WithDiff(acceptanceDiff) + if err != nil { + return nil, err + } + } + } + } + + return diff, nil +} + // verifyAndBuildUTXO verifies all transactions in the given block and builds its UTXO // to save extra traversals it returns the transactions acceptance data and the compactFeeData for the new block func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx, fastAdd bool) ( @@ -919,17 +953,30 @@ func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx return nil, nil, nil, err } - diff, err := node.diffFromTxs(pastUTXO, transactions) + // We diff from the acceptance data here to "replace" the blueScore that was diff-ed + // out of the virtual's UTXO in pastUTXO with this node's blueScore. + diffFromAcceptanceData, err := node.diffFromAcceptanceData(pastUTXO, txsAcceptanceData) + if err != nil { + return nil, nil, nil, err + } + utxo, err := pastUTXO.WithDiff(diffFromAcceptanceData) + if err != nil { + return nil, nil, nil, err + } - utxo, err := pastUTXO.WithDiff(diff) + diffFromTxs, err := node.diffFromTxs(utxo, transactions) + if err != nil { + return nil, nil, nil, err + } + utxo, err = utxo.WithDiff(diffFromTxs) 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", + str := fmt.Sprintf("block %s UTXO commitment is invalid - block "+ + "header indicates %s, but calculated value is %s", node.hash, node.utxoCommitment, calculatedMultisetHash) return nil, nil, nil, ruleError(ErrBadUTXOCommitment, str) } diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index 1bd553474..ac93b822e 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -55,8 +55,8 @@ func TestBlockCount(t *testing.T) { defer teardownFunc() // Since we're not dealing with the real block DAG, set the coinbase - // maturity to 1. - dag.TestSetCoinbaseMaturity(1) + // maturity to 0. + dag.TestSetCoinbaseMaturity(0) for i := 1; i < len(blocks); i++ { isOrphan, delay, err := dag.ProcessBlock(blocks[i], BFNone) @@ -108,8 +108,8 @@ func TestHaveBlock(t *testing.T) { defer teardownFunc() // Since we're not dealing with the real block DAG, set the coinbase - // maturity to 1. - dag.TestSetCoinbaseMaturity(1) + // maturity to 0. + dag.TestSetCoinbaseMaturity(0) for i := 1; i < len(blocks); i++ { isOrphan, delay, err := dag.ProcessBlock(blocks[i], BFNone) @@ -209,7 +209,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: "13580c9c2ed13caeedbad15167ee47bc1d26b4f88cc13054893a6d795c3baa7b", want: true}, + {hash: "6ffe9704c50b3f1892ce9e667337304ec0e9eb50a23673bc8ff7aaa20745ee4a", want: true}, // Block 100000 should be present (as an orphan). {hash: "65b20b048a074793ebfd1196e49341c8d194dabfc6b44a4fd0c607406e122baf", want: true}, @@ -299,7 +299,7 @@ func TestCalcSequenceLock(t *testing.T) { TxID: *unConfTx.TxID(), Index: 0, } - if isAccepted, err := utxoSet.AddTx(unConfTx, UnminedBlueScore); err != nil { + if isAccepted, err := utxoSet.AddTx(unConfTx, UnacceptedBlueScore); err != nil { t.Fatalf("AddTx unexpectedly failed. Error: %s", err) } else if !isAccepted { t.Fatalf("AddTx unexpectedly didn't add tx %s", unConfTx.TxID()) @@ -823,8 +823,8 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF defer teardownFunc() // Since we're not dealing with the real block DAG, set the coinbase - // maturity to 1. - dag.TestSetCoinbaseMaturity(1) + // maturity to 0. + dag.TestSetCoinbaseMaturity(0) guard := monkey.Patch(targetFunction, replacementFunction) defer guard.Unpatch() @@ -909,7 +909,7 @@ func TestConfirmations(t *testing.T) { t.Fatalf("Failed to setup DAG instance: %v", err) } defer teardownFunc() - dag.TestSetCoinbaseMaturity(1) + dag.TestSetCoinbaseMaturity(0) // Check that the genesis block of a DAG with only the genesis block in it has confirmations = 1. genesisConfirmations, err := dag.blockConfirmations(dag.genesis) @@ -1024,7 +1024,7 @@ func TestAcceptingBlock(t *testing.T) { t.Fatalf("Failed to setup DAG instance: %v", err) } defer teardownFunc() - dag.TestSetCoinbaseMaturity(1) + dag.TestSetCoinbaseMaturity(0) // Check that the genesis block of a DAG with only the genesis block in it is accepted by the virtual. genesisAcceptingBlock, err := dag.acceptingBlock(dag.genesis) diff --git a/blockdag/external_dag_test.go b/blockdag/external_dag_test.go index ec93ef207..d68fc6e3f 100644 --- a/blockdag/external_dag_test.go +++ b/blockdag/external_dag_test.go @@ -163,7 +163,7 @@ func TestFinality(t *testing.T) { func TestSubnetworkRegistry(t *testing.T) { params := dagconfig.SimNetParams params.K = 1 - params.BlockCoinbaseMaturity = 1 + params.BlockCoinbaseMaturity = 0 dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{ DAGParams: ¶ms, }) @@ -188,7 +188,7 @@ func TestSubnetworkRegistry(t *testing.T) { func TestChainedTransactions(t *testing.T) { params := dagconfig.SimNetParams - params.BlockCoinbaseMaturity = 1 + params.BlockCoinbaseMaturity = 0 // Create a new database and dag instance to run tests against. dag, teardownFunc, err := blockdag.DAGSetup("TestChainedTransactions", blockdag.Config{ DAGParams: ¶ms, @@ -304,7 +304,7 @@ func TestChainedTransactions(t *testing.T) { func TestGasLimit(t *testing.T) { params := dagconfig.SimNetParams params.K = 1 - params.BlockCoinbaseMaturity = 1 + params.BlockCoinbaseMaturity = 0 dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{ DAGParams: ¶ms, }) diff --git a/blockdag/indexers/txindex_test.go b/blockdag/indexers/txindex_test.go index bd8f8223f..7f2adfe9d 100644 --- a/blockdag/indexers/txindex_test.go +++ b/blockdag/indexers/txindex_test.go @@ -40,7 +40,7 @@ func TestTxIndexConnectBlock(t *testing.T) { indexManager := NewManager([]Indexer{txIndex}) params := dagconfig.SimNetParams - params.BlockCoinbaseMaturity = 1 + params.BlockCoinbaseMaturity = 0 params.K = 1 config := blockdag.Config{ diff --git a/blockdag/test_utils.go b/blockdag/test_utils.go index 7f68f803e..22fd1c9dd 100644 --- a/blockdag/test_utils.go +++ b/blockdag/test_utils.go @@ -154,16 +154,24 @@ func GetVirtualFromParentsForTest(dag *BlockDAG, parentHashes []*daghash.Hash) ( } virtual := newVirtualBlock(parents, dag.dagParams.K) - pastUTXO, _, err := dag.pastUTXO(&virtual.blockNode) + pastUTXO, acceptanceData, err := dag.pastUTXO(&virtual.blockNode) if err != nil { return nil, err } - diffPastUTXO := pastUTXO.clone().(*DiffUTXOSet) - err = diffPastUTXO.meldToBase() + diffFromAcceptanceData, err := virtual.blockNode.diffFromAcceptanceData(pastUTXO, acceptanceData) if err != nil { return nil, err } - virtual.utxoSet = diffPastUTXO.base + utxo, err := pastUTXO.WithDiff(diffFromAcceptanceData) + if err != nil { + return nil, err + } + diffUTXO := utxo.clone().(*DiffUTXOSet) + err = diffUTXO.meldToBase() + if err != nil { + return nil, err + } + virtual.utxoSet = diffUTXO.base return virtual, nil } diff --git a/blockdag/testdata/blk_0_to_4.dat b/blockdag/testdata/blk_0_to_4.dat index fc0edcbbf..daadaadc1 100644 Binary files a/blockdag/testdata/blk_0_to_4.dat and b/blockdag/testdata/blk_0_to_4.dat differ diff --git a/blockdag/testdata/blk_3A.dat b/blockdag/testdata/blk_3A.dat index a940ee38f..ed3b66cd6 100644 Binary files a/blockdag/testdata/blk_3A.dat and b/blockdag/testdata/blk_3A.dat differ diff --git a/blockdag/testdata/blk_3B.dat b/blockdag/testdata/blk_3B.dat index a9b3f99c6..494acd4fc 100644 Binary files a/blockdag/testdata/blk_3B.dat and b/blockdag/testdata/blk_3B.dat differ diff --git a/blockdag/testdata/blk_3C.dat b/blockdag/testdata/blk_3C.dat index 507e9f879..4771606c8 100644 Binary files a/blockdag/testdata/blk_3C.dat and b/blockdag/testdata/blk_3C.dat differ diff --git a/blockdag/testdata/blk_3D.dat b/blockdag/testdata/blk_3D.dat index d2c30d6f2..2f84a83e0 100644 Binary files a/blockdag/testdata/blk_3D.dat and b/blockdag/testdata/blk_3D.dat differ diff --git a/blockdag/utxoset.go b/blockdag/utxoset.go index 85ad82ece..6201d54cd 100644 --- a/blockdag/utxoset.go +++ b/blockdag/utxoset.go @@ -13,15 +13,15 @@ import ( ) const ( - // UnminedBlueScore is the blue score used for the "block" blueScore field of the - // contextual transaction information provided in a transaction store - // when it has not yet been mined into a block. - UnminedBlueScore = math.MaxUint64 + // UnacceptedBlueScore is the blue score used for the "block" blueScore + // field of the contextual transaction information provided in a + // transaction store when it has not yet been accepted by a block. + UnacceptedBlueScore = math.MaxUint64 ) // UTXOEntry houses details about an individual transaction output in a utxo // set such as whether or not it was contained in a coinbase tx, the blue -// score of the block that contains the tx, its public key script, and how +// score of the block that accepts the tx, its public key script, and how // much it pays. type UTXOEntry struct { // NOTE: Additions, deletions, or modifications to the order of the @@ -32,7 +32,7 @@ type UTXOEntry struct { amount uint64 pkScript []byte // The public key script for the output. - blockBlueScore uint64 // Blue score of the block containing the tx. + blockBlueScore uint64 // Blue score of the block accepting the tx. // packedFlags contains additional info about output such as whether it // is a coinbase, and whether it has been modified @@ -47,7 +47,7 @@ func (entry *UTXOEntry) IsCoinbase() bool { return entry.packedFlags&tfCoinbase == tfCoinbase } -// BlockBlueScore returns the blue score of the block containing the output. +// BlockBlueScore returns the blue score of the block accepting the output. func (entry *UTXOEntry) BlockBlueScore() uint64 { return entry.blockBlueScore } @@ -62,10 +62,10 @@ func (entry *UTXOEntry) PkScript() []byte { return entry.pkScript } -// IsUnmined returns true iff this UTXOEntry has still not been mined, -// a.k.a. still in the mempool. -func (entry *UTXOEntry) IsUnmined() bool { - return entry.blockBlueScore == UnminedBlueScore +// IsUnaccepted returns true iff this UTXOEntry has been included in a block +// but has not yet been accepted by any block. +func (entry *UTXOEntry) IsUnaccepted() bool { + return entry.blockBlueScore == UnacceptedBlueScore } // txoFlags is a bitmask defining additional information and state for a @@ -100,7 +100,8 @@ func (uc utxoCollection) String() string { i := 0 for outpoint, utxoEntry := range uc { - utxoStrings[i] = fmt.Sprintf("(%s, %d) => %d", outpoint.TxID, outpoint.Index, utxoEntry.amount) + utxoStrings[i] = fmt.Sprintf("(%s, %d) => %d, blueScore: %d", + outpoint.TxID, outpoint.Index, utxoEntry.amount, utxoEntry.blockBlueScore) i++ } @@ -133,6 +134,13 @@ func (uc utxoCollection) contains(outpoint wire.Outpoint) bool { return ok } +// containsWithBlueScore returns a boolean value indicating whether a UTXOEntry +// is in the set and its blue score is equal to the given blue score. +func (uc utxoCollection) containsWithBlueScore(outpoint wire.Outpoint, blueScore uint64) bool { + entry, ok := uc.get(outpoint) + return ok && entry.blockBlueScore == blueScore +} + // clone returns a clone of this collection func (uc utxoCollection) clone() utxoCollection { clone := utxoCollection{} @@ -203,10 +211,19 @@ func (d *UTXODiff) diffFrom(other *UTXODiff) (*UTXODiff, error) { // If they are not in other.toAdd - should be added in result.toRemove // If they are in other.toRemove - base utxoSet is not the same for outpoint, utxoEntry := range d.toAdd { - if !other.toAdd.contains(outpoint) { + if !other.toAdd.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toRemove.add(outpoint, utxoEntry) } - if other.toRemove.contains(outpoint) { + if diffEntry, ok := other.toRemove.get(outpoint); ok { + // An exception is made for entries with unequal blue scores + // as long as the appropriate entry exists in either d.toRemove + // or other.toAdd. + // These are just "updates" to accepted blue score + if diffEntry.blockBlueScore != utxoEntry.blockBlueScore && + (d.toRemove.containsWithBlueScore(outpoint, diffEntry.blockBlueScore) || + other.toAdd.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore)) { + continue + } return nil, fmt.Errorf("diffFrom: outpoint %s both in d.toAdd and in other.toRemove", outpoint) } } @@ -215,10 +232,19 @@ func (d *UTXODiff) diffFrom(other *UTXODiff) (*UTXODiff, error) { // If they are not in other.toRemove - should be added in result.toAdd // If they are in other.toAdd - base utxoSet is not the same for outpoint, utxoEntry := range d.toRemove { - if !other.toRemove.contains(outpoint) { + if !other.toRemove.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toAdd.add(outpoint, utxoEntry) } - if other.toAdd.contains(outpoint) { + if diffEntry, ok := other.toAdd.get(outpoint); ok { + // An exception is made for entries with unequal blue scores + // as long as the appropriate entry exists in either d.toAdd + // or other.toRemove. + // These are just "updates" to accepted blue score + if diffEntry.blockBlueScore != utxoEntry.blockBlueScore && + (d.toAdd.containsWithBlueScore(outpoint, diffEntry.blockBlueScore) || + other.toRemove.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore)) { + continue + } return nil, errors.New("diffFrom: transaction both in d.toRemove and in other.toAdd") } } @@ -226,7 +252,7 @@ func (d *UTXODiff) diffFrom(other *UTXODiff) (*UTXODiff, error) { // All transactions in other.toAdd: // If they are not in d.toAdd - should be added in result.toAdd for outpoint, utxoEntry := range other.toAdd { - if !d.toAdd.contains(outpoint) { + if !d.toAdd.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toAdd.add(outpoint, utxoEntry) } } @@ -234,7 +260,7 @@ func (d *UTXODiff) diffFrom(other *UTXODiff) (*UTXODiff, error) { // All transactions in other.toRemove: // If they are not in d.toRemove - should be added in result.toRemove for outpoint, utxoEntry := range other.toRemove { - if !d.toRemove.contains(outpoint) { + if !d.toRemove.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toRemove.add(outpoint, utxoEntry) } } @@ -283,10 +309,19 @@ func (d *UTXODiff) WithDiff(diff *UTXODiff) (*UTXODiff, error) { // If they are in diff.toAdd - should throw an error // Otherwise - should be ignored for outpoint, utxoEntry := range d.toAdd { - if !diff.toRemove.contains(outpoint) { + if !diff.toRemove.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toAdd.add(outpoint, utxoEntry) } - if diff.toAdd.contains(outpoint) { + if diffEntry, ok := diff.toAdd.get(outpoint); ok { + // An exception is made for entries with unequal blue scores + // as long as the appropriate entry exists in either d.toRemove + // or diff.toRemove. + // These are just "updates" to accepted blue score + if diffEntry.blockBlueScore != utxoEntry.blockBlueScore && + (d.toRemove.containsWithBlueScore(outpoint, diffEntry.blockBlueScore) || + diff.toRemove.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore)) { + continue + } return nil, ruleError(ErrWithDiff, fmt.Sprintf("WithDiff: outpoint %s both in d.toAdd and in other.toAdd", outpoint)) } } @@ -296,10 +331,19 @@ func (d *UTXODiff) WithDiff(diff *UTXODiff) (*UTXODiff, error) { // If they are in diff.toRemove - should throw an error // Otherwise - should be ignored for outpoint, utxoEntry := range d.toRemove { - if !diff.toAdd.contains(outpoint) { + if !diff.toAdd.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toRemove.add(outpoint, utxoEntry) } - if diff.toRemove.contains(outpoint) { + if diffEntry, ok := diff.toRemove.get(outpoint); ok { + // An exception is made for entries with unequal blue scores + // as long as the appropriate entry exists in either d.toAdd + // or diff.toAdd. + // These are just "updates" to accepted blue score + if diffEntry.blockBlueScore != utxoEntry.blockBlueScore && + (d.toAdd.containsWithBlueScore(outpoint, diffEntry.blockBlueScore) || + diff.toAdd.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore)) { + continue + } return nil, ruleError(ErrWithDiff, "WithDiff: transaction both in d.toRemove and in other.toRemove") } } @@ -307,7 +351,7 @@ func (d *UTXODiff) WithDiff(diff *UTXODiff) (*UTXODiff, error) { // All transactions in diff.toAdd: // If they are not in d.toRemove - should be added in result.toAdd for outpoint, utxoEntry := range diff.toAdd { - if !d.toRemove.contains(outpoint) { + if !d.toRemove.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toAdd.add(outpoint, utxoEntry) } } @@ -315,7 +359,7 @@ func (d *UTXODiff) WithDiff(diff *UTXODiff) (*UTXODiff, error) { // All transactions in diff.toRemove: // If they are not in d.toAdd - should be added in result.toRemove for outpoint, utxoEntry := range diff.toRemove { - if !d.toAdd.contains(outpoint) { + if !d.toAdd.containsWithBlueScore(outpoint, utxoEntry.blockBlueScore) { result.toRemove.add(outpoint, utxoEntry) } } @@ -337,7 +381,7 @@ func (d *UTXODiff) clone() *UTXODiff { // AddEntry adds a UTXOEntry to the diff func (d *UTXODiff) AddEntry(outpoint wire.Outpoint, entry *UTXOEntry) error { - if d.toRemove.contains(outpoint) { + if d.toRemove.containsWithBlueScore(outpoint, entry.blockBlueScore) { d.toRemove.remove(outpoint) } else if _, exists := d.toAdd[outpoint]; exists { return fmt.Errorf("AddEntry: Cannot add outpoint %s twice", outpoint) @@ -356,7 +400,7 @@ func (d *UTXODiff) AddEntry(outpoint wire.Outpoint, entry *UTXOEntry) error { // RemoveEntry removes a UTXOEntry from the diff func (d *UTXODiff) RemoveEntry(outpoint wire.Outpoint, entry *UTXOEntry) error { - if d.toAdd.contains(outpoint) { + if d.toAdd.containsWithBlueScore(outpoint, entry.blockBlueScore) { d.toAdd.remove(outpoint) } else if _, exists := d.toRemove[outpoint]; exists { return fmt.Errorf("removeEntry: Cannot remove outpoint %s twice", outpoint) @@ -393,7 +437,8 @@ type UTXOSet interface { fmt.Stringer diffFrom(other UTXOSet) (*UTXODiff, error) WithDiff(utxoDiff *UTXODiff) (UTXOSet, error) - diffFromTx(tx *wire.MsgTx, node *blockNode) (*UTXODiff, error) + diffFromTx(tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) + diffFromAcceptedTx(tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (ok bool, err error) clone() UTXOSet Get(outpoint wire.Outpoint) (*UTXOEntry, bool) @@ -405,7 +450,7 @@ type UTXOSet interface { // for both diff-based and full UTXO sets // Returns a diff that is equivalent to provided transaction, // or an error if provided transaction is not valid in the context of this UTXOSet -func diffFromTx(u UTXOSet, tx *wire.MsgTx, containingNode *blockNode) (*UTXODiff, error) { +func diffFromTx(u UTXOSet, tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) { diff := NewUTXODiff() isCoinbase := tx.IsCoinBase() if !isCoinbase { @@ -423,7 +468,7 @@ func diffFromTx(u UTXOSet, tx *wire.MsgTx, containingNode *blockNode) (*UTXODiff } } for i, txOut := range tx.TxOut { - entry := NewUTXOEntry(txOut, isCoinbase, containingNode.blueScore) + entry := NewUTXOEntry(txOut, isCoinbase, acceptingBlueScore) outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i)) err := diff.AddEntry(outpoint, entry) if err != nil { @@ -433,6 +478,38 @@ func diffFromTx(u UTXOSet, tx *wire.MsgTx, containingNode *blockNode) (*UTXODiff return diff, nil } +// diffFromAcceptedTx is a common implementation for diffFromAcceptedTx, that works +// for both diff-based and full UTXO sets. +// Returns a diff that replaces an entry's blockBlueScore with the given acceptingBlueScore. +// Returns an error if the provided transaction's entry is not valid in the context +// of this UTXOSet. +func diffFromAcceptedTx(u UTXOSet, tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) { + diff := NewUTXODiff() + isCoinbase := tx.IsCoinBase() + for i, txOut := range tx.TxOut { + // Fetch any unaccepted transaction + existingOutpoint := *wire.NewOutpoint(tx.TxID(), uint32(i)) + existingEntry, ok := u.Get(existingOutpoint) + if !ok { + return nil, fmt.Errorf("cannot accept outpoint %s because it doesn't exist in the given UTXO", existingOutpoint) + } + + // Remove unaccepted entries + err := diff.RemoveEntry(existingOutpoint, existingEntry) + if err != nil { + return nil, err + } + + // Add new entries with their accepting blue score + newEntry := NewUTXOEntry(txOut, isCoinbase, acceptingBlueScore) + err = diff.AddEntry(existingOutpoint, newEntry) + if err != nil { + return nil, err + } + } + return diff, nil +} + // FullUTXOSet represents a full list of transaction outputs and their values type FullUTXOSet struct { utxoCollection @@ -501,8 +578,8 @@ func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool // diffFromTx returns a diff that is equivalent to provided transaction, // or an error if provided transaction is not valid in the context of this UTXOSet -func (fus *FullUTXOSet) diffFromTx(tx *wire.MsgTx, node *blockNode) (*UTXODiff, error) { - return diffFromTx(fus, tx, node) +func (fus *FullUTXOSet) diffFromTx(tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) { + return diffFromTx(fus, tx, acceptingBlueScore) } func (fus *FullUTXOSet) containsInputs(tx *wire.MsgTx) bool { @@ -516,6 +593,10 @@ func (fus *FullUTXOSet) containsInputs(tx *wire.MsgTx) bool { return true } +func (fus *FullUTXOSet) diffFromAcceptedTx(tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) { + return diffFromAcceptedTx(fus, tx, acceptingBlueScore) +} + // clone returns a clone of this utxoSet func (fus *FullUTXOSet) clone() UTXOSet { return &FullUTXOSet{utxoCollection: fus.utxoCollection.clone(), UTXOMultiset: fus.UTXOMultiset.Clone()} @@ -661,7 +742,7 @@ func (dus *DiffUTXOSet) containsInputs(tx *wire.MsgTx) bool { isInBase := dus.base.contains(outpoint) isInDiffToAdd := dus.UTXODiff.toAdd.contains(outpoint) isInDiffToRemove := dus.UTXODiff.toRemove.contains(outpoint) - if (!isInBase && !isInDiffToAdd) || isInDiffToRemove { + if (!isInBase && !isInDiffToAdd) || (isInDiffToRemove && !(isInBase && isInDiffToAdd)) { return false } } @@ -691,8 +772,12 @@ func (dus *DiffUTXOSet) meldToBase() error { // diffFromTx returns a diff that is equivalent to provided transaction, // or an error if provided transaction is not valid in the context of this UTXOSet -func (dus *DiffUTXOSet) diffFromTx(tx *wire.MsgTx, node *blockNode) (*UTXODiff, error) { - return diffFromTx(dus, tx, node) +func (dus *DiffUTXOSet) diffFromTx(tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) { + return diffFromTx(dus, tx, acceptingBlueScore) +} + +func (dus *DiffUTXOSet) diffFromAcceptedTx(tx *wire.MsgTx, acceptingBlueScore uint64) (*UTXODiff, error) { + return diffFromAcceptedTx(dus, tx, acceptingBlueScore) } func (dus *DiffUTXOSet) String() string { @@ -707,7 +792,12 @@ func (dus *DiffUTXOSet) clone() UTXOSet { // Get returns the UTXOEntry associated with provided outpoint in this UTXOSet. // Returns false in second output if this UTXOEntry was not found func (dus *DiffUTXOSet) Get(outpoint wire.Outpoint) (*UTXOEntry, bool) { - if dus.UTXODiff.toRemove.contains(outpoint) { + if toRemoveEntry, ok := dus.UTXODiff.toRemove.get(outpoint); ok { + // An exception is made for entries with unequal blue scores + // These are just "updates" to accepted blue score + if toAddEntry, ok := dus.UTXODiff.toAdd.get(outpoint); ok && toAddEntry.blockBlueScore != toRemoveEntry.blockBlueScore { + return toAddEntry, true + } return nil, false } if txOut, ok := dus.base.get(outpoint); ok { diff --git a/blockdag/utxoset_test.go b/blockdag/utxoset_test.go index 00b732de3..3f4305553 100644 --- a/blockdag/utxoset_test.go +++ b/blockdag/utxoset_test.go @@ -38,7 +38,7 @@ func TestUTXOCollection(t *testing.T) { collection: utxoCollection{ outpoint0: utxoEntry1, }, - expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 20 ]", + expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 20, blueScore: 1 ]", }, { name: "two members", @@ -46,7 +46,7 @@ func TestUTXOCollection(t *testing.T) { outpoint0: utxoEntry0, outpoint1: utxoEntry1, }, - expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20 ]", + expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0, (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]", }, } @@ -106,7 +106,7 @@ func TestUTXODiff(t *testing.T) { } // Test utxoDiff string representation - expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20 ], Multiset-Hash: 7cb61e48005b0c817211d04589d719bff87d86a6a6ce2454515f57265382ded7" + expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], Multiset-Hash: 7cb61e48005b0c817211d04589d719bff87d86a6a6ce2454515f57265382ded7" diffString := clonedDiff.String() if diffString != expectedDiffString { t.Errorf("unexpected diff string. "+ @@ -119,7 +119,8 @@ func TestUTXODiff(t *testing.T) { func TestUTXODiffRules(t *testing.T) { txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000") outpoint0 := *wire.NewOutpoint(txID0, 0) - utxoEntry0 := NewUTXOEntry(&wire.TxOut{PkScript: []byte{}, Value: 10}, true, 0) + utxoEntry1 := NewUTXOEntry(&wire.TxOut{PkScript: []byte{}, Value: 10}, true, 10) + utxoEntry2 := NewUTXOEntry(&wire.TxOut{PkScript: []byte{}, Value: 10}, true, 20) // For each of the following test cases, we will: // this.diffFrom(other) and compare it to expectedDiffFromResult @@ -136,11 +137,11 @@ func TestUTXODiffRules(t *testing.T) { { name: "one toAdd in this, one toAdd in other", this: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, other: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, expectedDiffFromResult: &UTXODiff{ @@ -152,12 +153,12 @@ func TestUTXODiffRules(t *testing.T) { { name: "one toAdd in this, one toRemove in other", this: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, other: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, expectedDiffFromResult: nil, expectedWithDiffResult: &UTXODiff{ @@ -168,7 +169,7 @@ func TestUTXODiffRules(t *testing.T) { { name: "one toAdd in this, empty other", this: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, other: &UTXODiff{ @@ -177,10 +178,10 @@ func TestUTXODiffRules(t *testing.T) { }, expectedDiffFromResult: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, expectedWithDiffResult: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, }, @@ -188,10 +189,10 @@ func TestUTXODiffRules(t *testing.T) { name: "one toRemove in this, one toAdd in other", this: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, other: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, expectedDiffFromResult: nil, @@ -204,11 +205,11 @@ func TestUTXODiffRules(t *testing.T) { name: "one toRemove in this, one toRemove in other", this: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, other: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, expectedDiffFromResult: &UTXODiff{ toAdd: utxoCollection{}, @@ -220,19 +221,19 @@ func TestUTXODiffRules(t *testing.T) { name: "one toRemove in this, empty other", this: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, other: &UTXODiff{ toAdd: utxoCollection{}, toRemove: utxoCollection{}, }, expectedDiffFromResult: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, expectedWithDiffResult: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, }, { @@ -242,15 +243,15 @@ func TestUTXODiffRules(t *testing.T) { toRemove: utxoCollection{}, }, other: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, expectedDiffFromResult: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, expectedWithDiffResult: &UTXODiff{ - toAdd: utxoCollection{outpoint0: utxoEntry0}, + toAdd: utxoCollection{outpoint0: utxoEntry1}, toRemove: utxoCollection{}, }, }, @@ -262,15 +263,15 @@ func TestUTXODiffRules(t *testing.T) { }, other: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, expectedDiffFromResult: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, expectedWithDiffResult: &UTXODiff{ toAdd: utxoCollection{}, - toRemove: utxoCollection{outpoint0: utxoEntry0}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, }, }, { @@ -292,6 +293,108 @@ func TestUTXODiffRules(t *testing.T) { toRemove: utxoCollection{}, }, }, + { + name: "equal outpoints different blue scores: first in toAdd in this, second in toAdd in other", + this: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{}, + }, + other: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry2}, + toRemove: utxoCollection{}, + }, + expectedDiffFromResult: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry2}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, + }, + expectedWithDiffResult: nil, + }, + { + name: "equal outpoints different blue scores: first in toRemove in this, second in toRemove in other", + this: &UTXODiff{ + toAdd: utxoCollection{}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, + }, + other: &UTXODiff{ + toAdd: utxoCollection{}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + expectedDiffFromResult: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + expectedWithDiffResult: nil, + }, + { + name: "equal outpoints different blue scores: first in toAdd and second in toRemove in this, empty other", + this: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + other: &UTXODiff{ + toAdd: utxoCollection{}, + toRemove: utxoCollection{}, + }, + expectedDiffFromResult: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry2}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, + }, + expectedWithDiffResult: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + }, + { + name: "equal outpoints different blue scores: empty this, first in toAdd and second in toRemove in other", + this: &UTXODiff{ + toAdd: utxoCollection{}, + toRemove: utxoCollection{}, + }, + other: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + expectedDiffFromResult: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + expectedWithDiffResult: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + }, + { + name: "equal outpoints different blue scores: first in toAdd and second in toRemove in both this and other", + this: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + other: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + expectedDiffFromResult: &UTXODiff{ + toAdd: utxoCollection{}, + toRemove: utxoCollection{}, + }, + expectedWithDiffResult: nil, + }, + { + name: "equal outpoints different blue scores: first in toAdd in this and toRemove in other, second in toRemove in this and toAdd in other", + this: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry1}, + toRemove: utxoCollection{outpoint0: utxoEntry2}, + }, + other: &UTXODiff{ + toAdd: utxoCollection{outpoint0: utxoEntry2}, + toRemove: utxoCollection{outpoint0: utxoEntry1}, + }, + expectedDiffFromResult: nil, + expectedWithDiffResult: &UTXODiff{ + toAdd: utxoCollection{}, + toRemove: utxoCollection{}, + }, + }, } for _, test := range tests { @@ -317,6 +420,18 @@ func TestUTXODiffRules(t *testing.T) { "Expected: \"%v\", got: \"%v\".", test.name, expectedDiffFromResult, diffResult) } + // Make sure that WithDiff after diffFrom results in the original other + if isDiffFromOk { + otherResult, err := this.WithDiff(diffResult) + if err != nil { + t.Errorf("WithDiff unexpectedly failed in test \"%s\": %s", test.name, err) + } + if !other.equal(otherResult) { + t.Errorf("unexpected WithDiff result in test \"%s\". "+ + "Expected: \"%v\", got: \"%v\".", test.name, other, otherResult) + } + } + // WithDiff from this to other withDiffResult, err := this.WithDiff(other) @@ -333,6 +448,18 @@ func TestUTXODiffRules(t *testing.T) { t.Errorf("unexpected WithDiff result in test \"%s\". "+ "Expected: \"%v\", got: \"%v\".", test.name, expectedWithDiffResult, withDiffResult) } + + // Make sure that diffFrom after WithDiff results in the original other + if isWithDiffOk { + otherResult, err := this.diffFrom(withDiffResult) + if err != nil { + t.Errorf("diffFrom unexpectedly failed in test \"%s\": %s", test.name, err) + } + if !other.equal(otherResult) { + t.Errorf("unexpected diffFrom result in test \"%s\". "+ + "Expected: \"%v\", got: \"%v\".", test.name, other, otherResult) + } + } } } @@ -343,6 +470,10 @@ func areMultisetsEqual(a *btcec.Multiset, b *btcec.Multiset) bool { } func (d *UTXODiff) equal(other *UTXODiff) bool { + if d == nil || other == nil { + return d == other + } + return reflect.DeepEqual(d.toAdd, other.toAdd) && reflect.DeepEqual(d.toRemove, other.toRemove) && areMultisetsEqual(d.diffMultiset, other.diffMultiset) @@ -554,7 +685,7 @@ func TestDiffUTXOSet(t *testing.T) { toRemove: utxoCollection{}, }, }, - expectedString: "{Base: [ ], To Add: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10 ], To Remove: [ ], Multiset-Hash:da4768bd0359c3426268d6707c1fc17a68c45ef1ea734331b07568418234487f}", + expectedString: "{Base: [ ], To Add: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Remove: [ ], Multiset-Hash:da4768bd0359c3426268d6707c1fc17a68c45ef1ea734331b07568418234487f}", expectedCollection: utxoCollection{outpoint0: utxoEntry0}, }, { @@ -567,7 +698,7 @@ func TestDiffUTXOSet(t *testing.T) { }, }, expectedMeldSet: nil, - expectedString: "{Base: [ ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10 ], Multiset-Hash:046242cb1bb1e6d3fd91d0f181e1b2d4a597ac57fa2584fc3c2eb0e0f46c9369}", + expectedString: "{Base: [ ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], Multiset-Hash:046242cb1bb1e6d3fd91d0f181e1b2d4a597ac57fa2584fc3c2eb0e0f46c9369}", expectedCollection: utxoCollection{}, expectedMeldToBaseError: "Couldn't remove outpoint 0000000000000000000000000000000000000000000000000000000000000000:0 because it doesn't exist in the DiffUTXOSet base", }, @@ -592,7 +723,7 @@ func TestDiffUTXOSet(t *testing.T) { toRemove: utxoCollection{}, }, }, - expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10 ], To Add: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20 ], To Remove: [ ], Multiset-Hash:556cc61fd4d7e74d7807ca2298c5320375a6a20310a18920e54667220924baff}", + expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], To Remove: [ ], Multiset-Hash:556cc61fd4d7e74d7807ca2298c5320375a6a20310a18920e54667220924baff}", expectedCollection: utxoCollection{ outpoint0: utxoEntry0, outpoint1: utxoEntry1, @@ -616,7 +747,7 @@ func TestDiffUTXOSet(t *testing.T) { toRemove: utxoCollection{}, }, }, - expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10 ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10 ], Multiset-Hash:0000000000000000000000000000000000000000000000000000000000000000}", + expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], Multiset-Hash:0000000000000000000000000000000000000000000000000000000000000000}", expectedCollection: utxoCollection{}, }, } @@ -903,7 +1034,7 @@ func TestDiffFromTx(t *testing.T) { } else if !isAccepted { t.Fatalf("AddTx unexpectedly didn't add tx %s", cbTx.TxID()) } - node := &blockNode{blueScore: 2} //Fake node + acceptingBlueScore := uint64(2) cbOutpoint := wire.Outpoint{TxID: *cbTx.TxID(), Index: 0} txIns := []*wire.TxIn{{ PreviousOutpoint: cbOutpoint, @@ -915,7 +1046,7 @@ func TestDiffFromTx(t *testing.T) { Value: uint64(1), }} tx := wire.NewNativeMsgTx(wire.TxVersion, txIns, txOuts) - diff, err := fus.diffFromTx(tx, node) + diff, err := fus.diffFromTx(tx, acceptingBlueScore) if err != nil { t.Errorf("diffFromTx: %v", err) } @@ -942,7 +1073,7 @@ func TestDiffFromTx(t *testing.T) { Value: uint64(1), }} invalidTx := wire.NewNativeMsgTx(wire.TxVersion, invalidTxIns, invalidTxOuts) - _, err = fus.diffFromTx(invalidTx, node) + _, err = fus.diffFromTx(invalidTx, acceptingBlueScore) if err == nil { t.Errorf("diffFromTx: expected an error but got ") } @@ -958,7 +1089,7 @@ func TestDiffFromTx(t *testing.T) { } else if !isAccepted { t.Fatalf("AddTx unexpectedly didn't add tx %s", tx.TxID()) } - _, err = dus.diffFromTx(tx, node) + _, err = dus.diffFromTx(tx, acceptingBlueScore) if err == nil { t.Errorf("diffFromTx: expected an error but got ") } diff --git a/blockdag/validate_test.go b/blockdag/validate_test.go index fe744fbb5..54216263b 100644 --- a/blockdag/validate_test.go +++ b/blockdag/validate_test.go @@ -79,8 +79,8 @@ func TestCheckConnectBlockTemplate(t *testing.T) { defer teardownFunc() // Since we're not dealing with the real block DAG, set the coinbase - // maturity to 1. - dag.TestSetCoinbaseMaturity(1) + // maturity to 0. + dag.TestSetCoinbaseMaturity(0) // Load up blocks such that there is a side chain. // (genesis block) -> 1 -> 2 -> 3 -> 4 diff --git a/mempool/mempool.go b/mempool/mempool.go index 9a7fa2904..53d6aeb29 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -591,7 +591,7 @@ func (mp *TxPool) markTransactionOutputsUnspent(tx *util.Tx, diff *blockdag.UTXO if restoreInputs { if prevTxDesc, exists := mp.pool[txIn.PreviousOutpoint.TxID]; exists { prevOut := prevTxDesc.Tx.MsgTx().TxOut[txIn.PreviousOutpoint.Index] - entry := blockdag.NewUTXOEntry(prevOut, false, blockdag.UnminedBlueScore) + entry := blockdag.NewUTXOEntry(prevOut, false, blockdag.UnacceptedBlueScore) err := diff.AddEntry(txIn.PreviousOutpoint, entry) if err != nil { return err @@ -599,7 +599,7 @@ func (mp *TxPool) markTransactionOutputsUnspent(tx *util.Tx, diff *blockdag.UTXO } if prevTxDesc, exists := mp.depends[txIn.PreviousOutpoint.TxID]; exists { prevOut := prevTxDesc.Tx.MsgTx().TxOut[txIn.PreviousOutpoint.Index] - entry := blockdag.NewUTXOEntry(prevOut, false, blockdag.UnminedBlueScore) + entry := blockdag.NewUTXOEntry(prevOut, false, blockdag.UnacceptedBlueScore) err := diff.AddEntry(txIn.PreviousOutpoint, entry) if err != nil { return err @@ -716,7 +716,7 @@ func (mp *TxPool) addTransaction(tx *util.Tx, height uint64, blueScore uint64, f for _, txIn := range tx.MsgTx().TxIn { mp.outpoints[txIn.PreviousOutpoint] = tx } - if isAccepted, err := mp.mpUTXOSet.AddTx(tx.MsgTx(), blockdag.UnminedBlueScore); err != nil { + if isAccepted, err := mp.mpUTXOSet.AddTx(tx.MsgTx(), blockdag.UnacceptedBlueScore); err != nil { return nil, err } else if !isAccepted { return nil, fmt.Errorf("unexpectedly failed to add tx %s to the mempool utxo set", tx.ID()) diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 55a9673cc..207f8a6bb 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -126,7 +126,7 @@ func (p *poolHarness) CreateCoinbaseTx(blueScore uint64, numOutputs uint32) (*ut return nil, err } - txIns := []*wire.TxIn{&wire.TxIn{ + txIns := []*wire.TxIn{{ // Coinbase transactions have no inputs, so previous outpoint is // zero hash and max index. PreviousOutpoint: *wire.NewOutpoint(&daghash.TxID{}, @@ -1816,7 +1816,7 @@ var dummyBlock = wire.MsgBlock{ func TestTransactionGas(t *testing.T) { params := dagconfig.SimNetParams - params.BlockCoinbaseMaturity = 1 + params.BlockCoinbaseMaturity = 0 tc, spendableOuts, teardownFunc, err := newPoolHarness(t, ¶ms, 6, "TestTransactionGas") if err != nil { t.Fatalf("unable to create test pool: %v", err) diff --git a/mining/cpuminer/cpuminer.go b/mining/cpuminer/cpuminer.go index 00a8481d0..17a02996a 100644 --- a/mining/cpuminer/cpuminer.go +++ b/mining/cpuminer/cpuminer.go @@ -204,8 +204,7 @@ func (m *CPUMiner) submitBlock(block *util.Block) bool { // This function will return early with false when conditions that trigger a // stale block such as a new block showing up or periodically when there are // new transactions and enough time has elapsed without finding a solution. -func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blueScore uint64, - ticker *time.Ticker, quit chan struct{}) bool { +func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit chan struct{}) bool { // Create some convenience variables. header := &msgBlock.Header @@ -226,7 +225,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blueScore uint64, // Update the extra nonce in the block template with the // new value by regenerating the coinbase script and // setting the merkle root to the new value. - m.g.UpdateExtraNonce(msgBlock, blueScore, extraNonce) + m.g.UpdateExtraNonce(msgBlock, extraNonce) // Search through the entire nonce range for a solution while // periodically checking for early quit and stale block @@ -345,7 +344,7 @@ out: // with false when conditions that trigger a stale block, so // a new block template can be generated. When the return is // true a solution was found, so submit the solved block. - if m.solveBlock(template.Block, currentBlueScore+1, ticker, quit) { + if m.solveBlock(template.Block, ticker, quit) { block := util.NewBlock(template.Block) m.submitBlock(block) } @@ -579,7 +578,6 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*daghash.Hash, error) { // be changing and this would otherwise end up building a new block // template on a block that is in the process of becoming stale. m.submitBlockLock.Lock() - currentBlueScore := m.g.VirtualBlueScore() // Choose a payment address at random. rand.Seed(time.Now().UnixNano()) @@ -601,7 +599,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*daghash.Hash, error) { // with false when conditions that trigger a stale block, so // a new block template can be generated. When the return is // true a solution was found, so submit the solved block. - if m.solveBlock(template.Block, currentBlueScore, ticker, nil) { + if m.solveBlock(template.Block, ticker, nil) { block := util.NewBlock(template.Block) m.submitBlock(block) blockHashes[i] = block.Hash() diff --git a/mining/mining.go b/mining/mining.go index 20de285fb..e486c3db5 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -529,7 +529,7 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe for _, tx := range blockTxns { msgBlock.AddTransaction(tx.MsgTx()) } - utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, nextBlockBlueScore) + utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions) if err != nil { return nil, err } @@ -582,11 +582,12 @@ func CoinbasePayloadExtraData(extraNonce uint64) ([]byte, error) { return w.Bytes(), nil } -func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx, nextBlueScore uint64) (*daghash.Hash, error) { - utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, nextBlueScore, false) +func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx) (*daghash.Hash, error) { + utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, blockdag.UnacceptedBlueScore, false) if err != nil { return nil, err } + return utxoWithTransactions.Multiset().Hash(), nil } @@ -611,7 +612,7 @@ func (g *BlkTmplGenerator) UpdateBlockTime(msgBlock *wire.MsgBlock) error { // block by regenerating the coinbase script with the passed value and block // height. It also recalculates and updates the new merkle root that results // from changing the coinbase script. -func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockBlueScore uint64, extraNonce uint64) error { +func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, extraNonce uint64) error { coinbasePayloadPkScript, _, err := blockdag.DeserializeCoinbasePayload(msgBlock.Transactions[util.CoinbaseTransactionIndex]) if err != nil { return err @@ -643,7 +644,7 @@ func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockBlueSc hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions()) msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root() - utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, blockBlueScore) + utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions) if err != nil { return err } diff --git a/mining/test_utils.go b/mining/test_utils.go index ef3130ca5..7911bd308 100644 --- a/mining/test_utils.go +++ b/mining/test_utils.go @@ -73,7 +73,7 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren } // In order of creating deterministic coinbase tx ids. - err = blockTemplateGenerator.UpdateExtraNonce(template.Block, dag.VirtualBlueScore(), GenerateDeterministicExtraNonceForTest()) + err = blockTemplateGenerator.UpdateExtraNonce(template.Block, GenerateDeterministicExtraNonceForTest()) if err != nil { return nil, err } @@ -107,7 +107,7 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren } template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root() - template.Block.Header.UTXOCommitment, err = blockTemplateGenerator.buildUTXOCommitment(template.Block.Transactions, dag.VirtualBlueScore()) + template.Block.Header.UTXOCommitment, err = blockTemplateGenerator.buildUTXOCommitment(template.Block.Transactions) if err != nil { return nil, err }