mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 22:26:47 +00:00
[NOD-180] Add validation of utxo commitments (#310)
* [NOD-172] Port EMCH from bchd * [NOD-172] Fix hdkeychain.TestErrors and add btcec.TestRecoverCompact * [NOD-172] Make ECMH immutable * [NOD-172] Fix gofmt errors * [NOD-172] Add TestMultiset_NewMultisetFromDataSlice and fix Point to be immutable * [NOD-172] Fix gofmt errors * [NOD-172] Add test for checking that the Union of a multiset and its inverse is zero * [NOD-179] Add ECMH Point to all UTXO-structs * [NOD-179] Fix utxo set tests * [NOD-179] Fix mempool tests * [NOD-179] Remove RemoveTxOuts * [NOD-179] Move serializeBlockUTXODiffData to the top of the file * [NOD-179] Fix serializeBlockUTXODiffData comment format * [NOD-179] Fix AddTx comment and name return values * [NOD-180] Validate utxo commitments * [NOD-179] Fix TestAcceptingBlock and TestConfirmations to not use the block hash as phantom break even * [NOD-180] Fix typo * [NOD-180] move most of the logic in calcUTXOCommitment to UTXOSet.WithTransactions * [NOD-180] Optionally return error when a transaction in WithTransactions is double spent * [NOD-180] Rename allowDoubleSpends to ignoreDoubleSpends
This commit is contained in:
parent
aa51b5f071
commit
7069d173c6
@ -898,6 +898,14 @@ func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculatedMultisetHash := utxo.Multiset().Hash()
|
||||||
|
if !calculatedMultisetHash.IsEqual(node.utxoCommitment) {
|
||||||
|
str := fmt.Sprintf("block UTXO commitment is invalid - block "+
|
||||||
|
"header indicates %s, but calculated value is %s",
|
||||||
|
node.utxoCommitment, calculatedMultisetHash)
|
||||||
|
return nil, nil, nil, ruleError(ErrBadUTXOCommitment, str)
|
||||||
|
}
|
||||||
return utxo, txsAcceptanceData, feeData, nil
|
return utxo, txsAcceptanceData, feeData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ package blockdag
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -25,7 +24,6 @@ import (
|
|||||||
"github.com/daglabs/btcd/util"
|
"github.com/daglabs/btcd/util"
|
||||||
"github.com/daglabs/btcd/util/daghash"
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
"github.com/daglabs/btcd/util/subnetworkid"
|
"github.com/daglabs/btcd/util/subnetworkid"
|
||||||
"github.com/daglabs/btcd/util/txsort"
|
|
||||||
"github.com/daglabs/btcd/wire"
|
"github.com/daglabs/btcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -191,7 +189,7 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
|
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
|
||||||
|
|
||||||
// Block 3b should be present (as a second child of Block 2).
|
// Block 3b should be present (as a second child of Block 2).
|
||||||
{hash: "3f2ded16b7115e69a48cee5f4be743ff23ad8d41da16d059c38cc83d14459863", want: true},
|
{hash: "4bb2e2f55fabd67e217126dbc41e7101d0d6058800368c428cd6e397c111ee47", want: true},
|
||||||
|
|
||||||
// Block 100000 should be present (as an orphan).
|
// Block 100000 should be present (as an orphan).
|
||||||
{hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true},
|
{hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true},
|
||||||
@ -803,7 +801,7 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
|
|||||||
}
|
}
|
||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
// Since we're not dealing with the real block chain, set the block reward
|
// Since we're not dealing with the real block DAG, set the block reward
|
||||||
// maturity to 1.
|
// maturity to 1.
|
||||||
dag.TestSetBlockRewardMaturity(1)
|
dag.TestSetBlockRewardMaturity(1)
|
||||||
|
|
||||||
@ -819,7 +817,6 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
|
|||||||
"is an orphan\n", i)
|
"is an orphan\n", i)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("ERROR %v\n", err)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -875,390 +872,12 @@ func TestNew(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateFeeTransaction(t *testing.T) {
|
|
||||||
params := dagconfig.SimNetParams
|
|
||||||
params.K = 1
|
|
||||||
dag, teardownFunc, err := DAGSetup("TestValidateFeeTransaction", Config{
|
|
||||||
DAGParams: ¶ms,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
|
||||||
}
|
|
||||||
defer teardownFunc()
|
|
||||||
extraNonce := int64(0)
|
|
||||||
createCoinbase := func(pkScript []byte) *wire.MsgTx {
|
|
||||||
extraNonce++
|
|
||||||
cbTx, err := createCoinbaseTxForTest(dag.Height()+1, 2, extraNonce, ¶ms)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("createCoinbaseTxForTest: %v", err)
|
|
||||||
}
|
|
||||||
if pkScript != nil {
|
|
||||||
cbTx.TxOut[0].PkScript = pkScript
|
|
||||||
}
|
|
||||||
return cbTx
|
|
||||||
}
|
|
||||||
|
|
||||||
var flags BehaviorFlags
|
|
||||||
flags |= BFFastAdd | BFNoPoWCheck
|
|
||||||
|
|
||||||
buildBlock := func(blockName string, parentHashes []*daghash.Hash, transactions []*wire.MsgTx, expectedErrorCode ErrorCode) *wire.MsgBlock {
|
|
||||||
utilTxs := make([]*util.Tx, len(transactions))
|
|
||||||
for i, tx := range transactions {
|
|
||||||
utilTxs[i] = util.NewTx(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
newVirtual, err := GetVirtualFromParentsForTest(dag, parentHashes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("block %v: unexpected error when setting virtual for test: %v", blockName, err)
|
|
||||||
}
|
|
||||||
oldVirtual := SetVirtualForTest(dag, newVirtual)
|
|
||||||
acceptedIDMerkleRoot, err := dag.NextAcceptedIDMerkleRoot()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("block %v: unexpected error when getting next acceptIDMerkleRoot: %v", blockName, err)
|
|
||||||
}
|
|
||||||
SetVirtualForTest(dag, oldVirtual)
|
|
||||||
|
|
||||||
daghash.Sort(parentHashes)
|
|
||||||
msgBlock := &wire.MsgBlock{
|
|
||||||
Header: wire.BlockHeader{
|
|
||||||
Bits: dag.genesis.Header().Bits,
|
|
||||||
ParentHashes: parentHashes,
|
|
||||||
HashMerkleRoot: BuildHashMerkleTreeStore(utilTxs).Root(),
|
|
||||||
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
|
||||||
UTXOCommitment: &daghash.ZeroHash,
|
|
||||||
},
|
|
||||||
Transactions: transactions,
|
|
||||||
}
|
|
||||||
block := util.NewBlock(msgBlock)
|
|
||||||
isOrphan, err := dag.ProcessBlock(block, flags)
|
|
||||||
if expectedErrorCode != 0 {
|
|
||||||
checkResult := checkRuleError(err, RuleError{
|
|
||||||
ErrorCode: expectedErrorCode,
|
|
||||||
})
|
|
||||||
if checkResult != nil {
|
|
||||||
t.Errorf("block %v: unexpected error code: %v", blockName, checkResult)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("block %v: unexpected error: %v", blockName, err)
|
|
||||||
}
|
|
||||||
if isOrphan {
|
|
||||||
t.Errorf("block %v unexpectely got orphaned", blockName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return msgBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
cb1 := createCoinbase(nil)
|
|
||||||
blockWithExtraFeeTxTransactions := []*wire.MsgTx{
|
|
||||||
cb1,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*dag.genesis.hash),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
{ // Extra Fee Transaction
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*dag.genesis.hash),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
buildBlock("blockWithExtraFeeTx", []*daghash.Hash{dag.genesis.hash}, blockWithExtraFeeTxTransactions, ErrMultipleFeeTransactions)
|
|
||||||
|
|
||||||
block1Txs := []*wire.MsgTx{
|
|
||||||
cb1,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*dag.genesis.hash),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block1 := buildBlock("block1", []*daghash.Hash{dag.genesis.hash}, block1Txs, 0)
|
|
||||||
|
|
||||||
cb1A := createCoinbase(nil)
|
|
||||||
block1ATxs := []*wire.MsgTx{
|
|
||||||
cb1A,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*dag.genesis.hash),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block1A := buildBlock("block1A", []*daghash.Hash{dag.genesis.hash}, block1ATxs, 0)
|
|
||||||
|
|
||||||
block1AChildCbPkScript, err := payToPubKeyHashScript((&[20]byte{0x1A, 0xC0})[:])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("payToPubKeyHashScript: %v", err)
|
|
||||||
}
|
|
||||||
cb1AChild := createCoinbase(block1AChildCbPkScript)
|
|
||||||
block1AChildTxs := []*wire.MsgTx{
|
|
||||||
cb1AChild,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block1A.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: *cb1A.TxID(),
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
PkScript: OpTrueScript,
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block1AChild := buildBlock("block1AChild", []*daghash.Hash{block1A.BlockHash()}, block1AChildTxs, 0)
|
|
||||||
|
|
||||||
cb2 := createCoinbase(nil)
|
|
||||||
block2Txs := []*wire.MsgTx{
|
|
||||||
cb2,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block1.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block2 := buildBlock("block2", []*daghash.Hash{block1.BlockHash()}, block2Txs, 0)
|
|
||||||
|
|
||||||
cb3 := createCoinbase(nil)
|
|
||||||
block3Txs := []*wire.MsgTx{
|
|
||||||
cb3,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block2.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block3 := buildBlock("block3", []*daghash.Hash{block2.BlockHash()}, block3Txs, 0)
|
|
||||||
|
|
||||||
block4CbPkScript, err := payToPubKeyHashScript((&[20]byte{0x40})[:])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("payToPubKeyHashScript: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cb4 := createCoinbase(block4CbPkScript)
|
|
||||||
block4Txs := []*wire.MsgTx{
|
|
||||||
cb4,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block3.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: *cb3.TxID(),
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
PkScript: OpTrueScript,
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block4 := buildBlock("block4", []*daghash.Hash{block3.BlockHash()}, block4Txs, 0)
|
|
||||||
|
|
||||||
block4ACbPkScript, err := payToPubKeyHashScript((&[20]byte{0x4A})[:])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("payToPubKeyHashScript: %v", err)
|
|
||||||
}
|
|
||||||
cb4A := createCoinbase(block4ACbPkScript)
|
|
||||||
block4ATxs := []*wire.MsgTx{
|
|
||||||
cb4A,
|
|
||||||
{ // Fee Transaction
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block3.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: *cb3.TxID(),
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
PkScript: OpTrueScript,
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SubnetworkID: *subnetworkid.SubnetworkIDNative,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
block4A := buildBlock("block4A", []*daghash.Hash{block3.BlockHash()}, block4ATxs, 0)
|
|
||||||
|
|
||||||
cb5 := createCoinbase(nil)
|
|
||||||
feeInOuts := map[daghash.Hash]*struct {
|
|
||||||
txIn *wire.TxIn
|
|
||||||
txOut *wire.TxOut
|
|
||||||
}{
|
|
||||||
*block4.BlockHash(): {
|
|
||||||
txIn: &wire.TxIn{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block4.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
txOut: &wire.TxOut{
|
|
||||||
PkScript: block4CbPkScript,
|
|
||||||
Value: cb3.TxOut[0].Value - 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
*block4A.BlockHash(): {
|
|
||||||
txIn: &wire.TxIn{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block4A.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
},
|
|
||||||
txOut: &wire.TxOut{
|
|
||||||
PkScript: block4ACbPkScript,
|
|
||||||
Value: cb3.TxOut[1].Value - 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
txIns := []*wire.TxIn{}
|
|
||||||
txOuts := []*wire.TxOut{}
|
|
||||||
for hash := range feeInOuts {
|
|
||||||
txIns = append(txIns, feeInOuts[hash].txIn)
|
|
||||||
txOuts = append(txOuts, feeInOuts[hash].txOut)
|
|
||||||
}
|
|
||||||
block5FeeTx := wire.NewNativeMsgTx(1, txIns, txOuts)
|
|
||||||
sortedBlock5FeeTx := txsort.Sort(block5FeeTx)
|
|
||||||
|
|
||||||
block5Txs := []*wire.MsgTx{cb5, sortedBlock5FeeTx}
|
|
||||||
|
|
||||||
block5ParentHashes := []*daghash.Hash{block4.BlockHash(), block4A.BlockHash()}
|
|
||||||
buildBlock("block5", block5ParentHashes, block5Txs, 0)
|
|
||||||
|
|
||||||
sortedBlock5FeeTx.TxIn[0], sortedBlock5FeeTx.TxIn[1] = sortedBlock5FeeTx.TxIn[1], sortedBlock5FeeTx.TxIn[0]
|
|
||||||
buildBlock("block5WrongOrder", block5ParentHashes, block5Txs, ErrBadFeeTransaction)
|
|
||||||
|
|
||||||
block5FeeTxWith1Achild := block5FeeTx.Copy()
|
|
||||||
|
|
||||||
block5FeeTxWith1Achild.AddTxIn(&wire.TxIn{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
TxID: daghash.TxID(*block1AChild.BlockHash()),
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
})
|
|
||||||
block5FeeTxWith1Achild.AddTxOut(&wire.TxOut{
|
|
||||||
PkScript: block1AChildCbPkScript,
|
|
||||||
Value: cb1AChild.TxOut[0].Value - 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
sortedBlock5FeeTxWith1Achild := txsort.Sort(block5FeeTxWith1Achild)
|
|
||||||
|
|
||||||
block5Txs[1] = sortedBlock5FeeTxWith1Achild
|
|
||||||
buildBlock("block5WithRedBlockFees", block5ParentHashes, block5Txs, ErrBadFeeTransaction)
|
|
||||||
|
|
||||||
block5FeeTxWithWrongFees := block5FeeTx.Copy()
|
|
||||||
block5FeeTxWithWrongFees.TxOut[0].Value--
|
|
||||||
sortedBlock5FeeTxWithWrongFees := txsort.Sort(block5FeeTxWithWrongFees)
|
|
||||||
block5Txs[1] = sortedBlock5FeeTxWithWrongFees
|
|
||||||
buildBlock("block5WithRedBlockFees", block5ParentHashes, block5Txs, ErrBadFeeTransaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfirmations(t *testing.T) {
|
func TestConfirmations(t *testing.T) {
|
||||||
// Create a new database and DAG instance to run tests against.
|
// Create a new database and DAG instance to run tests against.
|
||||||
|
params := dagconfig.SimNetParams
|
||||||
|
params.K = 1
|
||||||
dag, teardownFunc, err := DAGSetup("TestBlockCount", Config{
|
dag, teardownFunc, err := DAGSetup("TestBlockCount", Config{
|
||||||
DAGParams: &dagconfig.SimNetParams,
|
DAGParams: ¶ms,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||||
@ -1275,29 +894,17 @@ func TestConfirmations(t *testing.T) {
|
|||||||
t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. Want: 1, got: %d", genesisConfirmations)
|
t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. Want: 1, got: %d", genesisConfirmations)
|
||||||
}
|
}
|
||||||
|
|
||||||
processBlocks := func(blocks []*util.Block) {
|
|
||||||
for _, block := range blocks {
|
|
||||||
isOrphan, err := dag.ProcessBlock(block, BFNone)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ProcessBlock fail on block %s: %v\n", block.Hash(), err)
|
|
||||||
}
|
|
||||||
if isOrphan {
|
|
||||||
t.Fatalf("ProcessBlock incorrectly returned block %s is an orphan\n", block.Hash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a chain of blocks
|
// Add a chain of blocks
|
||||||
loadedBlocks, err := loadBlocks("blk_0_to_4.dat")
|
chainBlocks := make([]*blockNode, 5)
|
||||||
if err != nil {
|
chainBlocks[0] = dag.genesis
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
buildNode := buildNodeGenerator(dag.dagParams.K, true)
|
||||||
|
for i := uint32(1); i < 5; i++ {
|
||||||
|
chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1]))
|
||||||
|
dag.virtual.AddTip(chainBlocks[i])
|
||||||
}
|
}
|
||||||
chainBlocks := loadedBlocks[1:]
|
|
||||||
processBlocks(chainBlocks)
|
|
||||||
|
|
||||||
// Make sure that each one of the chain blocks has the expected confirmations number
|
// Make sure that each one of the chain blocks has the expected confirmations number
|
||||||
for i, block := range chainBlocks {
|
for i, node := range chainBlocks {
|
||||||
node := dag.index.LookupNode(block.Hash())
|
|
||||||
confirmations, err := dag.confirmations(node)
|
confirmations, err := dag.confirmations(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestConfirmations: confirmations for node 1 unexpectedly failed: %s", err)
|
t.Fatalf("TestConfirmations: confirmations for node 1 unexpectedly failed: %s", err)
|
||||||
@ -1310,48 +917,55 @@ func TestConfirmations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a branching block
|
branchingBlocks := make([]*blockNode, 2)
|
||||||
loadedBlocks, err = loadBlocks("blk_3B.dat")
|
// Add two branching blocks
|
||||||
if err != nil {
|
branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1]))
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
dag.virtual.AddTip(branchingBlocks[0])
|
||||||
}
|
branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0]))
|
||||||
processBlocks(loadedBlocks)
|
dag.virtual.AddTip(branchingBlocks[1])
|
||||||
|
|
||||||
// Check that the genesis has a confirmations number == blockCount
|
// Check that the genesis has a confirmations number == len(chainBlocks)
|
||||||
genesisConfirmations, err = dag.confirmations(dag.genesis)
|
genesisConfirmations, err = dag.confirmations(dag.genesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestConfirmations: confirmations for genesis block unexpectedly failed: %s", err)
|
t.Fatalf("TestConfirmations: confirmations for genesis block unexpectedly failed: %s", err)
|
||||||
}
|
}
|
||||||
expectedGenesisConfirmations := dag.blockCount
|
expectedGenesisConfirmations := uint64(len(chainBlocks))
|
||||||
if genesisConfirmations != expectedGenesisConfirmations {
|
if genesisConfirmations != expectedGenesisConfirmations {
|
||||||
t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. "+
|
t.Fatalf("TestConfirmations: unexpected confirmations for genesis block. "+
|
||||||
"Want: %d, got: %d", expectedGenesisConfirmations, genesisConfirmations)
|
"Want: %d, got: %d", expectedGenesisConfirmations, genesisConfirmations)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that each of the tips had a confirmation number of 1.
|
// Check that each of the blue tips has a confirmation number of 1, and each of the red tips has 0 confirmations.
|
||||||
tips := dag.virtual.tips()
|
tips := dag.virtual.tips()
|
||||||
for _, tip := range tips {
|
for _, tip := range tips {
|
||||||
tipConfirmations, err := dag.confirmations(tip)
|
tipConfirmations, err := dag.confirmations(tip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestConfirmations: confirmations for tip unexpectedly failed: %s", err)
|
t.Fatalf("TestConfirmations: confirmations for tip unexpectedly failed: %s", err)
|
||||||
}
|
}
|
||||||
if tipConfirmations != 1 {
|
acceptingBlock, err := dag.acceptingBlock(tip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("TestConfirmations: dag.acceptingBlock unexpectedly failed: %s", err)
|
||||||
|
}
|
||||||
|
expectedConfirmations := uint64(1)
|
||||||
|
if acceptingBlock == nil {
|
||||||
|
expectedConfirmations = 0
|
||||||
|
}
|
||||||
|
if tipConfirmations != expectedConfirmations {
|
||||||
t.Fatalf("TestConfirmations: unexpected confirmations for tip. "+
|
t.Fatalf("TestConfirmations: unexpected confirmations for tip. "+
|
||||||
"Want: 1, got: %d", tipConfirmations)
|
"Want: %d, got: %d", expectedConfirmations, tipConfirmations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate K blocks to force the "main" chain to become red
|
// Generate 100 blocks to force the "main" chain to become red
|
||||||
nodeGenerator := buildNodeGenerator(dag.dagParams.K, false)
|
branchingChainTip := branchingBlocks[1]
|
||||||
branchingChainTip := dag.index.LookupNode(loadedBlocks[0].Hash())
|
for i := uint32(0); i < 100; i++ {
|
||||||
for i := uint32(0); i < dag.dagParams.K; i++ {
|
nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip))
|
||||||
nextBranchingChainTip := nodeGenerator(setFromSlice(branchingChainTip))
|
|
||||||
dag.virtual.AddTip(nextBranchingChainTip)
|
dag.virtual.AddTip(nextBranchingChainTip)
|
||||||
branchingChainTip = nextBranchingChainTip
|
branchingChainTip = nextBranchingChainTip
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that a red block has confirmation number = 0
|
// Make sure that a red block has confirmation number = 0
|
||||||
redChainBlock := dag.index.LookupNode(chainBlocks[3].Hash())
|
redChainBlock := chainBlocks[3]
|
||||||
redChainBlockConfirmations, err := dag.confirmations(redChainBlock)
|
redChainBlockConfirmations, err := dag.confirmations(redChainBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestConfirmations: confirmations for red chain block unexpectedly failed: %s", err)
|
t.Fatalf("TestConfirmations: confirmations for red chain block unexpectedly failed: %s", err)
|
||||||
@ -1362,7 +976,7 @@ func TestConfirmations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the red tip has confirmation number = 0
|
// Make sure that the red tip has confirmation number = 0
|
||||||
redChainTip := dag.index.LookupNode(chainBlocks[len(chainBlocks)-1].Hash())
|
redChainTip := chainBlocks[len(chainBlocks)-1]
|
||||||
redChainTipConfirmations, err := dag.confirmations(redChainTip)
|
redChainTipConfirmations, err := dag.confirmations(redChainTip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestConfirmations: confirmations for red chain tip unexpectedly failed: %s", err)
|
t.Fatalf("TestConfirmations: confirmations for red chain tip unexpectedly failed: %s", err)
|
||||||
@ -1375,8 +989,10 @@ func TestConfirmations(t *testing.T) {
|
|||||||
|
|
||||||
func TestAcceptingBlock(t *testing.T) {
|
func TestAcceptingBlock(t *testing.T) {
|
||||||
// Create a new database and DAG instance to run tests against.
|
// Create a new database and DAG instance to run tests against.
|
||||||
|
params := dagconfig.SimNetParams
|
||||||
|
params.K = 1
|
||||||
dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{
|
dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{
|
||||||
DAGParams: &dagconfig.SimNetParams,
|
DAGParams: ¶ms,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||||
@ -1394,34 +1010,20 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
"Want: virtual, got: %s", genesisAcceptingBlock.hash)
|
"Want: virtual, got: %s", genesisAcceptingBlock.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
processBlocks := func(blocks []*util.Block) {
|
chainBlocks := make([]*blockNode, 5)
|
||||||
for _, block := range blocks {
|
chainBlocks[0] = dag.genesis
|
||||||
isOrphan, err := dag.ProcessBlock(block, BFNone)
|
buildNode := buildNodeGenerator(dag.dagParams.K, true)
|
||||||
if err != nil {
|
for i := uint32(1); i <= 4; i++ {
|
||||||
t.Fatalf("ProcessBlock fail on block %s: %v\n", block.Hash(), err)
|
chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1]))
|
||||||
}
|
dag.virtual.AddTip(chainBlocks[i])
|
||||||
if isOrphan {
|
|
||||||
t.Fatalf("ProcessBlock incorrectly returned block %s is an orphan\n", block.Hash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a chain of blocks
|
|
||||||
chainBlocks, err := loadBlocks("blk_0_to_4.dat")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
|
||||||
}
|
|
||||||
processBlocks(chainBlocks[1:])
|
|
||||||
|
|
||||||
// Make sure that each chain block (including the genesis) is accepted by its child
|
// Make sure that each chain block (including the genesis) is accepted by its child
|
||||||
for i, chainBlock := range chainBlocks[:1] {
|
for i, chainBlockNode := range chainBlocks[:len(chainBlocks)-1] {
|
||||||
expectedAcceptingBlock := chainBlocks[i+1]
|
expectedAcceptingBlockNode := chainBlocks[i+1]
|
||||||
expectedAcceptingBlockNode := dag.index.LookupNode(expectedAcceptingBlock.Hash())
|
|
||||||
|
|
||||||
chainBlockNode := dag.index.LookupNode(chainBlock.Hash())
|
|
||||||
chainAcceptingBlockNode, err := dag.acceptingBlock(chainBlockNode)
|
chainAcceptingBlockNode, err := dag.acceptingBlock(chainBlockNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for chain block unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for chain block %d unexpectedly failed: %s", i, err)
|
||||||
}
|
}
|
||||||
if expectedAcceptingBlockNode != chainAcceptingBlockNode {
|
if expectedAcceptingBlockNode != chainAcceptingBlockNode {
|
||||||
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for chain block. "+
|
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for chain block. "+
|
||||||
@ -1429,16 +1031,16 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a branching block
|
branchingBlocks := make([]*blockNode, 2)
|
||||||
branchingBlock, err := loadBlocks("blk_3B.dat")
|
// Add two branching blocks
|
||||||
if err != nil {
|
branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1]))
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
dag.virtual.AddTip(branchingBlocks[0])
|
||||||
}
|
branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0]))
|
||||||
processBlocks(branchingBlock)
|
dag.virtual.AddTip(branchingBlocks[1])
|
||||||
|
|
||||||
// Make sure that the accepting block of the parent of the branching block didn't change
|
// Make sure that the accepting block of the parent of the branching block didn't change
|
||||||
expectedAcceptingBlock := dag.index.LookupNode(chainBlocks[3].Hash())
|
expectedAcceptingBlock := chainBlocks[2]
|
||||||
intersectionBlock := dag.index.LookupNode(chainBlocks[2].Hash())
|
intersectionBlock := chainBlocks[1]
|
||||||
intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock)
|
intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err)
|
||||||
@ -1448,29 +1050,26 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
"Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash)
|
"Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the accepting block of all the tips in the virtual block
|
// Make sure that the accepting block of the chain tips is the virtual block
|
||||||
for _, tip := range dag.virtual.tips() {
|
tipAcceptingBlock, err := dag.acceptingBlock(chainBlocks[len(chainBlocks)-1])
|
||||||
tipAcceptingBlock, err := dag.acceptingBlock(tip)
|
if err != nil {
|
||||||
if err != nil {
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err)
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err)
|
}
|
||||||
}
|
if tipAcceptingBlock != &dag.virtual.blockNode {
|
||||||
if tipAcceptingBlock != &dag.virtual.blockNode {
|
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+
|
||||||
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+
|
"Want: Virtual, got: %s", tipAcceptingBlock.hash)
|
||||||
"Want: Virtual, got: %s", tipAcceptingBlock.hash)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate K blocks to force the "main" chain to become red
|
// Generate 100 blocks to force the "main" chain to become red
|
||||||
nodeGenerator := buildNodeGenerator(dag.dagParams.K, false)
|
branchingChainTip := branchingBlocks[1]
|
||||||
branchingChainTip := dag.index.LookupNode(branchingBlock[0].Hash())
|
for i := uint32(0); i < 100; i++ {
|
||||||
for i := uint32(0); i < dag.dagParams.K; i++ {
|
nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip))
|
||||||
nextBranchingChainTip := nodeGenerator(setFromSlice(branchingChainTip))
|
|
||||||
dag.virtual.AddTip(nextBranchingChainTip)
|
dag.virtual.AddTip(nextBranchingChainTip)
|
||||||
branchingChainTip = nextBranchingChainTip
|
branchingChainTip = nextBranchingChainTip
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that a red block returns nil
|
// Make sure that a red block returns nil
|
||||||
redChainBlock := dag.index.LookupNode(chainBlocks[3].Hash())
|
redChainBlock := chainBlocks[2]
|
||||||
redChainBlockAcceptionBlock, err := dag.acceptingBlock(redChainBlock)
|
redChainBlockAcceptionBlock, err := dag.acceptingBlock(redChainBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain block unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain block unexpectedly failed: %s", err)
|
||||||
@ -1481,7 +1080,7 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that a red tip returns nil
|
// Make sure that a red tip returns nil
|
||||||
redChainTip := dag.index.LookupNode(chainBlocks[len(chainBlocks)-1].Hash())
|
redChainTip := chainBlocks[len(chainBlocks)-1]
|
||||||
redChainTipAcceptingBlock, err := dag.acceptingBlock(redChainTip)
|
redChainTipAcceptingBlock, err := dag.acceptingBlock(redChainTip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain tip unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain tip unexpectedly failed: %s", err)
|
||||||
@ -1491,16 +1090,3 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
"Want: nil, got: %s", redChainTipAcceptingBlock.hash)
|
"Want: nil, got: %s", redChainTipAcceptingBlock.hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// payToPubKeyHashScript creates a new script to pay a transaction
|
|
||||||
// output to a 20-byte pubkey hash. It is expected that the input is a valid
|
|
||||||
// hash.
|
|
||||||
func payToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
|
|
||||||
return txscript.NewScriptBuilder().
|
|
||||||
AddOp(txscript.OpDup).
|
|
||||||
AddOp(txscript.OpHash160).
|
|
||||||
AddData(pubKeyHash).
|
|
||||||
AddOp(txscript.OpEqualVerify).
|
|
||||||
AddOp(txscript.OpCheckSig).
|
|
||||||
Script()
|
|
||||||
}
|
|
||||||
|
@ -84,6 +84,10 @@ const (
|
|||||||
// the expected value.
|
// the expected value.
|
||||||
ErrBadMerkleRoot
|
ErrBadMerkleRoot
|
||||||
|
|
||||||
|
// ErrBadUTXOCommitment indicates the calculated UTXO commitment does not match
|
||||||
|
// the expected value.
|
||||||
|
ErrBadUTXOCommitment
|
||||||
|
|
||||||
// ErrBadCheckpoint indicates a block that is expected to be at a
|
// ErrBadCheckpoint indicates a block that is expected to be at a
|
||||||
// checkpoint height does not match the expected one.
|
// checkpoint height does not match the expected one.
|
||||||
ErrBadCheckpoint
|
ErrBadCheckpoint
|
||||||
|
@ -67,5 +67,5 @@ func ExampleBlockDAG_ProcessBlock() {
|
|||||||
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Failed to process block: already have block 63bbcfdd699ffd8cb19878564b14d3af8ba4d7ee4d1dd54925a7c21d5b5b5fdc
|
// Failed to process block: already have block 09c081c17dbfb421fc015aa77ba17e278c1a1cf9a7311a2042ffb8932d2ff365
|
||||||
}
|
}
|
||||||
|
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3A.dat
vendored
BIN
blockdag/testdata/blk_3A.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3B.dat
vendored
BIN
blockdag/testdata/blk_3B.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3C.dat
vendored
BIN
blockdag/testdata/blk_3C.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3D.dat
vendored
BIN
blockdag/testdata/blk_3D.dat
vendored
Binary file not shown.
@ -390,6 +390,7 @@ type UTXOSet interface {
|
|||||||
clone() UTXOSet
|
clone() UTXOSet
|
||||||
Get(outPoint wire.OutPoint) (*UTXOEntry, bool)
|
Get(outPoint wire.OutPoint) (*UTXOEntry, bool)
|
||||||
Multiset() *btcec.Multiset
|
Multiset() *btcec.Multiset
|
||||||
|
WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// diffFromTx is a common implementation for diffFromTx, that works
|
// diffFromTx is a common implementation for diffFromTx, that works
|
||||||
@ -550,6 +551,21 @@ func (fus *FullUTXOSet) removeAndUpdateMultiset(outPoint wire.OutPoint) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTransactions returns a new UTXO Set with the added transactions
|
||||||
|
func (fus *FullUTXOSet) WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error) {
|
||||||
|
diffSet := NewDiffUTXOSet(fus, NewUTXODiff())
|
||||||
|
for _, tx := range transactions {
|
||||||
|
isAccepted, err := diffSet.AddTx(tx, blockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ignoreDoubleSpends && !isAccepted {
|
||||||
|
return nil, fmt.Errorf("Transaction %s is not valid with the current UTXO set", tx.TxID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UTXOSet(diffSet), nil
|
||||||
|
}
|
||||||
|
|
||||||
// DiffUTXOSet represents a utxoSet with a base fullUTXOSet and a UTXODiff
|
// DiffUTXOSet represents a utxoSet with a base fullUTXOSet and a UTXODiff
|
||||||
type DiffUTXOSet struct {
|
type DiffUTXOSet struct {
|
||||||
base *FullUTXOSet
|
base *FullUTXOSet
|
||||||
@ -698,6 +714,21 @@ func (dus *DiffUTXOSet) Multiset() *btcec.Multiset {
|
|||||||
return dus.base.UTXOMultiset.Union(dus.UTXODiff.diffMultiset)
|
return dus.base.UTXOMultiset.Union(dus.UTXODiff.diffMultiset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTransactions returns a new UTXO Set with the added transactions
|
||||||
|
func (dus *DiffUTXOSet) WithTransactions(transactions []*wire.MsgTx, blockHeight uint64, ignoreDoubleSpends bool) (UTXOSet, error) {
|
||||||
|
diffSet := NewDiffUTXOSet(dus.base, dus.UTXODiff.clone())
|
||||||
|
for _, tx := range transactions {
|
||||||
|
isAccepted, err := diffSet.AddTx(tx, blockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ignoreDoubleSpends && !isAccepted {
|
||||||
|
return nil, fmt.Errorf("Transaction %s is not valid with the current UTXO set", tx.TxID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UTXOSet(diffSet), nil
|
||||||
|
}
|
||||||
|
|
||||||
func addUTXOToMultiset(ms *btcec.Multiset, entry *UTXOEntry, outPoint *wire.OutPoint) (*btcec.Multiset, error) {
|
func addUTXOToMultiset(ms *btcec.Multiset, entry *UTXOEntry, outPoint *wire.OutPoint) (*btcec.Multiset, error) {
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
err := serializeUTXO(w, entry, outPoint)
|
err := serializeUTXO(w, entry, outPoint)
|
||||||
|
@ -109,13 +109,14 @@ func (ms *Multiset) Subtract(otherMultiset *Multiset) *Multiset {
|
|||||||
// Hash serializes and returns the hash of the multiset. The hash of an empty
|
// Hash serializes and returns the hash of the multiset. The hash of an empty
|
||||||
// set is the 32 byte value of zero. The hash of a non-empty multiset is the
|
// set is the 32 byte value of zero. The hash of a non-empty multiset is the
|
||||||
// sha256 hash of the 32 byte x value concatenated with the 32 byte y value.
|
// sha256 hash of the 32 byte x value concatenated with the 32 byte y value.
|
||||||
func (ms *Multiset) Hash() daghash.Hash {
|
func (ms *Multiset) Hash() *daghash.Hash {
|
||||||
if ms.x.Sign() == 0 && ms.y.Sign() == 0 {
|
if ms.x.Sign() == 0 && ms.y.Sign() == 0 {
|
||||||
return daghash.Hash{}
|
return &daghash.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha256.Sum256(append(ms.x.Bytes(), ms.y.Bytes()...))
|
hash := sha256.Sum256(append(ms.x.Bytes(), ms.y.Bytes()...))
|
||||||
return daghash.Hash(hash)
|
castHash := daghash.Hash(hash)
|
||||||
|
return &castHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Point returns a copy of the x and y coordinates of the current multiset state.
|
// Point returns a copy of the x and y coordinates of the current multiset state.
|
||||||
|
@ -41,10 +41,10 @@ var genesisCoinbaseTx = wire.NewNativeMsgTx(1, genesisTxIns, genesisTxOuts)
|
|||||||
// genesisHash is the hash of the first block in the block chain for the main
|
// genesisHash is the hash of the first block in the block chain for the main
|
||||||
// network (genesis block).
|
// network (genesis block).
|
||||||
var genesisHash = daghash.Hash([daghash.HashSize]byte{
|
var genesisHash = daghash.Hash([daghash.HashSize]byte{
|
||||||
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25,
|
0x65, 0xf3, 0x2f, 0x2d, 0x93, 0xb8, 0xff, 0x42,
|
||||||
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
0x20, 0x1a, 0x31, 0xa7, 0xf9, 0x1c, 0x1a, 0x8c,
|
||||||
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
0x27, 0x7e, 0xa1, 0x7b, 0xa7, 0x5a, 0x01, 0xfc,
|
||||||
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
0x21, 0xb4, 0xbf, 0x7d, 0xc1, 0x81, 0xc0, 0x09,
|
||||||
})
|
})
|
||||||
|
|
||||||
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
||||||
@ -64,10 +64,15 @@ var genesisBlock = wire.MsgBlock{
|
|||||||
ParentHashes: []*daghash.Hash{},
|
ParentHashes: []*daghash.Hash{},
|
||||||
HashMerkleRoot: &genesisMerkleRoot,
|
HashMerkleRoot: &genesisMerkleRoot,
|
||||||
AcceptedIDMerkleRoot: &daghash.Hash{},
|
AcceptedIDMerkleRoot: &daghash.Hash{},
|
||||||
UTXOCommitment: &daghash.Hash{},
|
UTXOCommitment: &daghash.Hash{
|
||||||
Timestamp: time.Unix(0x5cdac4b0, 0),
|
0x51, 0x9f, 0x81, 0xbb, 0x93, 0xfd, 0x72, 0x9d,
|
||||||
Bits: 0x207fffff,
|
0x9d, 0xe0, 0x60, 0x80, 0xfa, 0x01, 0xe6, 0x34,
|
||||||
Nonce: 0x0,
|
0x93, 0x22, 0xed, 0x67, 0x69, 0x14, 0x52, 0xca,
|
||||||
|
0x95, 0xd1, 0x9b, 0x77, 0xdb, 0xb8, 0x12, 0x80,
|
||||||
|
},
|
||||||
|
Timestamp: time.Unix(0x5cdac4b0, 0),
|
||||||
|
Bits: 0x207fffff,
|
||||||
|
Nonce: 0x3,
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{genesisCoinbaseTx},
|
Transactions: []*wire.MsgTx{genesisCoinbaseTx},
|
||||||
}
|
}
|
||||||
|
@ -121,35 +121,35 @@ func TestSimNetGenesisBlock(t *testing.T) {
|
|||||||
// genesisBlockBytes are the wire encoded bytes for the genesis block of the
|
// genesisBlockBytes are the wire encoded bytes for the genesis block of the
|
||||||
// main network as of protocol version 60002.
|
// main network as of protocol version 60002.
|
||||||
var genesisBlockBytes = []byte{
|
var genesisBlockBytes = []byte{
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b, /* |........| */
|
0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b,
|
||||||
0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae, /* |.vW.}...| */
|
0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae,
|
||||||
0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb, /* |..".....| */
|
0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb,
|
||||||
0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d, /* |.......M| */
|
0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d,
|
||||||
0x42, 0x01, 0xff, 0xed, 0x9d, 0x00, 0x00, 0x00, /* |B.......| */
|
0x42, 0x01, 0xff, 0xed, 0x9d, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x9f, 0x81,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0xbb, 0x93, 0xfd, 0x72, 0x9d, 0x9d, 0xe0, 0x60,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x80, 0xfa, 0x01, 0xe6, 0x34, 0x93, 0x22, 0xed,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x67, 0x69, 0x14, 0x52, 0xca, 0x95, 0xd1, 0x9b,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xc4, 0xda, /* |........| */
|
0x77, 0xdb, 0xb8, 0x12, 0x80, 0xb0, 0xc4, 0xda,
|
||||||
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, /* |\.......| */
|
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
|
||||||
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* | .......| */
|
0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, /* |........| */
|
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||||
0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f, /* |......./| */
|
0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f,
|
||||||
0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, /* |P2SH/btc| */
|
0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63,
|
||||||
0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* |d/......| */
|
0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01, /* |......*.| */
|
0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01,
|
||||||
0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00, /* |....Q...| */
|
0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, /* |.| */
|
0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
||||||
|
@ -660,18 +660,22 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var msgBlock wire.MsgBlock
|
var msgBlock wire.MsgBlock
|
||||||
|
for _, tx := range blockTxns {
|
||||||
|
msgBlock.AddTransaction(tx.MsgTx())
|
||||||
|
}
|
||||||
|
utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, nextBlockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
msgBlock.Header = wire.BlockHeader{
|
msgBlock.Header = wire.BlockHeader{
|
||||||
Version: nextBlockVersion,
|
Version: nextBlockVersion,
|
||||||
ParentHashes: g.dag.TipHashes(),
|
ParentHashes: g.dag.TipHashes(),
|
||||||
HashMerkleRoot: hashMerkleTree.Root(),
|
HashMerkleRoot: hashMerkleTree.Root(),
|
||||||
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
||||||
UTXOCommitment: &daghash.ZeroHash,
|
UTXOCommitment: utxoCommitment,
|
||||||
Timestamp: ts,
|
Timestamp: ts,
|
||||||
Bits: reqDifficulty,
|
Bits: reqDifficulty,
|
||||||
}
|
}
|
||||||
for _, tx := range blockTxns {
|
|
||||||
msgBlock.AddTransaction(tx.MsgTx())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, perform a full check on the created block against the chain
|
// Finally, perform a full check on the created block against the chain
|
||||||
// consensus rules to ensure it properly connects to the current best
|
// consensus rules to ensure it properly connects to the current best
|
||||||
@ -697,6 +701,14 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx, nextBlockHeight uint64) (*daghash.Hash, error) {
|
||||||
|
utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, nextBlockHeight, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utxoWithTransactions.Multiset().Hash(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateBlockTime updates the timestamp in the header of the passed block to
|
// UpdateBlockTime updates the timestamp in the header of the passed block to
|
||||||
// the current time while taking into account the median time of the last
|
// the current time while taking into account the median time of the last
|
||||||
// several blocks to ensure the new time is after that time per the chain
|
// several blocks to ensure the new time is after that time per the chain
|
||||||
@ -749,6 +761,13 @@ func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight
|
|||||||
hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions())
|
hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions())
|
||||||
msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root()
|
msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root()
|
||||||
|
|
||||||
|
utxoCommitment, err := g.buildUTXOCommitment(msgBlock.Transactions, blockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msgBlock.Header.UTXOCommitment = utxoCommitment
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,10 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In order of creating deterministic coinbase tx ids.
|
// In order of creating deterministic coinbase tx ids.
|
||||||
blockTemplateGenerator.UpdateExtraNonce(template.Block, dag.Height()+1, GenerateDeterministicExtraNonceForTest())
|
err = blockTemplateGenerator.UpdateExtraNonce(template.Block, dag.Height()+1, GenerateDeterministicExtraNonceForTest())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
txsToAdd := make([]*wire.MsgTx, 0)
|
txsToAdd := make([]*wire.MsgTx, 0)
|
||||||
for _, tx := range transactions {
|
for _, tx := range transactions {
|
||||||
@ -108,19 +111,18 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
|
|||||||
template.Block.Transactions = append(template.Block.Transactions, tx)
|
template.Block.Transactions = append(template.Block.Transactions, tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateMerkleRoots := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0)
|
updateHeaderFields := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0)
|
||||||
if updateMerkleRoots {
|
if updateHeaderFields {
|
||||||
utilTxs := make([]*util.Tx, len(template.Block.Transactions))
|
utilTxs := make([]*util.Tx, len(template.Block.Transactions))
|
||||||
for i, tx := range template.Block.Transactions {
|
for i, tx := range template.Block.Transactions {
|
||||||
utilTxs[i] = util.NewTx(tx)
|
utilTxs[i] = util.NewTx(tx)
|
||||||
}
|
}
|
||||||
template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root()
|
template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root()
|
||||||
|
|
||||||
acceptedIDMerkleRoot, err := dag.NextAcceptedIDMerkleRoot()
|
template.Block.Header.UTXOCommitment, err = blockTemplateGenerator.buildUTXOCommitment(template.Block.Transactions, dag.Height()+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
template.Block.Header.AcceptedIDMerkleRoot = acceptedIDMerkleRoot
|
|
||||||
}
|
}
|
||||||
return template.Block, nil
|
return template.Block, nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user