[NOD-222] Use accepting block blue score instead of containing block blue score for sequence lock and block maturity (#333)

* [NOD-222] Added constant: UnacceptedBlueScore.

* [NOD-222] Made it so that block transactions always have UnacceptedBlueScore.

* [NOD-222] Implemented updating unaccepted UTXO entries with accepted ones in the virtual.

* [NOD-222] Fixed an unclear comment.

* [NOD-222] Fixed diffFromAcceptanceData not receiving the right blue score.

* [NOD-222] Fixed various issues with the implementation. It appears to work now.

* [NOD-222] Removed debug logs.

* [NOD-222] Fixed tests that relied on utxoCollection.String().

* [NOD-222] Fixed TestChainedTransactions.

* [NOD-222] Fixed tests that relied on GetVirtualFromParentsForTest.

* [NOD-222] Fixed having identical entries in toAdd and toRemove.

* [NOD-222] Fixed logic in diffFrom that I previously broke.

* [NOD-222] Fixed a wrong check.

* [NOD-222] Figured out the magical invocation to make everything work.

* [NOD-222] Fixed blockDB tests.

* [NOD-222] Removed debug method.

* [NOD-222] Fixed comments related to setting coinbase maturity to 0.

* [NOD-222] Fixed a typo in a comment.

* [NOD-222] Added a comment that explains the new addition in GetVirtualFromParentsForTest.

* [NOD-222] Added a comment to DiffUTXOSet.Get().

* [NOD-222] Fixed a nuance in DiffUTXOSet.containsInputs.

* [NOD-222] Replaced nonsense in GetVirtualFromParentsForTest with diffFromAcceptanceData.

* [NOD-222] Renamed newVirtualUTXO -> newVirtualPastUTXO.

* [NOD-222] Fixed a comment.

* [NOD-222] Extracted checking utxoCollection with blueScore to a method.

* [NOD-222] Added tests where the same entry is in both toAdd and toRemove.

* [NOD-222] Used Add/RemoveEntry inside diffFromAcceptedTx.

* [NOD-222] Removed superfluous test for UnacceptedBlueScore.

* [NOD-222] Added/Updated comments.

* [NOD-222] Added tests to TestUTXODiffRules.

* [NOD-222] Added appropriate protection against impossible "from"s in diffFrom.

* [NOD-222] Added a comment explaining why we diffFrom acceptanceData in verifyAndBuildUTXO.

* [NOD-222] Fixed comments and equal() in utxoset.
This commit is contained in:
stasatdaglabs 2019-07-02 16:28:54 +03:00 committed by Ori Newman
parent d6297a3192
commit 6d20202354
19 changed files with 389 additions and 114 deletions

View File

@ -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"

View File

@ -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)
}

View File

@ -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)

View File

@ -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: &params,
})
@ -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: &params,
@ -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: &params,
})

View File

@ -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{

View File

@ -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
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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 {

View File

@ -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 <nil>")
}
@ -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 <nil>")
}

View File

@ -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

View File

@ -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())

View File

@ -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, &params, 6, "TestTransactionGas")
if err != nil {
t.Fatalf("unable to create test pool: %v", err)

View File

@ -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()

View File

@ -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
}

View File

@ -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
}