[NOD-196] move coinbase scriptpukey to payload (#330)

* [NOD-196] move coinbase scriptpukey to payload (no tests) (#311)

* [NOD-196] Move coinbase scriptPubKey to payload

* [NOD-196] Rename SubnetworkID.IsFull to SubnetworkID.IsBuiltIn

* [NOD-196] Fix comments

* [NOD-196] Add block subsidy to fee transaction

* [NOD-196] Fix comments

* [NOD-217] Merge coinbase and fee transaction (#328)

* [NOD-196] Fix tests

* [NOD-196] Fix tests

* [NOD-217] Add error to getBluesFeeData

* [NOD-217] Merge Coinbase and fee transaction

* [NOD-217] Format project

* [NOD-217] Remove OpTrue default for mining.NewBlockTemplate

* [NOD-196] Format project

* [NOD-217] Add missing space before comment

* [NOD-196] Change MaxCoinbasePayloadLen to 150
This commit is contained in:
Ori Newman 2019-06-17 17:43:13 +03:00 committed by Svarog
parent 0c5f3d72bd
commit 263737b3fb
53 changed files with 693 additions and 901 deletions

View File

@ -21,7 +21,7 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
} }
defer teardownFunc() defer teardownFunc()
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
// Test rejecting the block if its parents are missing // Test rejecting the block if its parents are missing
orphanBlockFile := "blk_3B.dat" orphanBlockFile := "blk_3B.dat"

View File

@ -6,6 +6,7 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"github.com/daglabs/btcd/util/subnetworkid"
"io" "io"
"math" "math"
@ -80,7 +81,7 @@ var feeBucket = []byte("fees")
func (node *blockNode) getBluesFeeData(dag *BlockDAG) (map[daghash.Hash]compactFeeData, error) { func (node *blockNode) getBluesFeeData(dag *BlockDAG) (map[daghash.Hash]compactFeeData, error) {
bluesFeeData := make(map[daghash.Hash]compactFeeData) bluesFeeData := make(map[daghash.Hash]compactFeeData)
dag.db.View(func(dbTx database.Tx) error { err := dag.db.View(func(dbTx database.Tx) error {
for _, blueBlock := range node.blues { for _, blueBlock := range node.blues {
feeData, err := dbFetchFeeData(dbTx, blueBlock.hash) feeData, err := dbFetchFeeData(dbTx, blueBlock.hash)
if err != nil { if err != nil {
@ -92,6 +93,9 @@ func (node *blockNode) getBluesFeeData(dag *BlockDAG) (map[daghash.Hash]compactF
return nil return nil
}) })
if err != nil {
return nil, err
}
return bluesFeeData, nil return bluesFeeData, nil
} }
@ -119,26 +123,31 @@ func dbFetchFeeData(dbTx database.Tx, blockHash *daghash.Hash) (compactFeeData,
return feeData, nil return feeData, nil
} }
// The following functions deal with building and validating the fee transaction // The following functions deal with building and validating the coinbase transaction
func (node *blockNode) validateFeeTransaction(dag *BlockDAG, block *util.Block, txsAcceptanceData MultiBlockTxsAcceptanceData) error { func (node *blockNode) validateCoinbaseTransaction(dag *BlockDAG, block *util.Block, txsAcceptanceData MultiBlockTxsAcceptanceData) error {
if node.isGenesis() { if node.isGenesis() {
return nil return nil
} }
expectedFeeTransaction, err := node.buildFeeTransaction(dag, txsAcceptanceData) blockCoinbaseTx := block.CoinbaseTransaction().MsgTx()
pkScript, extraData, err := DeserializeCoinbasePayload(blockCoinbaseTx)
if err != nil {
return err
}
expectedCoinbaseTransaction, err := node.expectedCoinbaseTransaction(dag, txsAcceptanceData, pkScript, extraData)
if err != nil { if err != nil {
return err return err
} }
if !expectedFeeTransaction.TxHash().IsEqual(block.FeeTransaction().Hash()) { if !expectedCoinbaseTransaction.Hash().IsEqual(block.CoinbaseTransaction().Hash()) {
return ruleError(ErrBadFeeTransaction, "Fee transaction is not built as expected") return ruleError(ErrBadCoinbaseTransaction, "Coinbase transaction is not built as expected")
} }
return nil return nil
} }
// buildFeeTransaction returns the expected fee transaction for the current block // expectedCoinbaseTransaction returns the coinbase transaction for the current block
func (node *blockNode) buildFeeTransaction(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData) (*wire.MsgTx, error) { func (node *blockNode) expectedCoinbaseTransaction(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData, pkScript []byte, extraData []byte) (*util.Tx, error) {
bluesFeeData, err := node.getBluesFeeData(dag) bluesFeeData, err := node.getBluesFeeData(dag)
if err != nil { if err != nil {
return nil, err return nil, err
@ -148,7 +157,7 @@ func (node *blockNode) buildFeeTransaction(dag *BlockDAG, txsAcceptanceData Mult
txOuts := []*wire.TxOut{} txOuts := []*wire.TxOut{}
for _, blue := range node.blues { for _, blue := range node.blues {
txIn, txOut, err := feeInputAndOutputForBlueBlock(blue, txsAcceptanceData, bluesFeeData) txIn, txOut, err := coinbaseInputAndOutputForBlueBlock(dag, blue, txsAcceptanceData, bluesFeeData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -157,13 +166,59 @@ func (node *blockNode) buildFeeTransaction(dag *BlockDAG, txsAcceptanceData Mult
txOuts = append(txOuts, txOut) txOuts = append(txOuts, txOut)
} }
} }
feeTx := wire.NewNativeMsgTx(wire.TxVersion, txIns, txOuts) payload, err := SerializeCoinbasePayload(pkScript, extraData)
return txsort.Sort(feeTx), nil if err != nil {
return nil, err
}
coinbaseTx := wire.NewSubnetworkMsgTx(wire.TxVersion, txIns, txOuts, subnetworkid.SubnetworkIDCoinbase, 0, payload)
sortedCoinbaseTx := txsort.Sort(coinbaseTx)
return util.NewTx(sortedCoinbaseTx), nil
} }
// feeInputAndOutputForBlueBlock calculates the input and output that should go into the fee transaction of blueBlock // SerializeCoinbasePayload builds the coinbase payload based on the provided pkScript and extra data.
func SerializeCoinbasePayload(pkScript []byte, extraData []byte) ([]byte, error) {
w := &bytes.Buffer{}
err := wire.WriteVarInt(w, uint64(len(pkScript)))
if err != nil {
return nil, err
}
_, err = w.Write(pkScript)
if err != nil {
return nil, err
}
_, err = w.Write(extraData)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
// DeserializeCoinbasePayload deserialize the coinbase payload to its component (pkScript and extra data).
func DeserializeCoinbasePayload(tx *wire.MsgTx) (pkScript []byte, extraData []byte, err error) {
r := bytes.NewReader(tx.Payload)
pkScriptLen, err := wire.ReadVarInt(r)
if err != nil {
return nil, nil, err
}
pkScript = make([]byte, pkScriptLen)
_, err = r.Read(pkScript)
if err != nil {
return nil, nil, err
}
extraData = make([]byte, r.Len())
if r.Len() != 0 {
_, err = r.Read(extraData)
if err != nil {
return nil, nil, err
}
}
return pkScript, extraData, nil
}
// feeInputAndOutputForBlueBlock calculates the input and output that should go into the coinbase transaction of blueBlock
// If blueBlock gets no fee - returns only txIn and nil for txOut // If blueBlock gets no fee - returns only txIn and nil for txOut
func feeInputAndOutputForBlueBlock(blueBlock *blockNode, txsAcceptanceData MultiBlockTxsAcceptanceData, feeData map[daghash.Hash]compactFeeData) ( func coinbaseInputAndOutputForBlueBlock(dag *BlockDAG, blueBlock *blockNode,
txsAcceptanceData MultiBlockTxsAcceptanceData, feeData map[daghash.Hash]compactFeeData) (
*wire.TxIn, *wire.TxOut, error) { *wire.TxIn, *wire.TxOut, error) {
blockTxsAcceptanceData, ok := txsAcceptanceData[*blueBlock.hash] blockTxsAcceptanceData, ok := txsAcceptanceData[*blueBlock.hash]
@ -203,15 +258,20 @@ func feeInputAndOutputForBlueBlock(blueBlock *blockNode, txsAcceptanceData Multi
} }
} }
if totalFees == 0 { totalReward := CalcBlockSubsidy(blueBlock.height, dag.dagParams) + totalFees
if totalReward == 0 {
return txIn, nil, nil return txIn, nil, nil
} }
// the scriptPubKey for the fee is the same as the coinbase's first scriptPubKey // the PkScript for the coinbase is parsed from the coinbase payload
pkScript := blockTxsAcceptanceData[0].Tx.MsgTx().TxOut[0].PkScript pkScript, _, err := DeserializeCoinbasePayload(blockTxsAcceptanceData[0].Tx.MsgTx())
if err != nil {
return nil, nil, err
}
txOut := &wire.TxOut{ txOut := &wire.TxOut{
Value: totalFees, Value: totalReward,
PkScript: pkScript, PkScript: pkScript,
} }

View File

@ -160,10 +160,10 @@ func loadUTXOSet(filename string) (UTXOSet, error) {
return utxoSet, nil return utxoSet, nil
} }
// TestSetBlockRewardMaturity makes the ability to set the block reward maturity // TestSetCoinbaseMaturity makes the ability to set the coinbase maturity
// available when running tests. // available when running tests.
func (dag *BlockDAG) TestSetBlockRewardMaturity(maturity uint64) { func (dag *BlockDAG) TestSetCoinbaseMaturity(maturity uint64) {
dag.dagParams.BlockRewardMaturity = maturity dag.dagParams.BlockCoinbaseMaturity = maturity
} }
// newTestDAG returns a DAG that is usable for syntetic tests. It is // newTestDAG returns a DAG that is usable for syntetic tests. It is

View File

@ -371,10 +371,10 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util
// at any given height or time. // at any given height or time.
sequenceLock := &SequenceLock{Seconds: -1, BlockBlueScore: -1} sequenceLock := &SequenceLock{Seconds: -1, BlockBlueScore: -1}
// Sequence locks don't apply to block reward transactions Therefore, we // Sequence locks don't apply to coinbase transactions Therefore, we
// return sequence lock values of -1 indicating that this transaction // return sequence lock values of -1 indicating that this transaction
// can be included within a block at any given height or time. // can be included within a block at any given height or time.
if IsBlockReward(tx) { if tx.IsCoinBase() {
return sequenceLock, nil return sequenceLock, nil
} }
@ -574,7 +574,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bo
return errors.New(newErrString) return errors.New(newErrString)
} }
err = node.validateFeeTransaction(dag, block, txsAcceptanceData) err = node.validateCoinbaseTransaction(dag, block, txsAcceptanceData)
if err != nil { if err != nil {
return err return err
} }
@ -667,7 +667,7 @@ func (dag *BlockDAG) validateGasLimit(block *util.Block) error {
msgTx := tx.MsgTx() msgTx := tx.MsgTx()
// In DAGCoin and Registry sub-networks all txs must have Gas = 0, and that is validated in checkTransactionSanity // In DAGCoin and Registry sub-networks all txs must have Gas = 0, and that is validated in checkTransactionSanity
// Therefore - no need to check them here. // Therefore - no need to check them here.
if !msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) && !msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry) { if !msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) && !msgTx.SubnetworkID.IsBuiltIn() {
gasUsageInSubnetwork := gasUsageInAllSubnetworks[msgTx.SubnetworkID] gasUsageInSubnetwork := gasUsageInAllSubnetworks[msgTx.SubnetworkID]
gasUsageInSubnetwork += msgTx.Gas gasUsageInSubnetwork += msgTx.Gas
if gasUsageInSubnetwork < gasUsageInAllSubnetworks[msgTx.SubnetworkID] { // protect from overflows if gasUsageInSubnetwork < gasUsageInAllSubnetworks[msgTx.SubnetworkID] { // protect from overflows
@ -741,25 +741,25 @@ func (dag *BlockDAG) updateFinalityPoint() {
dag.lastFinalityPoint = newFinalityPoint dag.lastFinalityPoint = newFinalityPoint
} }
// NextBlockFeeTransaction prepares the fee transaction for the next mined block // NextBlockCoinbaseTransaction prepares the coinbase transaction for the next mined block
// //
// This function CAN'T be called with the DAG lock held. // This function CAN'T be called with the DAG lock held.
func (dag *BlockDAG) NextBlockFeeTransaction() (*wire.MsgTx, error) { func (dag *BlockDAG) NextBlockCoinbaseTransaction(pkScript []byte, extraData []byte) (*util.Tx, error) {
dag.dagLock.RLock() dag.dagLock.RLock()
defer dag.dagLock.RUnlock() defer dag.dagLock.RUnlock()
return dag.NextBlockFeeTransactionNoLock() return dag.NextBlockCoinbaseTransactionNoLock(pkScript, extraData)
} }
// NextBlockFeeTransactionNoLock prepares the fee transaction for the next mined block // NextBlockCoinbaseTransactionNoLock prepares the coinbase transaction for the next mined block
// //
// This function MUST be called with the DAG read-lock held // This function MUST be called with the DAG read-lock held
func (dag *BlockDAG) NextBlockFeeTransactionNoLock() (*wire.MsgTx, error) { func (dag *BlockDAG) NextBlockCoinbaseTransactionNoLock(pkScript []byte, extraData []byte) (*util.Tx, error) {
txsAcceptanceData, err := dag.TxsAcceptedByVirtual() txsAcceptanceData, err := dag.TxsAcceptedByVirtual()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return dag.virtual.blockNode.buildFeeTransaction(dag, txsAcceptanceData) return dag.virtual.blockNode.expectedCoinbaseTransaction(dag, txsAcceptanceData, pkScript, extraData)
} }
// NextAcceptedIDMerkleRoot prepares the acceptedIDMerkleRoot for the next mined block // NextAcceptedIDMerkleRoot prepares the acceptedIDMerkleRoot for the next mined block
@ -1010,12 +1010,18 @@ func (node *blockNode) updateParentsDiffs(dag *BlockDAG, newBlockUTXO UTXOSet) e
if err != nil { if err != nil {
return err return err
} }
dag.utxoDiffStore.setBlockDiffChild(parent, node) err = dag.utxoDiffStore.setBlockDiffChild(parent, node)
if err != nil {
return err
}
diff, err := newBlockUTXO.diffFrom(parentUTXO) diff, err := newBlockUTXO.diffFrom(parentUTXO)
if err != nil { if err != nil {
return err return err
} }
dag.utxoDiffStore.setBlockDiff(parent, diff) err = dag.utxoDiffStore.setBlockDiff(parent, diff)
if err != nil {
return err
}
} }
} }

View File

@ -54,9 +54,9 @@ func TestBlockCount(t *testing.T) {
} }
defer teardownFunc() defer teardownFunc()
// Since we're not dealing with the real block DAG, set the block reward // Since we're not dealing with the real block DAG, set the coinbase
// maturity to 1. // maturity to 1.
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
for i := 1; i < len(blocks); i++ { for i := 1; i < len(blocks); i++ {
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone) isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
@ -103,9 +103,9 @@ func TestHaveBlock(t *testing.T) {
} }
defer teardownFunc() defer teardownFunc()
// Since we're not dealing with the real block DAG, set the block reward // Since we're not dealing with the real block DAG, set the coinbase
// maturity to 1. // maturity to 1.
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
for i := 1; i < len(blocks); i++ { for i := 1; i < len(blocks); i++ {
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone) isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
@ -189,10 +189,10 @@ 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: "4bb2e2f55fabd67e217126dbc41e7101d0d6058800368c428cd6e397c111ee47", want: true}, {hash: "13580c9c2ed13caeedbad15167ee47bc1d26b4f88cc13054893a6d795c3baa7b", want: true},
// Block 100000 should be present (as an orphan). // Block 100000 should be present (as an orphan).
{hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true}, {hash: "01b4758d5e637d232b716cdc937335ebc5b8ea7f912b6a7fd2fc42b66b1d4d3e", want: true},
// Random hashes should not be available. // Random hashes should not be available.
{hash: "123", want: false}, {hash: "123", want: false},
@ -802,9 +802,9 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
} }
defer teardownFunc() defer teardownFunc()
// Since we're not dealing with the real block DAG, set the block reward // Since we're not dealing with the real block DAG, set the coinbase
// maturity to 1. // maturity to 1.
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
guard := monkey.Patch(targetFunction, replacementFunction) guard := monkey.Patch(targetFunction, replacementFunction)
defer guard.Unpatch() defer guard.Unpatch()
@ -884,7 +884,7 @@ func TestConfirmations(t *testing.T) {
t.Fatalf("Failed to setup DAG instance: %v", err) t.Fatalf("Failed to setup DAG instance: %v", err)
} }
defer teardownFunc() defer teardownFunc()
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
// Check that the genesis block of a DAG with only the genesis block in it has confirmations = 1. // Check that the genesis block of a DAG with only the genesis block in it has confirmations = 1.
genesisConfirmations, err := dag.blockConfirmations(dag.genesis) genesisConfirmations, err := dag.blockConfirmations(dag.genesis)
@ -999,7 +999,7 @@ func TestAcceptingBlock(t *testing.T) {
t.Fatalf("Failed to setup DAG instance: %v", err) t.Fatalf("Failed to setup DAG instance: %v", err)
} }
defer teardownFunc() defer teardownFunc()
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
// Check that the genesis block of a DAG with only the genesis block in it is accepted by the virtual. // 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) genesisAcceptingBlock, err := dag.acceptingBlock(dag.genesis)

View File

@ -135,7 +135,7 @@ func dbPutVersion(dbTx database.Tx, key []byte, version uint32) error {
// compressed script []byte variable // compressed script []byte variable
// //
// The serialized header code format is: // The serialized header code format is:
// bit 0 - containing transaction is a block reward // bit 0 - containing transaction is a coinbase
// bits 1-x - height of the block that contains the unspent txout // bits 1-x - height of the block that contains the unspent txout
// //
// Example 1: // Example 1:

View File

@ -54,7 +54,7 @@ func TestUtxoSerialization(t *testing.T) {
amount: 5000000000, amount: 5000000000,
pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"), pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
blockBlueScore: 1, blockBlueScore: 1,
packedFlags: tfBlockReward, packedFlags: tfCoinbase,
}, },
serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"), serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
}, },
@ -111,10 +111,10 @@ func TestUtxoSerialization(t *testing.T) {
utxoEntry.BlockBlueScore(), test.entry.BlockBlueScore()) utxoEntry.BlockBlueScore(), test.entry.BlockBlueScore())
continue continue
} }
if utxoEntry.IsBlockReward() != test.entry.IsBlockReward() { if utxoEntry.IsCoinbase() != test.entry.IsCoinbase() {
t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+ t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+
"coinbase flag: got %v, want %v", i, test.name, "coinbase flag: got %v, want %v", i, test.name,
utxoEntry.IsBlockReward(), test.entry.IsBlockReward()) utxoEntry.IsCoinbase(), test.entry.IsCoinbase())
continue continue
} }
} }

View File

@ -141,7 +141,7 @@ const (
ErrOverwriteTx ErrOverwriteTx
// ErrImmatureSpend indicates a transaction is attempting to spend a // ErrImmatureSpend indicates a transaction is attempting to spend a
// block reward that has not yet reached the required maturity. // coinbase that has not yet reached the required maturity.
ErrImmatureSpend ErrImmatureSpend
// ErrSpendTooHigh indicates a transaction is attempting to spend more // ErrSpendTooHigh indicates a transaction is attempting to spend more
@ -164,34 +164,12 @@ const (
// coinbase transaction. // coinbase transaction.
ErrMultipleCoinbases ErrMultipleCoinbases
// ErrBadCoinbaseScriptLen indicates the length of the signature script // ErrBadCoinbasePayloadLen indicates the length of the payload
// for a coinbase transaction is not within the valid range. // for a coinbase transaction is too high.
ErrBadCoinbaseScriptLen ErrBadCoinbasePayloadLen
// ErrBadCoinbaseValue indicates the amount of a coinbase value does // ErrBadCoinbaseTransaction indicates that the block's coinbase transaction is not build as expected
// not match the expected value of the subsidy plus the sum of all fees. ErrBadCoinbaseTransaction
ErrBadCoinbaseValue
// ErrMissingCoinbaseBlueScore indicates the coinbase transaction for a
// block does not start with the serialized block blue score as
// required for version 2 and higher blocks.
ErrMissingCoinbaseBlueScore
// ErrBadCoinbaseBlueScore indicates the serialized block blue score in the
// coinbase transaction for version 2 and higher blocks does not match
// the expected value.
ErrBadCoinbaseBlueScore
// ErrSecondTxNotFeeTransaction indicates the second transaction in
// a block is not a fee transaction.
ErrSecondTxNotFeeTransaction
// ErrBadFeeTransaction indicates that the block's fee transaction is not build as expected
ErrBadFeeTransaction
// ErrMultipleFeeTransactions indicates a block contains more than one
// fee transaction.
ErrMultipleFeeTransactions
// ErrScriptMalformed indicates a transaction script is malformed in // ErrScriptMalformed indicates a transaction script is malformed in
// some way. For example, it might be longer than the maximum allowed // some way. For example, it might be longer than the maximum allowed
@ -274,13 +252,8 @@ var errorCodeStrings = map[ErrorCode]string{
ErrTooManySigOps: "ErrTooManySigOps", ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase", ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases", ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen", ErrBadCoinbasePayloadLen: "ErrBadCoinbasePayloadLen",
ErrBadCoinbaseValue: "ErrBadCoinbaseValue", ErrBadCoinbaseTransaction: "ErrBadCoinbaseTransaction",
ErrMissingCoinbaseBlueScore: "ErrMissingCoinbaseBlueScore",
ErrBadCoinbaseBlueScore: "ErrBadCoinbaseBlueScore",
ErrSecondTxNotFeeTransaction: "ErrSecondTxNotFeeTransaction",
ErrBadFeeTransaction: "ErrBadFeeTransaction",
ErrMultipleFeeTransactions: "ErrMultipleFeeTransactions",
ErrScriptMalformed: "ErrScriptMalformed", ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation", ErrScriptValidation: "ErrScriptValidation",
ErrParentBlockUnknown: "ErrParentBlockUnknown", ErrParentBlockUnknown: "ErrParentBlockUnknown",

View File

@ -46,13 +46,8 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrTooManySigOps, "ErrTooManySigOps"}, {ErrTooManySigOps, "ErrTooManySigOps"},
{ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"}, {ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"},
{ErrMultipleCoinbases, "ErrMultipleCoinbases"}, {ErrMultipleCoinbases, "ErrMultipleCoinbases"},
{ErrBadCoinbaseScriptLen, "ErrBadCoinbaseScriptLen"}, {ErrBadCoinbasePayloadLen, "ErrBadCoinbasePayloadLen"},
{ErrBadCoinbaseValue, "ErrBadCoinbaseValue"}, {ErrBadCoinbaseTransaction, "ErrBadCoinbaseTransaction"},
{ErrMissingCoinbaseBlueScore, "ErrMissingCoinbaseBlueScore"},
{ErrBadCoinbaseBlueScore, "ErrBadCoinbaseBlueScore"},
{ErrSecondTxNotFeeTransaction, "ErrSecondTxNotFeeTransaction"},
{ErrBadFeeTransaction, "ErrBadFeeTransaction"},
{ErrMultipleFeeTransactions, "ErrMultipleFeeTransactions"},
{ErrScriptMalformed, "ErrScriptMalformed"}, {ErrScriptMalformed, "ErrScriptMalformed"},
{ErrScriptValidation, "ErrScriptValidation"}, {ErrScriptValidation, "ErrScriptValidation"},
{ErrParentBlockUnknown, "ErrParentBlockUnknown"}, {ErrParentBlockUnknown, "ErrParentBlockUnknown"},

View File

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

View File

@ -46,7 +46,7 @@ func TestFinality(t *testing.T) {
} }
defer teardownFunc() defer teardownFunc()
buildNodeToDag := func(parentHashes []*daghash.Hash) (*util.Block, error) { buildNodeToDag := func(parentHashes []*daghash.Hash) (*util.Block, error) {
msgBlock, err := mining.PrepareBlockForTest(dag, &params, parentHashes, nil, false, 1) msgBlock, err := mining.PrepareBlockForTest(dag, &params, parentHashes, nil, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -145,7 +145,7 @@ func TestFinality(t *testing.T) {
func TestSubnetworkRegistry(t *testing.T) { func TestSubnetworkRegistry(t *testing.T) {
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.K = 1 params.K = 1
params.BlockRewardMaturity = 1 params.BlockCoinbaseMaturity = 1
dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{ dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{
DAGParams: &params, DAGParams: &params,
}) })
@ -170,7 +170,7 @@ func TestSubnetworkRegistry(t *testing.T) {
func TestChainedTransactions(t *testing.T) { func TestChainedTransactions(t *testing.T) {
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.BlockRewardMaturity = 1 params.BlockCoinbaseMaturity = 1
// Create a new database and dag instance to run tests against. // Create a new database and dag instance to run tests against.
dag, teardownFunc, err := blockdag.DAGSetup("TestChainedTransactions", blockdag.Config{ dag, teardownFunc, err := blockdag.DAGSetup("TestChainedTransactions", blockdag.Config{
DAGParams: &params, DAGParams: &params,
@ -180,7 +180,7 @@ func TestChainedTransactions(t *testing.T) {
} }
defer teardownFunc() defer teardownFunc()
block1, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{params.GenesisHash}, nil, false, 1) block1, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{params.GenesisHash}, nil, false)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }
@ -224,7 +224,7 @@ func TestChainedTransactions(t *testing.T) {
} }
chainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{chainedTxIn}, []*wire.TxOut{chainedTxOut}) chainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{chainedTxIn}, []*wire.TxOut{chainedTxOut})
block2, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx, chainedTx}, true, 1) block2, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx, chainedTx}, true)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }
@ -255,7 +255,7 @@ func TestChainedTransactions(t *testing.T) {
} }
nonChainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{nonChainedTxIn}, []*wire.TxOut{nonChainedTxOut}) nonChainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{nonChainedTxIn}, []*wire.TxOut{nonChainedTxOut})
block3, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{nonChainedTx}, false, 1) block3, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{nonChainedTx}, false)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }
@ -274,7 +274,7 @@ func TestChainedTransactions(t *testing.T) {
func TestGasLimit(t *testing.T) { func TestGasLimit(t *testing.T) {
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.K = 1 params.K = 1
params.BlockRewardMaturity = 1 params.BlockCoinbaseMaturity = 1
dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{ dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{
DAGParams: &params, DAGParams: &params,
}) })
@ -290,16 +290,21 @@ func TestGasLimit(t *testing.T) {
t.Fatalf("could not register network: %s", err) t.Fatalf("could not register network: %s", err)
} }
fundsBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), nil, false, 2) cbTxs := []*wire.MsgTx{}
if err != nil { for i := 0; i < 4; i++ {
t.Fatalf("PrepareBlockForTest: %v", err) fundsBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), nil, false)
} if err != nil {
isOrphan, err := dag.ProcessBlock(util.NewBlock(fundsBlock), blockdag.BFNoPoWCheck) t.Fatalf("PrepareBlockForTest: %v", err)
if err != nil { }
t.Fatalf("ProcessBlock: %v", err) isOrphan, err := dag.ProcessBlock(util.NewBlock(fundsBlock), blockdag.BFNoPoWCheck)
} if err != nil {
if isOrphan { t.Fatalf("ProcessBlock: %v", err)
t.Fatalf("ProcessBlock: funds block got unexpectedly orphan") }
if isOrphan {
t.Fatalf("ProcessBlock: fundsBlock got unexpectedly orphan")
}
cbTxs = append(cbTxs, fundsBlock.Transactions[util.CoinbaseTransactionIndex])
} }
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil) signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
@ -312,37 +317,34 @@ func TestGasLimit(t *testing.T) {
t.Fatalf("Failed to build public key script: %s", err) t.Fatalf("Failed to build public key script: %s", err)
} }
cbTxValue := fundsBlock.Transactions[0].TxOut[0].Value
cbTxID := fundsBlock.Transactions[0].TxID()
tx1In := &wire.TxIn{ tx1In := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 0), PreviousOutpoint: *wire.NewOutpoint(cbTxs[0].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum, Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript, SignatureScript: signatureScript,
} }
tx1Out := &wire.TxOut{ tx1Out := &wire.TxOut{
Value: cbTxValue, Value: cbTxs[0].TxOut[0].Value,
PkScript: pkScript, PkScript: pkScript,
} }
tx1 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx1In}, []*wire.TxOut{tx1Out}, subnetworkID, 10000, []byte{}) tx1 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx1In}, []*wire.TxOut{tx1Out}, subnetworkID, 10000, []byte{})
tx2In := &wire.TxIn{ tx2In := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 1), PreviousOutpoint: *wire.NewOutpoint(cbTxs[1].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum, Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript, SignatureScript: signatureScript,
} }
tx2Out := &wire.TxOut{ tx2Out := &wire.TxOut{
Value: cbTxValue, Value: cbTxs[1].TxOut[0].Value,
PkScript: pkScript, PkScript: pkScript,
} }
tx2 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx2In}, []*wire.TxOut{tx2Out}, subnetworkID, 10000, []byte{}) tx2 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx2In}, []*wire.TxOut{tx2Out}, subnetworkID, 10000, []byte{})
// Here we check that we can't process a block that has transactions that exceed the gas limit // Here we check that we can't process a block that has transactions that exceed the gas limit
overLimitBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1, tx2}, true, 1) overLimitBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1, tx2}, true)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }
isOrphan, err = dag.ProcessBlock(util.NewBlock(overLimitBlock), blockdag.BFNoPoWCheck) isOrphan, err := dag.ProcessBlock(util.NewBlock(overLimitBlock), blockdag.BFNoPoWCheck)
if err == nil { if err == nil {
t.Fatalf("ProcessBlock expected to have an error") t.Fatalf("ProcessBlock expected to have an error")
} }
@ -357,19 +359,19 @@ func TestGasLimit(t *testing.T) {
} }
overflowGasTxIn := &wire.TxIn{ overflowGasTxIn := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 1), PreviousOutpoint: *wire.NewOutpoint(cbTxs[2].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum, Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript, SignatureScript: signatureScript,
} }
overflowGasTxOut := &wire.TxOut{ overflowGasTxOut := &wire.TxOut{
Value: cbTxValue, Value: cbTxs[2].TxOut[0].Value,
PkScript: pkScript, PkScript: pkScript,
} }
overflowGasTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{overflowGasTxIn}, []*wire.TxOut{overflowGasTxOut}, overflowGasTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{overflowGasTxIn}, []*wire.TxOut{overflowGasTxOut},
subnetworkID, math.MaxUint64, []byte{}) subnetworkID, math.MaxUint64, []byte{})
// Here we check that we can't process a block that its transactions' gas overflows uint64 // Here we check that we can't process a block that its transactions' gas overflows uint64
overflowGasBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1, overflowGasTx}, true, 1) overflowGasBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1, overflowGasTx}, true)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }
@ -389,18 +391,18 @@ func TestGasLimit(t *testing.T) {
nonExistentSubnetwork := &subnetworkid.SubnetworkID{123} nonExistentSubnetwork := &subnetworkid.SubnetworkID{123}
nonExistentSubnetworkTxIn := &wire.TxIn{ nonExistentSubnetworkTxIn := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 0), PreviousOutpoint: *wire.NewOutpoint(cbTxs[3].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum, Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript, SignatureScript: signatureScript,
} }
nonExistentSubnetworkTxOut := &wire.TxOut{ nonExistentSubnetworkTxOut := &wire.TxOut{
Value: cbTxValue, Value: cbTxs[3].TxOut[0].Value,
PkScript: pkScript, PkScript: pkScript,
} }
nonExistentSubnetworkTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{nonExistentSubnetworkTxIn}, nonExistentSubnetworkTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{nonExistentSubnetworkTxIn},
[]*wire.TxOut{nonExistentSubnetworkTxOut}, nonExistentSubnetwork, 1, []byte{}) []*wire.TxOut{nonExistentSubnetworkTxOut}, nonExistentSubnetwork, 1, []byte{})
nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true, 1) nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }
@ -413,7 +415,7 @@ func TestGasLimit(t *testing.T) {
} }
// Here we check that we can process a block with a transaction that doesn't exceed the gas limit // Here we check that we can process a block with a transaction that doesn't exceed the gas limit
validBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1}, true, 1) validBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1}, true)
if err != nil { if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err) t.Fatalf("PrepareBlockForTest: %v", err)
} }

View File

@ -241,9 +241,10 @@ func pushDataScript(items ...[]byte) []byte {
} }
// standardCoinbaseScript returns a standard script suitable for use as the // standardCoinbaseScript returns a standard script suitable for use as the
// signature script of the coinbase transaction of a new block. // signature script of the coinbase transaction of a new block. In particular,
func standardCoinbaseScript(blueScore uint64, extraNonce uint64) ([]byte, error) { // it starts with the block height that is required by version 2 blocks.
return txscript.NewScriptBuilder().AddInt64(int64(blueScore)). func standardCoinbaseScript(extraNonce uint64) ([]byte, error) {
return txscript.NewScriptBuilder().
AddInt64(int64(extraNonce)).Script() AddInt64(int64(extraNonce)).Script()
} }
@ -272,11 +273,10 @@ func uniqueOpReturnScript() []byte {
} }
// createCoinbaseTx returns a coinbase transaction paying an appropriate // createCoinbaseTx returns a coinbase transaction paying an appropriate
// subsidy based on the passed block height. The coinbase signature script // subsidy based on the passed block height.
// conforms to the requirements of version 2 blocks.
func (g *testGenerator) createCoinbaseTx(blueScore uint64) *wire.MsgTx { func (g *testGenerator) createCoinbaseTx(blueScore uint64) *wire.MsgTx {
extraNonce := uint64(0) extraNonce := uint64(0)
coinbaseScript, err := standardCoinbaseScript(blueScore, extraNonce) coinbaseScript, err := standardCoinbaseScript(extraNonce)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -910,7 +910,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
// genesis -> bm0 -> bm1 -> ... -> bm99 // genesis -> bm0 -> bm1 -> ... -> bm99
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
coinbaseMaturity := g.params.BlockRewardMaturity coinbaseMaturity := g.params.BlockCoinbaseMaturity
var testInstances []TestInstance var testInstances []TestInstance
for i := uint64(0); i < coinbaseMaturity; i++ { for i := uint64(0); i < coinbaseMaturity; i++ {
blockName := fmt.Sprintf("bm%d", i) blockName := fmt.Sprintf("bm%d", i)
@ -1001,45 +1001,6 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
// Too much proof-of-work coinbase tests. // Too much proof-of-work coinbase tests.
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Create a block that generates too coinbase.
//
// ... -> b1(0) -> b2(1) -> b5(2) -> b6(3)
// \-> b9(4)
// \-> b3(1) -> b4(2)
g.setTip("b6")
g.nextBlock("b9", outs[4], additionalCoinbase(1))
rejected(blockdag.ErrBadCoinbaseValue)
// Create a fork that ends with block that generates too much coinbase.
//
// ... -> b1(0) -> b2(1) -> b5(2) -> b6(3)
// \-> b10(3) -> b11(4)
// \-> b3(1) -> b4(2)
g.setTip("b5")
g.nextBlock("b10", outs[3])
acceptedToSideChainWithExpectedTip("b6")
g.nextBlock("b11", outs[4], additionalCoinbase(1))
rejected(blockdag.ErrBadCoinbaseValue)
// Create a fork that ends with block that generates too much coinbase
// as before, but with a valid fork first.
//
// ... -> b1(0) -> b2(1) -> b5(2) -> b6(3)
// | \-> b12(3) -> b13(4) -> b14(5)
// | (b12 added last)
// \-> b3(1) -> b4(2)
g.setTip("b5")
b12 := g.nextBlock("b12", outs[3])
b13 := g.nextBlock("b13", outs[4])
b14 := g.nextBlock("b14", outs[5], additionalCoinbase(1))
tests = append(tests, []TestInstance{
acceptBlock("b13", b13, true),
acceptBlock("b14", b14, true),
rejectBlock("b12", b12, blockdag.ErrBadCoinbaseValue),
expectTipBlock("b13", b13),
})
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Checksig signature operation count tests. // Checksig signature operation count tests.
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -1161,7 +1122,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
g.setTip("b15") g.setTip("b15")
tooSmallCbScript := repeatOpcode(0x00, minCoinbaseScriptLen-1) tooSmallCbScript := repeatOpcode(0x00, minCoinbaseScriptLen-1)
g.nextBlock("b26", outs[6], replaceCoinbaseSigScript(tooSmallCbScript)) g.nextBlock("b26", outs[6], replaceCoinbaseSigScript(tooSmallCbScript))
rejected(blockdag.ErrBadCoinbaseScriptLen) rejected(blockdag.ErrBadCoinbasePayloadLen)
// Parent was rejected, so this block must either be an orphan or // Parent was rejected, so this block must either be an orphan or
// outright rejected due to an invalid parent. // outright rejected due to an invalid parent.
@ -1177,7 +1138,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
g.setTip("b15") g.setTip("b15")
tooLargeCbScript := repeatOpcode(0x00, maxCoinbaseScriptLen+1) tooLargeCbScript := repeatOpcode(0x00, maxCoinbaseScriptLen+1)
g.nextBlock("b28", outs[6], replaceCoinbaseSigScript(tooLargeCbScript)) g.nextBlock("b28", outs[6], replaceCoinbaseSigScript(tooLargeCbScript))
rejected(blockdag.ErrBadCoinbaseScriptLen) rejected(blockdag.ErrBadCoinbasePayloadLen)
// Parent was rejected, so this block must either be an orphan or // Parent was rejected, so this block must either be an orphan or
// outright rejected due to an invalid parent. // outright rejected due to an invalid parent.
@ -1847,7 +1808,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
// \-> b68(20) // \-> b68(20)
g.setTip("b65") g.setTip("b65")
g.nextBlock("b68", outs[20], additionalCoinbase(10), additionalSpendFee(9)) g.nextBlock("b68", outs[20], additionalCoinbase(10), additionalSpendFee(9))
rejected(blockdag.ErrBadCoinbaseValue) rejected(blockdag.ErrBadCoinbaseTransaction)
// Create block that pays 10 extra to the coinbase and a tx that pays // Create block that pays 10 extra to the coinbase and a tx that pays
// the extra 10 fee. // the extra 10 fee.

View File

@ -119,7 +119,7 @@ var regressionNetParams = &dagconfig.Params{
GenesisHash: newHashFromStr("5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"), GenesisHash: newHashFromStr("5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"),
PowLimit: regressionPowLimit, PowLimit: regressionPowLimit,
PowLimitBits: 0x207fffff, PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100, BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 150, SubsidyReductionInterval: 150,
TargetTimespan: time.Hour * 24 * 14, // 14 days TargetTimespan: time.Hour * 24 * 14, // 14 days
TargetTimePerBlock: time.Second * 10, // 10 seconds TargetTimePerBlock: time.Second * 10, // 10 seconds

View File

@ -667,8 +667,8 @@ func (idx *AddrIndex) indexBlock(data writeIndexData, block *util.Block, dag *bl
// Coinbases do not reference any inputs. Since the block is // Coinbases do not reference any inputs. Since the block is
// required to have already gone through full validation, it has // required to have already gone through full validation, it has
// already been proven on the first transaction in the block is // already been proven on the first transaction in the block is
// a coinbase, and the second one is a fee transaction. // a coinbase.
if txIdx > 1 { if txIdx > util.CoinbaseTransactionIndex {
for _, txIn := range tx.MsgTx().TxIn { for _, txIn := range tx.MsgTx().TxIn {
// The UTXO should always have the input since // The UTXO should always have the input since
// the index contract requires it, however, be // the index contract requires it, however, be

View File

@ -40,7 +40,7 @@ func TestTxIndexConnectBlock(t *testing.T) {
indexManager := NewManager([]Indexer{txIndex}) indexManager := NewManager([]Indexer{txIndex})
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.BlockRewardMaturity = 1 params.BlockCoinbaseMaturity = 1
params.K = 1 params.K = 1
config := blockdag.Config{ config := blockdag.Config{
@ -57,7 +57,7 @@ func TestTxIndexConnectBlock(t *testing.T) {
} }
prepareAndProcessBlock := func(parentHashes []*daghash.Hash, transactions []*wire.MsgTx, blockName string) *wire.MsgBlock { prepareAndProcessBlock := func(parentHashes []*daghash.Hash, transactions []*wire.MsgTx, blockName string) *wire.MsgBlock {
block, err := mining.PrepareBlockForTest(dag, &params, parentHashes, transactions, false, 1) block, err := mining.PrepareBlockForTest(dag, &params, parentHashes, transactions, false)
if err != nil { if err != nil {
t.Fatalf("TestTxIndexConnectBlock: block %v got unexpected error from PrepareBlockForTest: %v", blockName, err) t.Fatalf("TestTxIndexConnectBlock: block %v got unexpected error from PrepareBlockForTest: %v", blockName, err)
} }

View File

@ -25,7 +25,7 @@ func TestMerkle(t *testing.T) {
idMerkleTree := BuildIDMerkleTreeStore(block.Transactions()) idMerkleTree := BuildIDMerkleTreeStore(block.Transactions())
calculatedIDMerkleRoot := idMerkleTree.Root() calculatedIDMerkleRoot := idMerkleTree.Root()
wantIDMerkleRoot, err := daghash.NewHashFromStr("65308857c92c4e5dd3c5e61b73d6b78a87456b5f8f16b13c1e02c47768a0b881") wantIDMerkleRoot, err := daghash.NewHashFromStr("3f69feb7edf5d0d67930afc990c8ec931e3428d7c7a65d7af6b81079319eb110")
if err != nil { if err != nil {
t.Errorf("BuildIDMerkleTreeStore: unexpected error: %s", err) t.Errorf("BuildIDMerkleTreeStore: unexpected error: %s", err)
} }

View File

@ -6,7 +6,6 @@ package blockdag
import ( import (
"fmt" "fmt"
"math"
"runtime" "runtime"
"time" "time"
@ -180,16 +179,16 @@ func newTxValidator(utxoSet UTXOSet, flags txscript.ScriptFlags, sigCache *txscr
// ValidateTransactionScripts validates the scripts for the passed transaction // ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines. // using multiple goroutines.
func ValidateTransactionScripts(tx *util.Tx, utxoSet UTXOSet, flags txscript.ScriptFlags, sigCache *txscript.SigCache) error { func ValidateTransactionScripts(tx *util.Tx, utxoSet UTXOSet, flags txscript.ScriptFlags, sigCache *txscript.SigCache) error {
// Don't validate coinbase transaction scripts.
if tx.IsCoinBase() {
return nil
}
// Collect all of the transaction inputs and required information for // Collect all of the transaction inputs and required information for
// validation. // validation.
txIns := tx.MsgTx().TxIn txIns := tx.MsgTx().TxIn
txValItems := make([]*txValidateItem, 0, len(txIns)) txValItems := make([]*txValidateItem, 0, len(txIns))
for txInIdx, txIn := range txIns { for txInIdx, txIn := range txIns {
// Skip block reward transactions.
if txIn.PreviousOutpoint.Index == math.MaxUint32 {
continue
}
txVI := &txValidateItem{ txVI := &txValidateItem{
txInIndex: txInIdx, txInIndex: txInIdx,
txIn: txIn, txIn: txIn,
@ -214,12 +213,11 @@ func checkBlockScripts(block *blockNode, utxoSet UTXOSet, transactions []*util.T
} }
txValItems := make([]*txValidateItem, 0, numInputs) txValItems := make([]*txValidateItem, 0, numInputs)
for _, tx := range transactions { for _, tx := range transactions {
// Skip coinbase transactions.
if tx.IsCoinBase() {
continue
}
for txInIdx, txIn := range tx.MsgTx().TxIn { for txInIdx, txIn := range tx.MsgTx().TxIn {
// Skip block reward transactions.
if txIn.PreviousOutpoint.Index == math.MaxUint32 {
continue
}
txVI := &txValidateItem{ txVI := &txValidateItem{
txInIndex: txInIdx, txInIndex: txInIdx,
txIn: txIn, txIn: txIn,

View File

@ -9,7 +9,6 @@ import (
"github.com/daglabs/btcd/util/subnetworkid" "github.com/daglabs/btcd/util/subnetworkid"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/database" "github.com/daglabs/btcd/database"
_ "github.com/daglabs/btcd/database/ffldb" // blank import ffldb so that its init() function runs before tests _ "github.com/daglabs/btcd/database/ffldb" // blank import ffldb so that its init() function runs before tests
"github.com/daglabs/btcd/txscript" "github.com/daglabs/btcd/txscript"
@ -136,48 +135,6 @@ func createTxForTest(numInputs uint32, numOutputs uint32, outputValue uint64, su
return wire.NewNativeMsgTx(wire.TxVersion, txIns, txOuts) return wire.NewNativeMsgTx(wire.TxVersion, txIns, txOuts)
} }
// createCoinbaseTxForTest returns a coinbase transaction with the requested number of
// outputs paying an appropriate subsidy based on the passed block blueScore to the
// address associated with the harness. It automatically uses a standard
// signature script that starts with the block blue score
func createCoinbaseTxForTest(blueScore uint64, numOutputs uint32, extraNonce int64, params *dagconfig.Params) (*wire.MsgTx, error) {
// Create standard coinbase script.
coinbaseScript, err := txscript.NewScriptBuilder().
AddInt64(int64(blueScore)).AddInt64(extraNonce).Script()
if err != nil {
return nil, err
}
txIns := []*wire.TxIn{&wire.TxIn{
// Coinbase transactions have no inputs, so previous outpoint is
// zero hash and max index.
PreviousOutpoint: *wire.NewOutpoint(&daghash.TxID{},
wire.MaxPrevOutIndex),
SignatureScript: coinbaseScript,
Sequence: wire.MaxTxInSequenceNum,
}}
txOuts := []*wire.TxOut{}
totalInput := CalcBlockSubsidy(blueScore, params)
amountPerOutput := totalInput / uint64(numOutputs)
remainder := totalInput - amountPerOutput*uint64(numOutputs)
for i := uint32(0); i < numOutputs; i++ {
// Ensure the final output accounts for any remainder that might
// be left from splitting the input amount.
amount := amountPerOutput
if i == numOutputs-1 {
amount = amountPerOutput + remainder
}
txOuts = append(txOuts, &wire.TxOut{
PkScript: OpTrueScript,
Value: amount,
})
}
return wire.NewNativeMsgTx(wire.TxVersion, txIns, txOuts), nil
}
// SetVirtualForTest replaces the dag's virtual block. This function is used for test purposes only // SetVirtualForTest replaces the dag's virtual block. This function is used for test purposes only
func SetVirtualForTest(dag *BlockDAG, virtual *virtualBlock) *virtualBlock { func SetVirtualForTest(dag *BlockDAG, virtual *virtualBlock) *virtualBlock {
oldVirtual := dag.virtual oldVirtual := dag.virtual

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -47,7 +47,7 @@ func utxoEntryHeaderCode(entry *UTXOEntry) uint64 {
// encodes the blue score shifted over one bit and the block reward flag // encodes the blue score shifted over one bit and the block reward flag
// in the lowest bit. // in the lowest bit.
headerCode := uint64(entry.BlockBlueScore()) << 1 headerCode := uint64(entry.BlockBlueScore()) << 1
if entry.IsBlockReward() { if entry.IsCoinbase() {
headerCode |= 0x01 headerCode |= 0x01
} }
@ -282,9 +282,9 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) {
// Decode the header code. // Decode the header code.
// //
// Bit 0 indicates whether the containing transaction is a block reward. // Bit 0 indicates whether the containing transaction is a coinbase.
// Bits 1-x encode the blue score of the containing transaction. // Bits 1-x encode blue score of the containing transaction.
isBlockReward := code&0x01 != 0 isCoinbase := code&0x01 != 0
blockBlueScore := code >> 1 blockBlueScore := code >> 1
// Decode the compressed unspent transaction output. // Decode the compressed unspent transaction output.
@ -300,8 +300,8 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) {
blockBlueScore: blockBlueScore, blockBlueScore: blockBlueScore,
packedFlags: 0, packedFlags: 0,
} }
if isBlockReward { if isCoinbase {
entry.packedFlags |= tfBlockReward entry.packedFlags |= tfCoinbase
} }
return entry, nil return entry, nil

View File

@ -20,7 +20,7 @@ const (
) )
// UTXOEntry houses details about an individual transaction output in a utxo // UTXOEntry houses details about an individual transaction output in a utxo
// set such as whether or not it was contained in a block reward tx, the blue // 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 contains the tx, its public key script, and how
// much it pays. // much it pays.
type UTXOEntry struct { type UTXOEntry struct {
@ -35,16 +35,16 @@ type UTXOEntry struct {
blockBlueScore uint64 // Blue score of the block containing the tx. blockBlueScore uint64 // Blue score of the block containing the tx.
// packedFlags contains additional info about output such as whether it // packedFlags contains additional info about output such as whether it
// is a block reward, and whether it has been modified // is a coinbase, and whether it has been modified
// since it was loaded. This approach is used in order to reduce memory // since it was loaded. This approach is used in order to reduce memory
// usage since there will be a lot of these in memory. // usage since there will be a lot of these in memory.
packedFlags txoFlags packedFlags txoFlags
} }
// IsBlockReward returns whether or not the output was contained in a block // IsCoinbase returns whether or not the output was contained in a block
// reward transaction. // reward transaction.
func (entry *UTXOEntry) IsBlockReward() bool { func (entry *UTXOEntry) IsCoinbase() bool {
return entry.packedFlags&tfBlockReward == tfBlockReward return entry.packedFlags&tfCoinbase == tfCoinbase
} }
// BlockBlueScore returns the blue score of the block containing the output. // BlockBlueScore returns the blue score of the block containing the output.
@ -73,20 +73,20 @@ func (entry *UTXOEntry) IsUnmined() bool {
type txoFlags uint8 type txoFlags uint8
const ( const (
// tfBlockReward indicates that a txout was contained in a block reward tx (coinbase or fee transaction). // tfCoinbase indicates that a txout was contained in a coinbase tx.
tfBlockReward txoFlags = 1 << iota tfCoinbase txoFlags = 1 << iota
) )
// NewUTXOEntry creates a new utxoEntry representing the given txOut // NewUTXOEntry creates a new utxoEntry representing the given txOut
func NewUTXOEntry(txOut *wire.TxOut, isBlockReward bool, blockBlueScore uint64) *UTXOEntry { func NewUTXOEntry(txOut *wire.TxOut, isCoinbase bool, blockBlueScore uint64) *UTXOEntry {
entry := &UTXOEntry{ entry := &UTXOEntry{
amount: txOut.Value, amount: txOut.Value,
pkScript: txOut.PkScript, pkScript: txOut.PkScript,
blockBlueScore: blockBlueScore, blockBlueScore: blockBlueScore,
} }
if isBlockReward { if isCoinbase {
entry.packedFlags |= tfBlockReward entry.packedFlags |= tfCoinbase
} }
return entry return entry
@ -407,8 +407,8 @@ type UTXOSet interface {
// or an error if provided transaction is not valid in the context of this UTXOSet // 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, containingNode *blockNode) (*UTXODiff, error) {
diff := NewUTXODiff() diff := NewUTXODiff()
isBlockReward := tx.IsBlockReward() isCoinbase := tx.IsCoinBase()
if !isBlockReward { if !isCoinbase {
for _, txIn := range tx.TxIn { for _, txIn := range tx.TxIn {
if entry, ok := u.Get(txIn.PreviousOutpoint); ok { if entry, ok := u.Get(txIn.PreviousOutpoint); ok {
err := diff.RemoveEntry(txIn.PreviousOutpoint, entry) err := diff.RemoveEntry(txIn.PreviousOutpoint, entry)
@ -423,7 +423,7 @@ func diffFromTx(u UTXOSet, tx *wire.MsgTx, containingNode *blockNode) (*UTXODiff
} }
} }
for i, txOut := range tx.TxOut { for i, txOut := range tx.TxOut {
entry := NewUTXOEntry(txOut, isBlockReward, containingNode.blueScore) entry := NewUTXOEntry(txOut, isCoinbase, containingNode.blueScore)
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i)) outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
err := diff.AddEntry(outpoint, entry) err := diff.AddEntry(outpoint, entry)
if err != nil { if err != nil {
@ -471,8 +471,8 @@ func (fus *FullUTXOSet) WithDiff(other *UTXODiff) (UTXOSet, error) {
// It returns error if something unexpected happens, such as serialization error (isAccepted=false doesn't // It returns error if something unexpected happens, such as serialization error (isAccepted=false doesn't
// necessarily means there's an error). // necessarily means there's an error).
func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool, err error) { func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool, err error) {
isBlockReward := tx.IsBlockReward() isCoinbase := tx.IsCoinBase()
if !isBlockReward { if !isCoinbase {
if !fus.containsInputs(tx) { if !fus.containsInputs(tx) {
return false, nil return false, nil
} }
@ -488,7 +488,7 @@ func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool
for i, txOut := range tx.TxOut { for i, txOut := range tx.TxOut {
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i)) outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
entry := NewUTXOEntry(txOut, isBlockReward, blueScore) entry := NewUTXOEntry(txOut, isCoinbase, blueScore)
err := fus.addAndUpdateMultiset(outpoint, entry) err := fus.addAndUpdateMultiset(outpoint, entry)
if err != nil { if err != nil {
@ -615,12 +615,12 @@ func (dus *DiffUTXOSet) WithDiff(other *UTXODiff) (UTXOSet, error) {
// AddTx adds a transaction to this utxoSet and returns true iff it's valid in this UTXO's context // AddTx adds a transaction to this utxoSet and returns true iff it's valid in this UTXO's context
func (dus *DiffUTXOSet) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (bool, error) { func (dus *DiffUTXOSet) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (bool, error) {
isBlockReward := tx.IsBlockReward() isCoinbase := tx.IsCoinBase()
if !isBlockReward && !dus.containsInputs(tx) { if !isCoinbase && !dus.containsInputs(tx) {
return false, nil return false, nil
} }
err := dus.appendTx(tx, blockBlueScore, isBlockReward) err := dus.appendTx(tx, blockBlueScore, isCoinbase)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -628,8 +628,8 @@ func (dus *DiffUTXOSet) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (bool, erro
return true, nil return true, nil
} }
func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isBlockReward bool) error { func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isCoinbase bool) error {
if !isBlockReward { if !isCoinbase {
for _, txIn := range tx.TxIn { for _, txIn := range tx.TxIn {
outpoint := *wire.NewOutpoint(&txIn.PreviousOutpoint.TxID, txIn.PreviousOutpoint.Index) outpoint := *wire.NewOutpoint(&txIn.PreviousOutpoint.TxID, txIn.PreviousOutpoint.Index)
entry, ok := dus.Get(outpoint) entry, ok := dus.Get(outpoint)
@ -645,7 +645,7 @@ func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isBlockR
for i, txOut := range tx.TxOut { for i, txOut := range tx.TxOut {
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i)) outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
entry := NewUTXOEntry(txOut, isBlockReward, blockBlueScore) entry := NewUTXOEntry(txOut, isCoinbase, blockBlueScore)
err := dus.UTXODiff.AddEntry(outpoint, entry) err := dus.UTXODiff.AddEntry(outpoint, entry)
if err != nil { if err != nil {

View File

@ -1,12 +1,12 @@
package blockdag package blockdag
import ( import (
"github.com/daglabs/btcd/util/subnetworkid"
"math" "math"
"reflect" "reflect"
"testing" "testing"
"github.com/daglabs/btcd/btcec" "github.com/daglabs/btcd/btcec"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/util/daghash" "github.com/daglabs/btcd/util/daghash"
"github.com/daglabs/btcd/wire" "github.com/daglabs/btcd/wire"
) )
@ -723,15 +723,15 @@ func TestUTXOSetDiffRules(t *testing.T) {
// TestDiffUTXOSet_addTx makes sure that diffUTXOSet addTx works as expected // TestDiffUTXOSet_addTx makes sure that diffUTXOSet addTx works as expected
func TestDiffUTXOSet_addTx(t *testing.T) { func TestDiffUTXOSet_addTx(t *testing.T) {
// transaction0 is coinbase. As such, it has exactly one input with hash zero and MaxUInt32 index // coinbaseTX is coinbase. As such, it has exactly one input with hash zero and MaxUInt32 index
txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000") txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
txIn0 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: wire.Outpoint{TxID: *txID0, Index: math.MaxUint32}, Sequence: 0} txIn0 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: wire.Outpoint{TxID: *txID0, Index: math.MaxUint32}, Sequence: 0}
txOut0 := &wire.TxOut{PkScript: []byte{0}, Value: 10} txOut0 := &wire.TxOut{PkScript: []byte{0}, Value: 10}
utxoEntry0 := NewUTXOEntry(txOut0, true, 0) utxoEntry0 := NewUTXOEntry(txOut0, true, 0)
transaction0 := wire.NewNativeMsgTx(1, []*wire.TxIn{txIn0}, []*wire.TxOut{txOut0}) coinbaseTX := wire.NewSubnetworkMsgTx(1, []*wire.TxIn{txIn0}, []*wire.TxOut{txOut0}, subnetworkid.SubnetworkIDCoinbase, 0, nil)
// transaction1 spends transaction0 // transaction1 spends coinbaseTX
id1 := transaction0.TxID() id1 := coinbaseTX.TxID()
outpoint1 := *wire.NewOutpoint(id1, 0) outpoint1 := *wire.NewOutpoint(id1, 0)
txIn1 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: outpoint1, Sequence: 0} txIn1 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: outpoint1, Sequence: 0}
txOut1 := &wire.TxOut{PkScript: []byte{1}, Value: 20} txOut1 := &wire.TxOut{PkScript: []byte{1}, Value: 20}
@ -764,7 +764,7 @@ func TestDiffUTXOSet_addTx(t *testing.T) {
name: "add coinbase transaction to empty set", name: "add coinbase transaction to empty set",
startSet: NewDiffUTXOSet(NewFullUTXOSet(), NewUTXODiff()), startSet: NewDiffUTXOSet(NewFullUTXOSet(), NewUTXODiff()),
startHeight: 0, startHeight: 0,
toAdd: []*wire.MsgTx{transaction0}, toAdd: []*wire.MsgTx{coinbaseTX},
expectedSet: &DiffUTXOSet{ expectedSet: &DiffUTXOSet{
base: &FullUTXOSet{utxoCollection: utxoCollection{}}, base: &FullUTXOSet{utxoCollection: utxoCollection{}},
UTXODiff: &UTXODiff{ UTXODiff: &UTXODiff{
@ -893,10 +893,11 @@ func TestDiffFromTx(t *testing.T) {
fus := addMultisetToFullUTXOSet(t, &FullUTXOSet{ fus := addMultisetToFullUTXOSet(t, &FullUTXOSet{
utxoCollection: utxoCollection{}, utxoCollection: utxoCollection{},
}) })
cbTx, err := createCoinbaseTxForTest(1, 1, 0, &dagconfig.SimNetParams)
if err != nil { txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
t.Errorf("createCoinbaseTxForTest: %v", err) txIn0 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: wire.Outpoint{TxID: *txID0, Index: math.MaxUint32}, Sequence: 0}
} txOut0 := &wire.TxOut{PkScript: []byte{0}, Value: 10}
cbTx := wire.NewSubnetworkMsgTx(1, []*wire.TxIn{txIn0}, []*wire.TxOut{txOut0}, subnetworkid.SubnetworkIDCoinbase, 0, nil)
if isAccepted, err := fus.AddTx(cbTx, 1); err != nil { if isAccepted, err := fus.AddTx(cbTx, 1); err != nil {
t.Fatalf("AddTx unexpectedly failed. Error: %s", err) t.Fatalf("AddTx unexpectedly failed. Error: %s", err)
} else if !isAccepted { } else if !isAccepted {

View File

@ -29,11 +29,8 @@ const (
// hours. // hours.
MaxTimeOffsetSeconds = 2 * 60 * 60 MaxTimeOffsetSeconds = 2 * 60 * 60
// MinCoinbaseScriptLen is the minimum length a coinbase script can be. // MaxCoinbasePayloadLen is the maximum length a coinbase payload can be.
MinCoinbaseScriptLen = 2 MaxCoinbasePayloadLen = 150
// MaxCoinbaseScriptLen is the maximum length a coinbase script can be.
MaxCoinbaseScriptLen = 100
// medianTimeBlocks is the number of previous blocks which should be // medianTimeBlocks is the number of previous blocks which should be
// used to calculate the median time used to validate block timestamps. // used to calculate the median time used to validate block timestamps.
@ -53,28 +50,6 @@ func isNullOutpoint(outpoint *wire.Outpoint) bool {
return false return false
} }
// IsCoinBase determines whether or not a transaction is a coinbase. A coinbase
// is a special transaction created by miners that has no inputs. This is
// represented in the block dag by a transaction with a single input that has
// a previous output transaction index set to the maximum value along with a
// zero hash.
func IsCoinBase(tx *util.Tx) bool {
return tx.MsgTx().IsCoinBase()
}
// IsBlockReward determines whether or not a transaction is a block reward (a fee transaction or block reward)
func IsBlockReward(tx *util.Tx) bool {
return tx.MsgTx().IsBlockReward()
}
// IsFeeTransaction determines whether or not a transaction is a fee transaction. A fee
// transaction is a special transaction created by miners that distributes fees to the
// previous blocks' miners. Each input of the fee transaction should set index to maximum
// value and reference the relevant block id, instead of previous transaction id.
func IsFeeTransaction(tx *util.Tx) bool {
return tx.MsgTx().IsFeeTransaction()
}
// SequenceLockActive determines if a transaction's sequence locks have been // SequenceLockActive determines if a transaction's sequence locks have been
// met, meaning that all the inputs of a given transaction have reached a // met, meaning that all the inputs of a given transaction have reached a
// blue score or time sufficient for their relative lock-time maturity. // blue score or time sufficient for their relative lock-time maturity.
@ -149,9 +124,10 @@ func CalcBlockSubsidy(blueScore uint64, dagParams *dagconfig.Params) uint64 {
// CheckTransactionSanity performs some preliminary checks on a transaction to // CheckTransactionSanity performs some preliminary checks on a transaction to
// ensure it is sane. These checks are context free. // ensure it is sane. These checks are context free.
func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID) error { func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID) error {
isCoinbase := tx.IsCoinBase()
// A transaction must have at least one input. // A transaction must have at least one input.
msgTx := tx.MsgTx() msgTx := tx.MsgTx()
if len(msgTx.TxIn) == 0 { if !isCoinbase && len(msgTx.TxIn) == 0 {
return ruleError(ErrNoTxInputs, "transaction has no inputs") return ruleError(ErrNoTxInputs, "transaction has no inputs")
} }
@ -210,14 +186,14 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
existingTxOut[txIn.PreviousOutpoint] = struct{}{} existingTxOut[txIn.PreviousOutpoint] = struct{}{}
} }
// Coinbase script length must be between min and max length. // Coinbase payload length must not exceed the max length.
if IsCoinBase(tx) { if isCoinbase {
slen := len(msgTx.TxIn[0].SignatureScript) payloadLen := len(msgTx.Payload)
if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen { if payloadLen > MaxCoinbasePayloadLen {
str := fmt.Sprintf("coinbase transaction script length "+ str := fmt.Sprintf("coinbase transaction payload length "+
"of %d is out of range (min: %d, max: %d)", "of %d is out of range (max: %d)",
slen, MinCoinbaseScriptLen, MaxCoinbaseScriptLen) payloadLen, MaxCoinbasePayloadLen)
return ruleError(ErrBadCoinbaseScriptLen, str) return ruleError(ErrBadCoinbasePayloadLen, str)
} }
} else { } else {
// Previous transaction outputs referenced by the inputs to this // Previous transaction outputs referenced by the inputs to this
@ -241,9 +217,9 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
return ruleError(ErrInvalidPayloadHash, "unexpected non-empty payload hash in native subnetwork") return ruleError(ErrInvalidPayloadHash, "unexpected non-empty payload hash in native subnetwork")
} }
// Transactions in native and subnetwork registry subnetworks must have Gas = 0 // Transactions in native, registry and coinbase subnetworks must have Gas = 0
if (msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) || if (msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) ||
msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry)) && msgTx.SubnetworkID.IsBuiltIn()) &&
msgTx.Gas > 0 { msgTx.Gas > 0 {
return ruleError(ErrInvalidGas, "transaction in the native or "+ return ruleError(ErrInvalidGas, "transaction in the native or "+
@ -265,10 +241,10 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
"with length != 8 bytes") "with length != 8 bytes")
} }
// If we are a partial node, only transactions on the Registry subnetwork // If we are a partial node, only transactions on built in subnetworks
// or our own subnetwork may have a payload // or our own subnetwork may have a payload
isLocalNodeFull := subnetworkID == nil isLocalNodeFull := subnetworkID == nil
shouldTxBeFull := msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry) || shouldTxBeFull := msgTx.SubnetworkID.IsBuiltIn() ||
msgTx.SubnetworkID.IsEqual(subnetworkID) msgTx.SubnetworkID.IsEqual(subnetworkID)
if !isLocalNodeFull && !shouldTxBeFull && len(msgTx.Payload) > 0 { if !isLocalNodeFull && !shouldTxBeFull && len(msgTx.Payload) > 0 {
return ruleError(ErrInvalidPayload, return ruleError(ErrInvalidPayload,
@ -347,9 +323,9 @@ func CountSigOps(tx *util.Tx) int {
// transactions which are of the pay-to-script-hash type. This uses the // transactions which are of the pay-to-script-hash type. This uses the
// precise, signature operation counting mechanism from the script engine which // precise, signature operation counting mechanism from the script engine which
// requires access to the input transaction scripts. // requires access to the input transaction scripts.
func CountP2SHSigOps(tx *util.Tx, isBlockReward bool, utxoSet UTXOSet) (int, error) { func CountP2SHSigOps(tx *util.Tx, isCoinbase bool, utxoSet UTXOSet) (int, error) {
// Block reward transactions have no interesting inputs. // Coinbase transactions have no interesting inputs.
if isBlockReward { if isCoinbase {
return 0, nil return 0, nil
} }
@ -499,36 +475,22 @@ func (dag *BlockDAG) checkBlockSanity(block *util.Block, flags BehaviorFlags) er
// The first transaction in a block must be a coinbase. // The first transaction in a block must be a coinbase.
transactions := block.Transactions() transactions := block.Transactions()
if !IsCoinBase(transactions[0]) { if !transactions[util.CoinbaseTransactionIndex].IsCoinBase() {
return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+ return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
"block is not a coinbase") "block is not a coinbase")
} }
isGenesis := block.MsgBlock().Header.IsGenesis() txOffset := util.CoinbaseTransactionIndex + 1
if !isGenesis && !IsFeeTransaction(transactions[1]) {
return ruleError(ErrSecondTxNotFeeTransaction, "second transaction in "+
"block is not a fee transaction")
}
txOffset := 2
if isGenesis {
txOffset = 1
}
// A block must not have more than one coinbase. And transactions must be // A block must not have more than one coinbase. And transactions must be
// ordered by subnetwork // ordered by subnetwork
for i, tx := range transactions[txOffset:] { for i, tx := range transactions[txOffset:] {
if IsCoinBase(tx) { if tx.IsCoinBase() {
str := fmt.Sprintf("block contains second coinbase at "+ str := fmt.Sprintf("block contains second coinbase at "+
"index %d", i+2) "index %d", i+2)
return ruleError(ErrMultipleCoinbases, str) return ruleError(ErrMultipleCoinbases, str)
} }
if IsFeeTransaction(tx) { if i != 0 && subnetworkid.Less(&tx.MsgTx().SubnetworkID, &transactions[i].MsgTx().SubnetworkID) {
str := fmt.Sprintf("block contains second fee transaction at "+
"index %d", i+2)
return ruleError(ErrMultipleFeeTransactions, str)
}
if subnetworkid.Less(&tx.MsgTx().SubnetworkID, &transactions[i].MsgTx().SubnetworkID) {
return ruleError(ErrTransactionsNotSorted, "transactions must be sorted by subnetwork") return ruleError(ErrTransactionsNotSorted, "transactions must be sorted by subnetwork")
} }
} }
@ -795,9 +757,8 @@ func ensureNoDuplicateTx(utxoSet UTXOSet, transactions []*util.Tx) error {
func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txBlueScore uint64, utxoSet UTXOSet, dagParams *dagconfig.Params, fastAdd bool) ( func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txBlueScore uint64, utxoSet UTXOSet, dagParams *dagconfig.Params, fastAdd bool) (
txFeeInSatoshi uint64, err error) { txFeeInSatoshi uint64, err error) {
// Block reward transactions (a.k.a. coinbase or fee transactions) // Coinbase transactions have no standard inputs to validate.
// have no standard inputs to validate. if tx.IsCoinBase() {
if IsBlockReward(tx) {
return 0, nil return 0, nil
} }
@ -815,7 +776,7 @@ func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txBlueScore uint64, utxoS
} }
if !fastAdd { if !fastAdd {
if err = validateBlockRewardMaturity(dagParams, entry, txBlueScore, txIn); err != nil { if err = validateCoinbaseMaturity(dagParams, entry, txBlueScore, txIn); err != nil {
return 0, err return 0, err
} }
} }
@ -873,19 +834,19 @@ func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txBlueScore uint64, utxoS
return txFeeInSatoshi, nil return txFeeInSatoshi, nil
} }
func validateBlockRewardMaturity(dagParams *dagconfig.Params, entry *UTXOEntry, txBlueScore uint64, txIn *wire.TxIn) error { func validateCoinbaseMaturity(dagParams *dagconfig.Params, entry *UTXOEntry, txBlueScore uint64, txIn *wire.TxIn) error {
// Ensure the transaction is not spending coins which have not // Ensure the transaction is not spending coins which have not
// yet reached the required block reward maturity. // yet reached the required coinbase maturity.
if entry.IsBlockReward() { if entry.IsCoinbase() {
originBlueScore := entry.BlockBlueScore() originBlueScore := entry.BlockBlueScore()
BlueScoreSincePrev := txBlueScore - originBlueScore blueScoreSincePrev := txBlueScore - originBlueScore
if BlueScoreSincePrev < dagParams.BlockRewardMaturity { if blueScoreSincePrev < dagParams.BlockCoinbaseMaturity {
str := fmt.Sprintf("tried to spend block reward "+ str := fmt.Sprintf("tried to spend coinbase "+
"transaction output %s from blue score %d "+ "transaction output %s from blue score %d "+
"to blue score %d before required maturity "+ "to blue score %d before required maturity "+
"of %d", txIn.PreviousOutpoint, "of %d", txIn.PreviousOutpoint,
originBlueScore, txBlueScore, originBlueScore, txBlueScore,
dagParams.BlockRewardMaturity) dagParams.BlockCoinbaseMaturity)
return ruleError(ErrImmatureSpend, str) return ruleError(ErrImmatureSpend, str)
} }
} }
@ -926,7 +887,7 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet,
// against all the inputs when the signature operations are out of // against all the inputs when the signature operations are out of
// bounds. // bounds.
// In addition - add all fees into a fee accumulator, to be stored and checked // In addition - add all fees into a fee accumulator, to be stored and checked
// when validating descendants' fee transactions. // when validating descendants' coinbase transactions.
var totalFees uint64 var totalFees uint64
compactFeeFactory := newCompactFeeFactory() compactFeeFactory := newCompactFeeFactory()
@ -957,22 +918,6 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet,
} }
if !fastAdd { if !fastAdd {
// The total output values of the coinbase transaction must not exceed
// the expected subsidy value plus total transaction fees gained from
// mining the block. It is safe to ignore overflow and out of range
// errors here because those error conditions would have already been
// caught by checkTransactionSanity.
var totalSatoshiOut uint64
for _, txOut := range transactions[0].MsgTx().TxOut {
totalSatoshiOut += txOut.Value
}
expectedSatoshiOut := CalcBlockSubsidy(block.blueScore, dag.dagParams)
if totalSatoshiOut > expectedSatoshiOut {
str := fmt.Sprintf("coinbase transaction for block pays %d "+
"which is more than expected value of %d",
totalSatoshiOut, expectedSatoshiOut)
return nil, ruleError(ErrBadCoinbaseValue, str)
}
// Don't run scripts if this node is before the latest known good // Don't run scripts if this node is before the latest known good
// checkpoint since the validity is verified via the checkpoints (all // checkpoint since the validity is verified via the checkpoints (all
@ -1041,12 +986,11 @@ func validateSigopsCount(pastUTXO UTXOSet, transactions []*util.Tx) error {
for i, tx := range transactions { for i, tx := range transactions {
numsigOps := CountSigOps(tx) numsigOps := CountSigOps(tx)
// Since the first transaction has already been verified to be a // Since the first transaction has already been verified to be a
// coinbase transaction, and the second transaction has already // coinbase transaction, use i != util.CoinbaseTransactionIndex
// been verified to be a fee transaction, use i < 2 as an // as an optimization for the flag to countP2SHSigOps for whether
// optimization for the flag to countP2SHSigOps for whether or // or not the transaction is a coinbase transaction rather than
// not the transaction is a block reward transaction rather than // having to do a full coinbase check again.
// having to do a full coinbase and fee transaction check again. numP2SHSigOps, err := CountP2SHSigOps(tx, i == util.CoinbaseTransactionIndex, pastUTXO)
numP2SHSigOps, err := CountP2SHSigOps(tx, i < 2, pastUTXO)
if err != nil { if err != nil {
return err return err
} }

View File

@ -76,9 +76,9 @@ func TestCheckConnectBlockTemplate(t *testing.T) {
} }
defer teardownFunc() defer teardownFunc()
// Since we're not dealing with the real block DAG, set the block reward // Since we're not dealing with the real block DAG, set the coinbase
// maturity to 1. // maturity to 1.
dag.TestSetBlockRewardMaturity(1) dag.TestSetCoinbaseMaturity(1)
// Load up blocks such that there is a side chain. // Load up blocks such that there is a side chain.
// (genesis block) -> 1 -> 2 -> 3 -> 4 // (genesis block) -> 1 -> 2 -> 3 -> 4
@ -611,6 +611,30 @@ func TestCheckTransactionSanity(t *testing.T) {
nil, nil,
func(tx *wire.MsgTx) { tx.TxIn[1].PreviousOutpoint.Index = 0 }, func(tx *wire.MsgTx) { tx.TxIn[1].PreviousOutpoint.Index = 0 },
ruleError(ErrDuplicateTxInputs, "")}, ruleError(ErrDuplicateTxInputs, "")},
{"1 input coinbase",
1,
1,
1,
*subnetworkid.SubnetworkIDNative,
&txSubnetworkData{subnetworkid.SubnetworkIDCoinbase, 0, nil},
nil,
nil},
{"no inputs coinbase",
0,
1,
1,
*subnetworkid.SubnetworkIDNative,
&txSubnetworkData{subnetworkid.SubnetworkIDCoinbase, 0, nil},
nil,
nil},
{"too long payload coinbase",
1,
1,
1,
*subnetworkid.SubnetworkIDNative,
&txSubnetworkData{subnetworkid.SubnetworkIDCoinbase, 0, make([]byte, MaxCoinbasePayloadLen+1)},
nil,
ruleError(ErrBadCoinbasePayloadLen, "")},
{"non-zero gas in DAGCoin", 1, 1, 0, {"non-zero gas in DAGCoin", 1, 1, 0,
*subnetworkid.SubnetworkIDNative, *subnetworkid.SubnetworkIDNative,
&txSubnetworkData{subnetworkid.SubnetworkIDNative, 1, []byte{}}, &txSubnetworkData{subnetworkid.SubnetworkIDNative, 1, []byte{}},
@ -687,10 +711,10 @@ var Block100000 = wire.MsgBlock{
}, },
}, },
HashMerkleRoot: &daghash.Hash{ HashMerkleRoot: &daghash.Hash{
0x30, 0xed, 0xf5, 0xbd, 0xd1, 0x4f, 0x8f, 0xb2, 0x32, 0x30, 0x46, 0x39, 0x5e, 0x27, 0x6d, 0x5a,
0x0b, 0x6c, 0x92, 0xac, 0xd2, 0x47, 0xb7, 0xd6, 0xc9, 0x64, 0x16, 0x29, 0x5b, 0xa4, 0x5a, 0xf3,
0x6f, 0x22, 0xfa, 0x60, 0x36, 0x80, 0x99, 0xc3, 0xc0, 0xfc, 0x1a, 0xa9, 0xcb, 0x2a, 0xd2, 0x9f,
0x6e, 0x39, 0x14, 0x9b, 0xcc, 0x1f, 0x31, 0xa9, 0xbe, 0x07, 0x0c, 0x47, 0xc9, 0x84, 0x39, 0x15,
}, },
AcceptedIDMerkleRoot: &daghash.Hash{ AcceptedIDMerkleRoot: &daghash.Hash{
0x8a, 0xb7, 0xd6, 0x73, 0x1b, 0xe6, 0xc5, 0xd3, 0x8a, 0xb7, 0xd6, 0x73, 0x1b, 0xe6, 0xc5, 0xd3,
@ -701,7 +725,7 @@ var Block100000 = wire.MsgBlock{
UTXOCommitment: &daghash.ZeroHash, UTXOCommitment: &daghash.ZeroHash,
Timestamp: time.Unix(0x5c404bc3, 0), Timestamp: time.Unix(0x5c404bc3, 0),
Bits: 0x207fffff, Bits: 0x207fffff,
Nonce: 0xdffffffffffffff9, Nonce: 0x00000001,
}, },
Transactions: []*wire.MsgTx{ Transactions: []*wire.MsgTx{
{ {
@ -709,27 +733,37 @@ var Block100000 = wire.MsgBlock{
TxIn: []*wire.TxIn{ TxIn: []*wire.TxIn{
{ {
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID{}, TxID: daghash.TxID{
0x9b, 0x22, 0x59, 0x44, 0x66, 0xf0, 0xbe, 0x50,
0x7c, 0x1c, 0x8a, 0xf6, 0x06, 0x27, 0xe6, 0x33,
0x38, 0x7e, 0xd1, 0xd5, 0x8c, 0x42, 0x59, 0x1a,
0x31, 0xac, 0x9a, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f,
},
Index: 0xffffffff, Index: 0xffffffff,
}, },
SignatureScript: []byte{ SignatureScript: nil,
0x02, 0x10, 0x27, 0x08, 0x8f, 0x22, 0xfb, 0x88, Sequence: math.MaxUint64,
0x45, 0x7b, 0xee, 0xeb, 0x0b, 0x2f, 0x50, 0x32,
0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, 0x64, 0x2f,
},
Sequence: math.MaxUint64,
}, },
}, },
TxOut: []*wire.TxOut{ TxOut: []*wire.TxOut{
{ {
Value: 0x12a05f200, // 5000000000 Value: 0x12a05f200, // 5000000000
PkScript: []byte{ PkScript: []byte{
0x51, 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49,
0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71, 0xc7,
0x7e, 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87,
}, },
}, },
}, },
LockTime: 0, LockTime: 0,
SubnetworkID: *subnetworkid.SubnetworkIDNative, SubnetworkID: *subnetworkid.SubnetworkIDCoinbase,
Payload: []byte{0x00},
PayloadHash: &daghash.Hash{
0x14, 0x06, 0xe0, 0x58, 0x81, 0xe2, 0x99, 0x36,
0x77, 0x66, 0xd3, 0x13, 0xe2, 0x6c, 0x05, 0x56,
0x4e, 0xc9, 0x1b, 0xf7, 0x21, 0xd3, 0x17, 0x26,
0xbd, 0x6e, 0x46, 0xe6, 0x06, 0x89, 0x53, 0x9a,
},
}, },
{ {
Version: 1, Version: 1,
@ -1006,34 +1040,37 @@ var BlockWithWrongTxOrder = wire.MsgBlock{
TxIn: []*wire.TxIn{ TxIn: []*wire.TxIn{
{ {
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID{}, TxID: daghash.TxID{
0x9b, 0x22, 0x59, 0x44, 0x66, 0xf0, 0xbe, 0x50,
0x7c, 0x1c, 0x8a, 0xf6, 0x06, 0x27, 0xe6, 0x33,
0x38, 0x7e, 0xd1, 0xd5, 0x8c, 0x42, 0x59, 0x1a,
0x31, 0xac, 0x9a, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f,
},
Index: 0xffffffff, Index: 0xffffffff,
}, },
SignatureScript: []byte{ SignatureScript: nil,
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02, Sequence: math.MaxUint64,
},
Sequence: math.MaxUint64,
}, },
}, },
TxOut: []*wire.TxOut{ TxOut: []*wire.TxOut{
{ {
Value: 0x12a05f200, // 5000000000 Value: 0x12a05f200, // 5000000000
PkScript: []byte{ PkScript: []byte{
0x41, // OP_DATA_65 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49,
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71, 0xc7,
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73, 0x7e, 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87,
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
0x84, // 65-byte signature
0xac, // OP_CHECKSIG
}, },
}, },
}, },
LockTime: 0, LockTime: 0,
SubnetworkID: *subnetworkid.SubnetworkIDCoinbase,
Payload: []byte{0x00},
PayloadHash: &daghash.Hash{
0x14, 0x06, 0xe0, 0x58, 0x81, 0xe2, 0x99, 0x36,
0x77, 0x66, 0xd3, 0x13, 0xe2, 0x6c, 0x05, 0x56,
0x4e, 0xc9, 0x1b, 0xf7, 0x21, 0xd3, 0x17, 0x26,
0xbd, 0x6e, 0x46, 0xe6, 0x06, 0x89, 0x53, 0x9a,
},
}, },
{ {
Version: 1, Version: 1,

View File

@ -90,10 +90,10 @@ func parseRawTransactionResult(result *btcjson.SearchRawTransactionsResult) (*wi
} }
func isTxMatured(tx *wire.MsgTx, confirmations uint64) bool { func isTxMatured(tx *wire.MsgTx, confirmations uint64) bool {
if !tx.IsBlockReward() { if !tx.IsCoinBase() {
return confirmations >= minConfirmations return confirmations >= minConfirmations
} }
return confirmations >= activeNetParams.BlockRewardMaturity return confirmations >= activeNetParams.BlockCoinbaseMaturity
} }
func buildUTXOs(txs []*wire.MsgTx) map[wire.Outpoint]*wire.MsgTx { func buildUTXOs(txs []*wire.MsgTx) map[wire.Outpoint]*wire.MsgTx {

View File

@ -30,13 +30,13 @@ const (
// PkScript bytes. // PkScript bytes.
outputSize uint64 = 8 + 1 + 25 outputSize uint64 = 8 + 1 + 25
txLifeSpan = 1000 txLifeSpan = 1000
requiredConfirmations = 10 requiredConfirmations = 10
approximateConfirmationsForBlockRewardMaturity = 150 approximateConfirmationsForCoinbaseMaturity = 150
searchRawTransactionResultCount = 1000 searchRawTransactionResultCount = 1000
searchRawTransactionMaxResults = 5000 searchRawTransactionMaxResults = 5000
txMaxQueueLength = 10000 txMaxQueueLength = 10000
maxResendDepth = 500 maxResendDepth = 500
) )
type walletTransaction struct { type walletTransaction struct {
@ -386,10 +386,10 @@ func addTxOutsToUTXOSet(walletUTXOSet utxoSet, tx *wire.MsgTx) {
} }
func isTxMatured(tx *wire.MsgTx, confirmations uint64) bool { func isTxMatured(tx *wire.MsgTx, confirmations uint64) bool {
if !tx.IsBlockReward() { if !tx.IsCoinBase() {
return confirmations >= requiredConfirmations return confirmations >= requiredConfirmations
} }
return confirmations >= approximateConfirmationsForBlockRewardMaturity return confirmations >= approximateConfirmationsForCoinbaseMaturity
} }
func calcUTXOSetFunds(walletUTXOSet utxoSet) uint64 { func calcUTXOSetFunds(walletUTXOSet utxoSet) uint64 {

View File

@ -80,7 +80,11 @@ func TestConstants(t *testing.T) {
t.Errorf("subnetworkid.SubnetworkIDNative value was changed from 0, therefore you probably need to update the help text for SubnetworkID") t.Errorf("subnetworkid.SubnetworkIDNative value was changed from 0, therefore you probably need to update the help text for SubnetworkID")
} }
one := subnetworkid.SubnetworkID{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} one := subnetworkid.SubnetworkID{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
if *subnetworkid.SubnetworkIDRegistry != one { if *subnetworkid.SubnetworkIDCoinbase != one {
t.Errorf("subnetworkid.SubnetworkIDRegistry value was changed from 1, therefore you probably need to update the help text for SubnetworkID") t.Errorf("subnetworkid.SubnetworkIDCoinbase value was changed from 1, therefore you probably need to update the help text for SubnetworkID")
}
two := subnetworkid.SubnetworkID{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
if *subnetworkid.SubnetworkIDRegistry != two {
t.Errorf("subnetworkid.SubnetworkIDRegistry value was changed from 2, therefore you probably need to update the help text for SubnetworkID")
} }
} }

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/daglabs/btcd/util/daghash" "github.com/daglabs/btcd/util/daghash"
"github.com/daglabs/btcd/util/subnetworkid"
"github.com/daglabs/btcd/wire" "github.com/daglabs/btcd/wire"
) )
@ -16,7 +17,7 @@ var genesisTxIns = []*wire.TxIn{
{ {
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID{}, TxID: daghash.TxID{},
Index: 0xffffffff, Index: math.MaxUint32,
}, },
SignatureScript: []byte{ SignatureScript: []byte{
0x00, 0x00, 0x0b, 0x2f, 0x50, 0x32, 0x53, 0x48, 0x00, 0x00, 0x0b, 0x2f, 0x50, 0x32, 0x53, 0x48,
@ -25,35 +26,35 @@ var genesisTxIns = []*wire.TxIn{
Sequence: math.MaxUint64, Sequence: math.MaxUint64,
}, },
} }
var genesisTxOuts = []*wire.TxOut{ var genesisTxOuts = []*wire.TxOut{}
{
Value: 0x12a05f200, var genesisTxPayload = []byte{
PkScript: []byte{ 0x17, // Varint
0x51, 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49, // OP-TRUE p2sh
}, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71, 0xc7,
}, 0x7e, 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87,
} }
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for // genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
// the main network, regression test network, and test network (version 3). // the main network, regression test network, and test network (version 3).
var genesisCoinbaseTx = wire.NewNativeMsgTx(1, genesisTxIns, genesisTxOuts) var genesisCoinbaseTx = wire.NewSubnetworkMsgTx(1, genesisTxIns, genesisTxOuts, subnetworkid.SubnetworkIDCoinbase, 0, genesisTxPayload)
// 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 DAG for the main
// network (genesis block). // network (genesis block).
var genesisHash = daghash.Hash([daghash.HashSize]byte{ var genesisHash = daghash.Hash([daghash.HashSize]byte{
0x65, 0xf3, 0x2f, 0x2d, 0x93, 0xb8, 0xff, 0x42, 0x9b, 0x22, 0x59, 0x44, 0x66, 0xf0, 0xbe, 0x50,
0x20, 0x1a, 0x31, 0xa7, 0xf9, 0x1c, 0x1a, 0x8c, 0x7c, 0x1c, 0x8a, 0xf6, 0x06, 0x27, 0xe6, 0x33,
0x27, 0x7e, 0xa1, 0x7b, 0xa7, 0x5a, 0x01, 0xfc, 0x38, 0x7e, 0xd1, 0xd5, 0x8c, 0x42, 0x59, 0x1a,
0x21, 0xb4, 0xbf, 0x7d, 0xc1, 0x81, 0xc0, 0x09, 0x31, 0xac, 0x9a, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f,
}) })
// genesisMerkleRoot is the hash of the first transaction in the genesis block // genesisMerkleRoot is the hash of the first transaction in the genesis block
// for the main network. // for the main network.
var genesisMerkleRoot = daghash.Hash([daghash.HashSize]byte{ var genesisMerkleRoot = daghash.Hash([daghash.HashSize]byte{
0xd4, 0xdc, 0x8b, 0xb8, 0x76, 0x57, 0x9d, 0x7d, 0x72, 0x10, 0x35, 0x85, 0xdd, 0xac, 0x82, 0x5c,
0xe9, 0x9d, 0xae, 0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0x49, 0x13, 0x9f, 0xc0, 0x0e, 0x37, 0xc0, 0x45,
0xa2, 0xe0, 0xbb, 0xbe, 0xed, 0xb0, 0xdb, 0xba, 0x71, 0xdf, 0xd9, 0xf6, 0x36, 0xdf, 0x4c, 0x42,
0xeb, 0x18, 0x4d, 0x42, 0x01, 0xff, 0xed, 0x9d, 0x72, 0x7b, 0x9e, 0x86, 0xdd, 0x37, 0xd2, 0xbd,
}) })
// genesisBlock defines the genesis block of the block DAG which serves as the // genesisBlock defines the genesis block of the block DAG which serves as the
@ -64,15 +65,10 @@ 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.ZeroHash,
0x51, 0x9f, 0x81, 0xbb, 0x93, 0xfd, 0x72, 0x9d, Timestamp: time.Unix(0x5cdac4b0, 0),
0x9d, 0xe0, 0x60, 0x80, 0xfa, 0x01, 0xe6, 0x34, Bits: 0x207fffff,
0x93, 0x22, 0xed, 0x67, 0x69, 0x14, 0x52, 0xca, Nonce: 0x1,
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},
} }
@ -120,13 +116,13 @@ var simNetGenesisBlock = genesisBlock
// the main network, regression test network, and test network (version 3). // the main network, regression test network, and test network (version 3).
var devNetGenesisCoinbaseTx = genesisCoinbaseTx var devNetGenesisCoinbaseTx = genesisCoinbaseTx
// devGenesisHash is the hash of the first block in the block chain for the development // devGenesisHash is the hash of the first block in the block DAG for the development
// network (genesis block). // network (genesis block).
var devNetGenesisHash = daghash.Hash([daghash.HashSize]byte{ var devNetGenesisHash = daghash.Hash([daghash.HashSize]byte{
0x7b, 0xea, 0x60, 0xdc, 0xaf, 0x9f, 0xc8, 0xf1, 0xf4, 0xd6, 0x37, 0x0b, 0xc6, 0x67, 0x41, 0x90,
0x28, 0x9d, 0xbf, 0xde, 0xf6, 0x3b, 0x1e, 0xe8, 0x06, 0x57, 0xef, 0x65, 0x45, 0x07, 0x3a, 0x50,
0x4d, 0x4c, 0xc1, 0x8c, 0xaa, 0xe7, 0xdf, 0x08, 0xb7, 0x85, 0x21, 0xb9, 0xa2, 0xe3, 0xf5, 0x9e,
0xe2, 0xe7, 0xa3, 0x6a, 0x4c, 0x7c, 0x00, 0x00, 0xe0, 0x45, 0x9b, 0xb0, 0xab, 0x33, 0x00, 0x00,
}) })
// devNetGenesisMerkleRoot is the hash of the first transaction in the genesis block // devNetGenesisMerkleRoot is the hash of the first transaction in the genesis block
@ -141,15 +137,10 @@ var devNetGenesisBlock = wire.MsgBlock{
ParentHashes: []*daghash.Hash{}, ParentHashes: []*daghash.Hash{},
HashMerkleRoot: &devNetGenesisMerkleRoot, HashMerkleRoot: &devNetGenesisMerkleRoot,
AcceptedIDMerkleRoot: &daghash.Hash{}, AcceptedIDMerkleRoot: &daghash.Hash{},
UTXOCommitment: &daghash.Hash{ UTXOCommitment: &daghash.ZeroHash,
0x51, 0x9f, 0x81, 0xbb, 0x93, 0xfd, 0x72, 0x9d, Timestamp: time.Unix(0x5d021d3e, 0),
0x9d, 0xe0, 0x60, 0x80, 0xfa, 0x01, 0xe6, 0x34, Bits: 0x1e7fffff,
0x93, 0x22, 0xed, 0x67, 0x69, 0x14, 0x52, 0xca, Nonce: 0x5221,
0x95, 0xd1, 0x9b, 0x77, 0xdb, 0xb8, 0x12, 0x80,
},
Timestamp: time.Unix(0x5cee6864, 0),
Bits: 0x1e7fffff,
Nonce: 0xe834,
}, },
Transactions: []*wire.MsgTx{devNetGenesisCoinbaseTx}, Transactions: []*wire.MsgTx{devNetGenesisCoinbaseTx},
} }

View File

@ -121,35 +121,24 @@ 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, 0x72, 0x10, 0x35, 0x85, 0xdd, 0xac, 0x82, 0x5c, 0x49, 0x13, 0x9f,
0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae, 0xc0, 0x0e, 0x37, 0xc0, 0x45, 0x71, 0xdf, 0xd9, 0xf6, 0x36, 0xdf, 0x4c, 0x42, 0x72, 0x7b, 0x9e,
0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb, 0x86, 0xdd, 0x37, 0xd2, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0xb0, 0xc4, 0xda, 0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x9f, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xbb, 0x93, 0xfd, 0x72, 0x9d, 0x9d, 0xe0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x80, 0xfa, 0x01, 0xe6, 0x34, 0x93, 0x22, 0xed, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f, 0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63,
0x67, 0x69, 0x14, 0x52, 0xca, 0x95, 0xd1, 0x9b, 0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x77, 0xdb, 0xb8, 0x12, 0x80, 0xb0, 0xc4, 0xda, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2,
0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x82, 0x4e, 0xb8, 0x87, 0x42, 0xd0, 0x6d, 0x1f, 0x8d, 0xc3, 0xad, 0x9f, 0x43, 0x9e, 0xed,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x6f, 0x43, 0x3c, 0x02, 0x71, 0x71, 0x69, 0xfb, 0xbc, 0x91, 0x44, 0xac, 0xf1, 0x93, 0xd3, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x7e, 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f,
0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63,
0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01,
0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
} }
// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of // regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of

View File

@ -128,9 +128,9 @@ type Params struct {
// block in compact form. // block in compact form.
PowLimitBits uint32 PowLimitBits uint32
// BlockRewardMaturity is the number of blocks required before newly mined // BlockCoinbaseMaturity is the number of blocks required before newly mined
// coins (coinbase or fee transactions) can be spent. // coins can be spent.
BlockRewardMaturity uint64 BlockCoinbaseMaturity uint64
// SubsidyReductionInterval is the interval of blocks before the subsidy // SubsidyReductionInterval is the interval of blocks before the subsidy
// is reduced. // is reduced.
@ -221,7 +221,7 @@ var MainNetParams = Params{
GenesisHash: &genesisHash, GenesisHash: &genesisHash,
PowLimit: mainPowLimit, PowLimit: mainPowLimit,
PowLimitBits: 0x207fffff, PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100, BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000, SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second TargetTimePerBlock: time.Second * 1, // 1 second
@ -284,7 +284,7 @@ var RegressionNetParams = Params{
GenesisHash: &regTestGenesisHash, GenesisHash: &regTestGenesisHash,
PowLimit: regressionPowLimit, PowLimit: regressionPowLimit,
PowLimitBits: 0x207fffff, PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100, BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 150, SubsidyReductionInterval: 150,
TargetTimespan: time.Hour * 1, // 1 hour TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second TargetTimePerBlock: time.Second * 1, // 1 second
@ -347,7 +347,7 @@ var TestNet3Params = Params{
GenesisHash: &testNet3GenesisHash, GenesisHash: &testNet3GenesisHash,
PowLimit: testNet3PowLimit, PowLimit: testNet3PowLimit,
PowLimitBits: 0x207fffff, PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100, BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000, SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second TargetTimePerBlock: time.Second * 1, // 1 second
@ -414,7 +414,7 @@ var SimNetParams = Params{
GenesisHash: &simNetGenesisHash, GenesisHash: &simNetGenesisHash,
PowLimit: simNetPowLimit, PowLimit: simNetPowLimit,
PowLimitBits: 0x207fffff, PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100, BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000, SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second TargetTimePerBlock: time.Second * 1, // 1 second
@ -473,7 +473,7 @@ var DevNetParams = Params{
GenesisHash: &devNetGenesisHash, GenesisHash: &devNetGenesisHash,
PowLimit: devNetPowLimit, PowLimit: devNetPowLimit,
PowLimitBits: util.BigToCompact(devNetPowLimit), // 0x1e7fffff PowLimitBits: util.BigToCompact(devNetPowLimit), // 0x1e7fffff
BlockRewardMaturity: 100, BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000, SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second TargetTimePerBlock: time.Second * 1, // 1 second

View File

@ -175,5 +175,5 @@ func Example_blockStorageAndRetrieval() {
fmt.Printf("Serialized block size: %d bytes\n", len(loadedBlockBytes)) fmt.Printf("Serialized block size: %d bytes\n", len(loadedBlockBytes))
// Output: // Output:
// Serialized block size: 225 bytes // Serialized block size: 280 bytes
} }

View File

@ -261,7 +261,7 @@ func (m *memWallet) evalOutputs(outputs []*wire.TxOut, txID *daghash.TxID,
// future. // future.
var maturityHeight uint64 var maturityHeight uint64
if isCoinbase { if isCoinbase {
maturityHeight = m.currentHeight + m.net.BlockRewardMaturity maturityHeight = m.currentHeight + m.net.BlockCoinbaseMaturity
} }
op := wire.Outpoint{TxID: *txID, Index: uint32(i)} op := wire.Outpoint{TxID: *txID, Index: uint32(i)}

View File

@ -232,7 +232,7 @@ func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error {
// Create a test chain with the desired number of mature coinbase // Create a test chain with the desired number of mature coinbase
// outputs. // outputs.
if createTestChain && numMatureOutputs != 0 { if createTestChain && numMatureOutputs != 0 {
numToGenerate := (uint32(h.ActiveNet.BlockRewardMaturity) + numToGenerate := (uint32(h.ActiveNet.BlockCoinbaseMaturity) +
numMatureOutputs) numMatureOutputs)
_, err := h.Node.Generate(numToGenerate) _, err := h.Node.Generate(numToGenerate)
if err != nil { if err != nil {

View File

@ -842,9 +842,9 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rejectDupOrphans bo
} }
} }
// A standalone transaction must not be a block reward transaction. // A standalone transaction must not be a coinbase transaction.
if blockdag.IsBlockReward(tx) { if tx.IsCoinBase() {
str := fmt.Sprintf("transaction %s is an individual block reward transaction", str := fmt.Sprintf("transaction %s is an individual coinbase transaction",
txID) txID)
return nil, nil, txRuleError(wire.RejectInvalid, str) return nil, nil, txRuleError(wire.RejectInvalid, str)
} }
@ -1360,12 +1360,12 @@ func (mp *TxPool) HandleNewBlock(block *util.Block, txChan chan NewBlockMsg) err
// no longer an orphan. Transactions which depend on a confirmed // no longer an orphan. Transactions which depend on a confirmed
// transaction are NOT removed recursively because they are still // transaction are NOT removed recursively because they are still
// valid. // valid.
err := mp.RemoveTransactions(block.Transactions()[util.FeeTransactionIndex+1:]) err := mp.RemoveTransactions(block.Transactions()[util.CoinbaseTransactionIndex+1:])
if err != nil { if err != nil {
mp.mpUTXOSet = oldUTXOSet mp.mpUTXOSet = oldUTXOSet
return err return err
} }
for _, tx := range block.Transactions()[util.FeeTransactionIndex+1:] { for _, tx := range block.Transactions()[util.CoinbaseTransactionIndex+1:] {
mp.RemoveDoubleSpends(tx) mp.RemoveDoubleSpends(tx)
mp.RemoveOrphan(tx) mp.RemoveOrphan(tx)
acceptedTxs := mp.ProcessOrphans(tx) acceptedTxs := mp.ProcessOrphans(tx)

View File

@ -121,7 +121,7 @@ func (p *poolHarness) CreateCoinbaseTx(blueScore uint64, numOutputs uint32) (*ut
// Create standard coinbase script. // Create standard coinbase script.
extraNonce := int64(0) extraNonce := int64(0)
coinbaseScript, err := txscript.NewScriptBuilder(). coinbaseScript, err := txscript.NewScriptBuilder().
AddInt64(int64(blueScore)).AddInt64(extraNonce).Script() AddInt64(extraNonce).Script()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -245,39 +245,55 @@ func (p *poolHarness) CreateTxChain(firstOutput spendableOutpoint, numTxns uint3
return txChain, nil return txChain, nil
} }
func (tc *testContext) mineTransactions(transactions []*util.Tx, coinbaseOutputs uint64) *util.Block { func (tc *testContext) mineTransactions(transactions []*util.Tx, numberOfBlocks uint64) []spendableOutpoint {
var outpoints []spendableOutpoint
msgTxs := make([]*wire.MsgTx, len(transactions)) msgTxs := make([]*wire.MsgTx, len(transactions))
for i, tx := range transactions { for i, tx := range transactions {
msgTxs[i] = tx.MsgTx() msgTxs[i] = tx.MsgTx()
} }
block, err := mining.PrepareBlockForTest(tc.harness.txPool.cfg.DAG, tc.harness.txPool.cfg.DAGParams, tc.harness.txPool.cfg.DAG.TipHashes(), msgTxs, true, coinbaseOutputs) for i := uint64(0); i < numberOfBlocks; i++ {
if err != nil { var blockTxs []*wire.MsgTx
tc.t.Fatalf("PrepareBlockForTest: %s", err) if i == 0 {
} blockTxs = msgTxs
utilBlock := util.NewBlock(block) }
isOrphan, err := tc.harness.txPool.cfg.DAG.ProcessBlock(utilBlock, blockdag.BFNoPoWCheck) block, err := mining.PrepareBlockForTest(tc.harness.txPool.cfg.DAG, tc.harness.txPool.cfg.DAGParams, tc.harness.txPool.cfg.DAG.TipHashes(), blockTxs, true)
if err != nil { if err != nil {
tc.t.Fatalf("ProcessBlock: %s", err) tc.t.Fatalf("PrepareBlockForTest: %s", err)
} }
if isOrphan { utilBlock := util.NewBlock(block)
tc.t.Fatalf("block %s was unexpectedly orphan", block.BlockHash()) isOrphan, err := tc.harness.txPool.cfg.DAG.ProcessBlock(utilBlock, blockdag.BFNoPoWCheck)
} if err != nil {
tc.t.Fatalf("ProcessBlock: %s", err)
}
if isOrphan {
tc.t.Fatalf("block %s was unexpectedly orphan", block.BlockHash())
}
// Handle new block by pool // Handle new block by pool
ch := make(chan NewBlockMsg) ch := make(chan NewBlockMsg)
go func() { go func() {
err = tc.harness.txPool.HandleNewBlock(utilBlock, ch) err = tc.harness.txPool.HandleNewBlock(utilBlock, ch)
close(ch) close(ch)
}() }()
// process messages pushed by HandleNewBlock // process messages pushed by HandleNewBlock
for range ch { for range ch {
}
// ensure that HandleNewBlock has not failed
if err != nil {
tc.t.Fatalf("HandleNewBlock failed to handle block %s", err)
}
coinbaseTx := block.Transactions[util.CoinbaseTransactionIndex]
for i, txOut := range coinbaseTx.TxOut {
outpoints = append(outpoints, spendableOutpoint{
outpoint: *wire.NewOutpoint(coinbaseTx.TxID(), uint32(i)),
amount: util.Amount(txOut.Value),
})
}
} }
// ensure that HandleNewBlock has not failed return outpoints
if err != nil {
tc.t.Fatalf("HandleNewBlock failed to handle block %s", err)
}
return utilBlock
} }
// newPoolHarness returns a new instance of a pool harness initialized with a // newPoolHarness returns a new instance of a pool harness initialized with a
@ -292,7 +308,7 @@ func newPoolHarness(t *testing.T, dagParams *dagconfig.Params, numOutputs uint32
} }
params := *dagParams params := *dagParams
params.BlockRewardMaturity = 0 params.BlockCoinbaseMaturity = 0
// Create a new database and chain instance to run tests against. // Create a new database and chain instance to run tests against.
dag, teardownFunc, err := blockdag.DAGSetup(dbName, blockdag.Config{ dag, teardownFunc, err := blockdag.DAGSetup(dbName, blockdag.Config{
@ -339,21 +355,12 @@ func newPoolHarness(t *testing.T, dagParams *dagconfig.Params, numOutputs uint32
} }
tc := &testContext{harness: harness, t: t} tc := &testContext{harness: harness, t: t}
block := tc.mineTransactions(nil, uint64(numOutputs))
coinbase := block.Transactions()[0]
// Create a single coinbase transaction and add it to the harness // Mine numOutputs blocks to get numOutputs coinbase outpoints
// chain's utxo set and set the harness chain height such that the outpoints := tc.mineTransactions(nil, uint64(numOutputs))
// coinbase will mature in the next block. This ensures the txpool
// accepts transactions which spend immature coinbases that will become
// mature in the next block.
outpoints := make([]spendableOutpoint, 0, numOutputs)
curHeight := harness.chain.BestHeight() curHeight := harness.chain.BestHeight()
for i := uint32(0); i < numOutputs; i++ { if params.BlockCoinbaseMaturity != 0 {
outpoints = append(outpoints, txOutToSpendableOutpoint(coinbase, i)) harness.chain.SetHeight(params.BlockCoinbaseMaturity + curHeight)
}
if dagParams.BlockRewardMaturity != 0 {
harness.chain.SetHeight(dagParams.BlockRewardMaturity + curHeight)
} else { } else {
harness.chain.SetHeight(curHeight + 1) harness.chain.SetHeight(curHeight + 1)
} }
@ -472,7 +479,7 @@ func (p *poolHarness) createTx(outpoint spendableOutpoint, fee uint64, numOutput
func TestProcessTransaction(t *testing.T) { func TestProcessTransaction(t *testing.T) {
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.BlockRewardMaturity = 0 params.BlockCoinbaseMaturity = 0
tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &params, 6, "TestProcessTransaction") tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &params, 6, "TestProcessTransaction")
if err != nil { if err != nil {
t.Fatalf("unable to create test pool: %v", err) t.Fatalf("unable to create test pool: %v", err)
@ -716,7 +723,7 @@ func TestProcessTransaction(t *testing.T) {
harness.txPool.cfg.CalcSequenceLockNoLock = calcSequenceLock harness.txPool.cfg.CalcSequenceLockNoLock = calcSequenceLock
//Transaction should be rejected from mempool because it has low fee, and its priority is above mining.MinHighPriority //Transaction should be rejected from mempool because it has low fee, and its priority is above mining.MinHighPriority
tx, err = harness.createTx(spendableOuts[4], 0, 100) tx, err = harness.createTx(spendableOuts[4], 0, 1000)
if err != nil { if err != nil {
t.Fatalf("unable to create transaction: %v", err) t.Fatalf("unable to create transaction: %v", err)
} }
@ -791,7 +798,7 @@ func TestAddrIndex(t *testing.T) {
} }
func TestDoubleSpends(t *testing.T) { func TestDoubleSpends(t *testing.T) {
tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &dagconfig.MainNetParams, 2, "TestDoubleSpends") tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &dagconfig.SimNetParams, 2, "TestDoubleSpends")
if err != nil { if err != nil {
t.Fatalf("unable to create test pool: %v", err) t.Fatalf("unable to create test pool: %v", err)
} }
@ -803,13 +810,19 @@ func TestDoubleSpends(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to create transaction: %v", err) t.Fatalf("unable to create transaction: %v", err)
} }
harness.txPool.ProcessTransaction(tx1, true, 0) _, err = harness.txPool.ProcessTransaction(tx1, true, 0)
if err != nil {
t.Fatalf("ProcessTransaction: %s", err)
}
tx2, err := harness.createTx(spendableOuts[1], uint64(txRelayFeeForTest)+1, 1) tx2, err := harness.createTx(spendableOuts[1], uint64(txRelayFeeForTest)+1, 1)
if err != nil { if err != nil {
t.Fatalf("unable to create transaction: %v", err) t.Fatalf("unable to create transaction: %v", err)
} }
harness.txPool.ProcessTransaction(tx2, true, 0) _, err = harness.txPool.ProcessTransaction(tx2, true, 0)
if err != nil {
t.Fatalf("ProcessTransaction: %s", err)
}
testPoolMembership(tc, tx1, false, true, false) testPoolMembership(tc, tx1, false, true, false)
testPoolMembership(tc, tx2, false, true, false) testPoolMembership(tc, tx2, false, true, false)
@ -1761,60 +1774,44 @@ var dummyBlock = wire.MsgBlock{
TxIn: []*wire.TxIn{ TxIn: []*wire.TxIn{
{ {
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID{}, TxID: daghash.TxID{
0x9b, 0x22, 0x59, 0x44, 0x66, 0xf0, 0xbe, 0x50,
0x7c, 0x1c, 0x8a, 0xf6, 0x06, 0x27, 0xe6, 0x33,
0x38, 0x7e, 0xd1, 0xd5, 0x8c, 0x42, 0x59, 0x1a,
0x31, 0xac, 0x9a, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f,
},
Index: 0xffffffff, Index: 0xffffffff,
}, },
SignatureScript: []byte{ SignatureScript: nil,
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02, Sequence: math.MaxUint64,
},
Sequence: math.MaxUint64,
}, },
}, },
TxOut: []*wire.TxOut{ TxOut: []*wire.TxOut{
{ {
Value: 0x12a05f200, // 5000000000 Value: 0x12a05f200, // 5000000000
PkScript: []byte{ PkScript: []byte{
0x41, // OP_DATA_65 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49,
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71, 0xc7,
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73, 0x7e, 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87,
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
0x84, // 65-byte signature
0xac, // OP_CHECKSIG
}, },
}, },
}, },
LockTime: 0, LockTime: 0,
SubnetworkID: *subnetworkid.SubnetworkIDNative, SubnetworkID: *subnetworkid.SubnetworkIDCoinbase,
}, Payload: []byte{0x00},
{ PayloadHash: &daghash.Hash{
Version: 1, 0x14, 0x06, 0xe0, 0x58, 0x81, 0xe2, 0x99, 0x36,
TxIn: []*wire.TxIn{ 0x77, 0x66, 0xd3, 0x13, 0xe2, 0x6c, 0x05, 0x56,
{ 0x4e, 0xc9, 0x1b, 0xf7, 0x21, 0xd3, 0x17, 0x26,
PreviousOutpoint: wire.Outpoint{ 0xbd, 0x6e, 0x46, 0xe6, 0x06, 0x89, 0x53, 0x9a,
TxID: daghash.TxID{
0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95,
0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3,
0x46, 0x11, 0x89, 0x6b, 0x82, 0x1a, 0x68, 0x3b,
0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
},
Index: 0xffffffff,
},
Sequence: math.MaxUint64,
},
}, },
SubnetworkID: *subnetworkid.SubnetworkIDNative,
}, },
}, },
} }
func TestTransactionGas(t *testing.T) { func TestTransactionGas(t *testing.T) {
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.BlockRewardMaturity = 1 params.BlockCoinbaseMaturity = 1
tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &params, 6, "TestTransactionGas") tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &params, 6, "TestTransactionGas")
if err != nil { if err != nil {
t.Fatalf("unable to create test pool: %v", err) t.Fatalf("unable to create test pool: %v", err)

View File

@ -191,9 +191,7 @@ func (m *CPUMiner) submitBlock(block *util.Block) bool {
} }
// The block was accepted. // The block was accepted.
coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0] log.Infof("Block submitted via CPU miner accepted (hash %s)", block.Hash())
log.Infof("Block submitted via CPU miner accepted (hash %s, "+
"amount %d)", block.Hash(), util.Amount(coinbaseTx.Value))
return true return true
} }

View File

@ -5,7 +5,9 @@
package mining package mining
import ( import (
"bytes"
"container/heap" "container/heap"
"encoding/binary"
"fmt" "fmt"
"sort" "sort"
"time" "time"
@ -180,63 +182,9 @@ type BlockTemplate struct {
ValidPayAddress bool ValidPayAddress bool
} }
// StandardCoinbaseScript returns a standard script suitable for use as the
// signature script of the coinbase transaction of a new block. In particular,
// it starts with the block blue score that is required by version 2 blocks and adds
// the extra nonce as well as additional coinbase flags.
func StandardCoinbaseScript(nextBlueScore uint64, extraNonce uint64) ([]byte, error) {
return txscript.NewScriptBuilder().AddInt64(int64(nextBlueScore)).
AddInt64(int64(extraNonce)).AddData([]byte(CoinbaseFlags)).
Script()
}
// CreateCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
// based on the passed block blue score to the provided address. When the address
// is nil, the coinbase transaction will instead be redeemable by anyone.
//
// See the comment for NewBlockTemplate for more information about why the nil
// address handling is useful.
func CreateCoinbaseTx(params *dagconfig.Params, coinbaseScript []byte, nextBlueScore uint64, addr util.Address) (*util.Tx, error) {
// Create the script to pay to the provided payment address if one was
// specified. Otherwise create a script that allows the coinbase to be
// redeemable by anyone.
var pkScript []byte
if addr != nil {
var err error
pkScript, err = txscript.PayToAddrScript(addr)
if err != nil {
return nil, err
}
} else {
scriptBuilder := txscript.NewScriptBuilder()
opTrueScript, err := scriptBuilder.AddOp(txscript.OpTrue).Script()
if err != nil {
return nil, err
}
pkScript, err = txscript.PayToScriptHashScript(opTrueScript)
if err != nil {
return nil, err
}
}
txIn := &wire.TxIn{
// Coinbase transactions have no inputs, so previous outpoint is
// zero hash and max index.
PreviousOutpoint: *wire.NewOutpoint(&daghash.TxID{},
wire.MaxPrevOutIndex),
SignatureScript: coinbaseScript,
Sequence: wire.MaxTxInSequenceNum,
}
txOut := &wire.TxOut{
Value: blockdag.CalcBlockSubsidy(nextBlueScore, params),
PkScript: pkScript,
}
return util.NewTx(wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut})), nil
}
// MinimumMedianTime returns the minimum allowed timestamp for a block building // MinimumMedianTime returns the minimum allowed timestamp for a block building
// on the end of the provided best chain. In particular, it is one second after // on the end of the DAG. In particular, it is one second after
// the median timestamp of the last several blocks per the chain consensus // the median timestamp of the last several blocks per the DAG consensus
// rules. // rules.
func MinimumMedianTime(dagMedianTime time.Time) time.Time { func MinimumMedianTime(dagMedianTime time.Time) time.Time {
return dagMedianTime.Add(time.Second) return dagMedianTime.Add(time.Second)
@ -361,36 +309,27 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
g.dag.RLock() g.dag.RLock()
defer g.dag.RUnlock() defer g.dag.RUnlock()
// Create a standard coinbase transaction paying to the provided
// address. NOTE: The coinbase value will be updated to include the
// fees from the selected transactions later after they have actually
// been selected. It is created here to detect any errors early
// before potentially doing a lot of work below. The extra nonce helps
// ensure the transaction is not a duplicate transaction (paying the
// same value to the same public key address would otherwise be an
// identical transaction for block version 1).
nextBlockBlueScore := g.dag.VirtualBlueScore() nextBlockBlueScore := g.dag.VirtualBlueScore()
coinbasePayloadPkScript, err := txscript.PayToAddrScript(payToAddress)
if err != nil {
return nil, err
}
extraNonce, err := random.Uint64() extraNonce, err := random.Uint64()
if err != nil { if err != nil {
return nil, err return nil, err
} }
coinbaseScript, err := StandardCoinbaseScript(nextBlockBlueScore, extraNonce) coinbasePayloadExtraData, err := CoinbasePayloadExtraData(extraNonce)
if err != nil { if err != nil {
return nil, err return nil, err
} }
coinbaseTx, err := CreateCoinbaseTx(g.dagParams, coinbaseScript, nextBlockBlueScore, payToAddress)
coinbaseTx, err := g.dag.NextBlockCoinbaseTransactionNoLock(coinbasePayloadPkScript, coinbasePayloadExtraData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
numCoinbaseSigOps := int64(blockdag.CountSigOps(coinbaseTx)) numCoinbaseSigOps := int64(blockdag.CountSigOps(coinbaseTx))
msgFeeTransaction, err := g.dag.NextBlockFeeTransactionNoLock()
if err != nil {
return nil, err
}
feeTransaction := util.NewTx(msgFeeTransaction)
feeTxSigOps := int64(blockdag.CountSigOps(feeTransaction))
// Get the current source transactions and create a priority queue to // Get the current source transactions and create a priority queue to
// hold the transactions which are ready for inclusion into a block // hold the transactions which are ready for inclusion into a block
// along with some priority related and fee metadata. Reserve the same // along with some priority related and fee metadata. Reserve the same
@ -404,14 +343,14 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
// generated block with reserved space. Also create a utxo view to // generated block with reserved space. Also create a utxo view to
// house all of the input transactions so multiple lookups can be // house all of the input transactions so multiple lookups can be
// avoided. // avoided.
blockTxns := make([]*util.Tx, 0, len(sourceTxns)+2) blockTxns := make([]*util.Tx, 0, len(sourceTxns)+1)
blockTxns = append(blockTxns, coinbaseTx, feeTransaction) blockTxns = append(blockTxns, coinbaseTx)
// The starting block size is the size of the block header plus the max // The starting block size is the size of the block header plus the max
// possible transaction count size, plus the size of the coinbase // possible transaction count size, plus the size of the coinbase
// transaction. // transaction.
blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize()) blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize())
blockSigOps := numCoinbaseSigOps + feeTxSigOps blockSigOps := numCoinbaseSigOps
totalFees := uint64(0) totalFees := uint64(0)
// Create slices to hold the fees and number of signature operations // Create slices to hold the fees and number of signature operations
@ -420,10 +359,10 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
// a transaction as it is selected for inclusion in the final block. // a transaction as it is selected for inclusion in the final block.
// However, since the total fees aren't known yet, use a dummy value for // However, since the total fees aren't known yet, use a dummy value for
// the coinbase fee which will be updated later. // the coinbase fee which will be updated later.
txFees := make([]uint64, 0, len(sourceTxns)+2) txFees := make([]uint64, 0, len(sourceTxns)+1)
txSigOpCounts := make([]int64, 0, len(sourceTxns)+2) txSigOpCounts := make([]int64, 0, len(sourceTxns)+1)
txFees = append(txFees, 0, 0) // For coinbase and fee txs txFees = append(txFees, 0) // For coinbase tx
txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps, feeTxSigOps) txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps)
log.Debugf("Considering %d transactions for inclusion to new block", log.Debugf("Considering %d transactions for inclusion to new block",
len(sourceTxns)) len(sourceTxns))
@ -431,8 +370,8 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
// A block can't have more than one coinbase or contain // A block can't have more than one coinbase or contain
// non-finalized transactions. // non-finalized transactions.
tx := txDesc.Tx tx := txDesc.Tx
if blockdag.IsBlockReward(tx) { if tx.IsCoinBase() {
log.Tracef("Skipping block reward tx %s", tx.ID()) log.Tracef("Skipping coinbase tx %s", tx.ID())
continue continue
} }
if !blockdag.IsFinalizedTransaction(tx, nextBlockBlueScore, if !blockdag.IsFinalizedTransaction(tx, nextBlockBlueScore,
@ -464,7 +403,7 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
prioItem := heap.Pop(priorityQueue).(*txPrioItem) prioItem := heap.Pop(priorityQueue).(*txPrioItem)
tx := prioItem.tx tx := prioItem.tx
if !tx.MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) && !tx.MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry) { if !tx.MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) && !tx.MsgTx().SubnetworkID.IsBuiltIn() {
subnetworkID := tx.MsgTx().SubnetworkID subnetworkID := tx.MsgTx().SubnetworkID
gasUsage, ok := gasUsageMap[subnetworkID] gasUsage, ok := gasUsageMap[subnetworkID]
if !ok { if !ok {
@ -574,6 +513,12 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
// Sort transactions by subnetwork ID before building Merkle tree // Sort transactions by subnetwork ID before building Merkle tree
sort.Slice(blockTxns, func(i, j int) bool { sort.Slice(blockTxns, func(i, j int) bool {
if blockTxns[i].MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDCoinbase) {
return true
}
if blockTxns[j].MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDCoinbase) {
return false
}
return subnetworkid.Less(&blockTxns[i].MsgTx().SubnetworkID, &blockTxns[j].MsgTx().SubnetworkID) return subnetworkid.Less(&blockTxns[i].MsgTx().SubnetworkID, &blockTxns[j].MsgTx().SubnetworkID)
}) })
@ -623,6 +568,23 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
}, nil }, nil
} }
// CoinbasePayloadExtraData returns coinbase payload extra data parameter
// which is built from extra nonce and coinbase flags.
func CoinbasePayloadExtraData(extraNonce uint64) ([]byte, error) {
extraNonceBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(extraNonceBytes, extraNonce)
w := &bytes.Buffer{}
_, err := w.Write(extraNonceBytes)
if err != nil {
return nil, err
}
_, err = w.Write([]byte(CoinbaseFlags))
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx, nextBlueScore uint64) (*daghash.Hash, error) { func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx, nextBlueScore uint64) (*daghash.Hash, error) {
utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, nextBlueScore, false) utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, nextBlueScore, false)
if err != nil { if err != nil {
@ -662,21 +624,31 @@ func (g *BlkTmplGenerator) UpdateBlockTime(msgBlock *wire.MsgBlock) error {
// height. It also recalculates and updates the new merkle root that results // height. It also recalculates and updates the new merkle root that results
// from changing the coinbase script. // from changing the coinbase script.
func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockBlueScore uint64, extraNonce uint64) error { func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockBlueScore uint64, extraNonce uint64) error {
coinbaseScript, err := StandardCoinbaseScript(blockBlueScore, extraNonce) coinbasePayloadPkScript, _, err := blockdag.DeserializeCoinbasePayload(msgBlock.Transactions[util.CoinbaseTransactionIndex])
if err != nil { if err != nil {
return err return err
} }
if len(coinbaseScript) > blockdag.MaxCoinbaseScriptLen {
return fmt.Errorf("coinbase transaction script length "+ coinbasePayloadExtraData, err := CoinbasePayloadExtraData(extraNonce)
"of %d is out of range (min: %d, max: %d)", if err != nil {
len(coinbaseScript), blockdag.MinCoinbaseScriptLen, return err
blockdag.MaxCoinbaseScriptLen)
} }
msgBlock.Transactions[0].TxIn[0].SignatureScript = coinbaseScript coinbasePayload, err := blockdag.SerializeCoinbasePayload(coinbasePayloadPkScript, coinbasePayloadExtraData)
if err != nil {
return err
}
if len(coinbasePayload) > blockdag.MaxCoinbasePayloadLen {
return fmt.Errorf("coinbase transaction script length "+
"of %d is out of range (max: %d)",
len(coinbasePayload),
blockdag.MaxCoinbasePayloadLen)
}
oldCoinbaseTx := msgBlock.Transactions[util.CoinbaseTransactionIndex]
msgBlock.Transactions[util.CoinbaseTransactionIndex] = wire.NewSubnetworkMsgTx(oldCoinbaseTx.Version, oldCoinbaseTx.TxIn, oldCoinbaseTx.TxOut, &oldCoinbaseTx.SubnetworkID, oldCoinbaseTx.Gas, coinbasePayload)
// TODO(davec): A util.Block should use saved in the state to avoid // TODO(davec): A util.Block should use saved in the state to avoid
// recalculating all of the other transaction hashes. // recalculating all of the other transaction hashes.
// block.Transactions[0].InvalidateCache() // block.Transactions[util.CoinbaseTransactionIndex].InvalidateCache()
// Recalculate the merkle roots with the updated extra nonce. // Recalculate the merkle roots with the updated extra nonce.
block := util.NewBlock(msgBlock) block := util.NewBlock(msgBlock)

View File

@ -73,7 +73,7 @@ func TestTxFeePrioHeap(t *testing.T) {
func TestNewBlockTemplate(t *testing.T) { func TestNewBlockTemplate(t *testing.T) {
params := dagconfig.SimNetParams params := dagconfig.SimNetParams
params.BlockRewardMaturity = 0 params.BlockCoinbaseMaturity = 0
dag, teardownFunc, err := blockdag.DAGSetup("TestNewBlockTemplate", blockdag.Config{ dag, teardownFunc, err := blockdag.DAGSetup("TestNewBlockTemplate", blockdag.Config{
DAGParams: &params, DAGParams: &params,
@ -97,30 +97,14 @@ func TestNewBlockTemplate(t *testing.T) {
txDescs: []*TxDesc{}, txDescs: []*TxDesc{},
} }
var createCoinbaseTxPatch *monkey.PatchGuard
createCoinbaseTxPatch = monkey.Patch(CreateCoinbaseTx, func(params *dagconfig.Params, coinbaseScript []byte, nextBlueScore uint64, addr util.Address) (*util.Tx, error) {
createCoinbaseTxPatch.Unpatch()
defer createCoinbaseTxPatch.Restore()
tx, err := CreateCoinbaseTx(params, coinbaseScript, nextBlueScore, addr)
if err != nil {
return nil, err
}
msgTx := tx.MsgTx()
//Here we split the coinbase to 10 outputs, so we'll be able to use it in many transactions
out := msgTx.TxOut[0]
out.Value /= 10
for i := 0; i < 9; i++ {
msgTx.AddTxOut(&*out)
}
return tx, nil
})
defer createCoinbaseTxPatch.Unpatch()
blockTemplateGenerator := NewBlkTmplGenerator(&policy, blockTemplateGenerator := NewBlkTmplGenerator(&policy,
&params, txSource, dag, blockdag.NewMedianTime(), txscript.NewSigCache(100000)) &params, txSource, dag, blockdag.NewMedianTime(), txscript.NewSigCache(100000))
template1, err := blockTemplateGenerator.NewBlockTemplate(nil) OpTrueAddr, err := OpTrueAddress(params.Prefix)
createCoinbaseTxPatch.Unpatch() if err != nil {
t.Fatalf("OpTrueAddress: %s", err)
}
template1, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
if err != nil { if err != nil {
t.Fatalf("NewBlockTemplate: %v", err) t.Fatalf("NewBlockTemplate: %v", err)
} }
@ -129,24 +113,33 @@ func TestNewBlockTemplate(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("ProcessBlock: %v", err) t.Fatalf("ProcessBlock: %v", err)
} }
if isOrphan { if isOrphan {
t.Fatalf("ProcessBlock: template1 got unexpectedly orphan") t.Fatalf("ProcessBlock: template1 got unexpectedly orphan")
} }
cbScript, err := StandardCoinbaseScript(dag.VirtualBlueScore(), 0) // We create another 4 blocks to in order to create more funds for tests.
if err != nil { cbTxs := []*wire.MsgTx{template1.Block.Transactions[util.CoinbaseTransactionIndex]}
t.Fatalf("standardCoinbaseScript: %v", err) for i := 0; i < 4; i++ {
template, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
if err != nil {
t.Fatalf("NewBlockTemplate: %v", err)
}
isOrphan, err = dag.ProcessBlock(util.NewBlock(template.Block), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if isOrphan {
t.Fatalf("ProcessBlock: template got unexpectedly orphan")
}
cbTxs = append(cbTxs, template.Block.Transactions[util.CoinbaseTransactionIndex])
} }
// We want to check that the miner filters coinbase transaction // We want to check that the miner filters coinbase transaction
cbTx, err := CreateCoinbaseTx(&params, cbScript, dag.VirtualBlueScore(), nil) cbTx, err := dag.NextBlockCoinbaseTransaction(nil, nil)
if err != nil { if err != nil {
t.Fatalf("createCoinbaseTx: %v", err) t.Fatalf("createCoinbaseTx: %v", err)
} }
template1CbTx := template1.Block.Transactions[0]
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil) signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil { if err != nil {
t.Fatalf("Error creating signature script: %s", err) t.Fatalf("Error creating signature script: %s", err)
@ -155,7 +148,7 @@ func TestNewBlockTemplate(t *testing.T) {
// tx is a regular transaction, and should not be filtered by the miner // tx is a regular transaction, and should not be filtered by the miner
txIn := &wire.TxIn{ txIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(), TxID: *cbTxs[0].TxID(),
Index: 0, Index: 0,
}, },
Sequence: wire.MaxTxInSequenceNum, Sequence: wire.MaxTxInSequenceNum,
@ -170,8 +163,8 @@ func TestNewBlockTemplate(t *testing.T) {
// We want to check that the miner filters non finalized transactions // We want to check that the miner filters non finalized transactions
txIn = &wire.TxIn{ txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(), TxID: *cbTxs[1].TxID(),
Index: 1, Index: 0,
}, },
Sequence: 0, Sequence: 0,
SignatureScript: signatureScript, SignatureScript: signatureScript,
@ -189,8 +182,8 @@ func TestNewBlockTemplate(t *testing.T) {
// We want to check that the miner filters transactions with non-existing subnetwork id. (It should first push it to the priority queue, and then ignore it) // We want to check that the miner filters transactions with non-existing subnetwork id. (It should first push it to the priority queue, and then ignore it)
txIn = &wire.TxIn{ txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(), TxID: *cbTxs[2].TxID(),
Index: 2, Index: 0,
}, },
Sequence: 0, Sequence: 0,
SignatureScript: signatureScript, SignatureScript: signatureScript,
@ -205,8 +198,8 @@ func TestNewBlockTemplate(t *testing.T) {
// We want to check that the miner doesn't filters transactions that do not exceed the subnetwork gas limit // We want to check that the miner doesn't filters transactions that do not exceed the subnetwork gas limit
txIn = &wire.TxIn{ txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(), TxID: *cbTxs[3].TxID(),
Index: 3, Index: 0,
}, },
Sequence: 0, Sequence: 0,
SignatureScript: signatureScript, SignatureScript: signatureScript,
@ -220,8 +213,7 @@ func TestNewBlockTemplate(t *testing.T) {
// We want to check that the miner filters transactions that exceed the subnetwork gas limit. (It should first push it to the priority queue, and then ignore it) // We want to check that the miner filters transactions that exceed the subnetwork gas limit. (It should first push it to the priority queue, and then ignore it)
txIn = &wire.TxIn{ txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{ PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(), TxID: *cbTxs[4].TxID(),
Index: 4,
}, },
Sequence: 0, Sequence: 0,
SignatureScript: signatureScript, SignatureScript: signatureScript,
@ -255,25 +247,6 @@ func TestNewBlockTemplate(t *testing.T) {
}, },
} }
standardCoinbaseScriptErrString := "standardCoinbaseScript err"
var standardCoinbaseScriptPatch *monkey.PatchGuard
standardCoinbaseScriptPatch = monkey.Patch(StandardCoinbaseScript, func(nextBlockHeight uint64, extraNonce uint64) ([]byte, error) {
return nil, errors.New(standardCoinbaseScriptErrString)
})
defer standardCoinbaseScriptPatch.Unpatch()
// We want to check that NewBlockTemplate will fail if standardCoinbaseScript returns an error
_, err = blockTemplateGenerator.NewBlockTemplate(nil)
standardCoinbaseScriptPatch.Unpatch()
if err == nil || err.Error() != standardCoinbaseScriptErrString {
t.Errorf("expected an error \"%v\" but got \"%v\"", standardCoinbaseScriptErrString, err)
}
if err == nil {
t.Errorf("expected an error but got <nil>")
}
// Here we check that the miner's priorty queue has the expected transactions after filtering. // Here we check that the miner's priorty queue has the expected transactions after filtering.
popReturnedUnexpectedValue := false popReturnedUnexpectedValue := false
expectedPops := map[daghash.TxID]bool{ expectedPops := map[daghash.TxID]bool{
@ -306,7 +279,7 @@ func TestNewBlockTemplate(t *testing.T) {
}) })
defer gasLimitPatch.Unpatch() defer gasLimitPatch.Unpatch()
template2, err := blockTemplateGenerator.NewBlockTemplate(nil) template3, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
popPatch.Unpatch() popPatch.Unpatch()
gasLimitPatch.Unpatch() gasLimitPatch.Unpatch()
@ -329,17 +302,17 @@ func TestNewBlockTemplate(t *testing.T) {
*subnetworkTx1.TxID(): false, *subnetworkTx1.TxID(): false,
} }
for _, tx := range template2.Block.Transactions[2:] { for _, tx := range template3.Block.Transactions[util.CoinbaseTransactionIndex+1:] {
id := *tx.TxID() id := *tx.TxID()
if _, ok := expectedTxs[id]; !ok { if _, ok := expectedTxs[id]; !ok {
t.Errorf("Unexpected tx %v in template2's candidate block", id) t.Errorf("Unexpected tx %v in template3's candidate block", id)
} }
expectedTxs[id] = true expectedTxs[id] = true
} }
for id, exists := range expectedTxs { for id, exists := range expectedTxs {
if !exists { if !exists {
t.Errorf("tx %v was expected to be in template2's candidate block, but wasn't", id) t.Errorf("tx %v was expected to be in template3's candidate block, but wasn't", id)
} }
} }
} }

View File

@ -37,8 +37,8 @@ func (txs *fakeTxSource) HaveTransaction(txID *daghash.TxID) bool {
return false return false
} }
// PrepareBlockForTest generates a block with the proper merkle roots, coinbase/fee transactions etc. This function is used for test purposes only // PrepareBlockForTest generates a block with the proper merkle roots, coinbase transaction etc. This function is used for test purposes only
func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, parentHashes []*daghash.Hash, transactions []*wire.MsgTx, forceTransactions bool, coinbaseOutputs uint64) (*wire.MsgBlock, error) { func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, parentHashes []*daghash.Hash, transactions []*wire.MsgTx, forceTransactions bool) (*wire.MsgBlock, error) {
newVirtual, err := blockdag.GetVirtualFromParentsForTest(dag, parentHashes) newVirtual, err := blockdag.GetVirtualFromParentsForTest(dag, parentHashes)
if err != nil { if err != nil {
return nil, err return nil, err
@ -62,7 +62,12 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
blockTemplateGenerator := NewBlkTmplGenerator(&policy, blockTemplateGenerator := NewBlkTmplGenerator(&policy,
params, txSource, dag, blockdag.NewMedianTime(), txscript.NewSigCache(100000)) params, txSource, dag, blockdag.NewMedianTime(), txscript.NewSigCache(100000))
template, err := blockTemplateGenerator.NewBlockTemplate(nil) OpTrueAddr, err := OpTrueAddress(params.Prefix)
if err != nil {
return nil, err
}
template, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -89,27 +94,12 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
txsToAdd = append(txsToAdd, tx) txsToAdd = append(txsToAdd, tx)
} }
} }
if coinbaseOutputs != 1 {
cb := template.Block.Transactions[0]
originalValue := cb.TxOut[0].Value
pkScript := cb.TxOut[0].PkScript
cb.TxOut = make([]*wire.TxOut, coinbaseOutputs)
if coinbaseOutputs != 0 {
newOutValue := originalValue / coinbaseOutputs
for i := uint64(0); i < coinbaseOutputs; i++ {
cb.TxOut[i] = &wire.TxOut{
Value: newOutValue,
PkScript: pkScript,
}
}
}
}
if forceTransactions && len(txsToAdd) > 0 { if forceTransactions && len(txsToAdd) > 0 {
for _, tx := range txsToAdd { for _, tx := range txsToAdd {
template.Block.Transactions = append(template.Block.Transactions, tx) template.Block.Transactions = append(template.Block.Transactions, tx)
} }
} }
updateHeaderFields := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0) updateHeaderFields := forceTransactions && len(txsToAdd) > 0
if updateHeaderFields { 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 {
@ -131,4 +121,8 @@ func GenerateDeterministicExtraNonceForTest() uint64 {
return extraNonceForTest return extraNonceForTest
} }
func OpTrueAddress(prefix util.Bech32Prefix) (util.Address, error) {
return util.NewAddressScriptHash(blockdag.OpTrueScript, prefix)
}
var extraNonceForTest = uint64(0) var extraNonceForTest = uint64(0)

View File

@ -1665,7 +1665,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool)
context := "Failed to create pay-to-addr script" context := "Failed to create pay-to-addr script"
return internalRPCError(err.Error(), context) return internalRPCError(err.Error(), context)
} }
template.Block.Transactions[0].TxOut[0].PkScript = pkScript template.Block.Transactions[util.CoinbaseTransactionIndex].TxOut[0].PkScript = pkScript
template.ValidPayAddress = true template.ValidPayAddress = true
// Update the merkle root. // Update the merkle root.
@ -1796,7 +1796,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
if useCoinbaseValue { if useCoinbaseValue {
reply.CoinbaseAux = gbtCoinbaseAux reply.CoinbaseAux = gbtCoinbaseAux
reply.CoinbaseValue = &msgBlock.Transactions[0].TxOut[0].Value reply.CoinbaseValue = &msgBlock.Transactions[util.CoinbaseTransactionIndex].TxOut[0].Value
} else { } else {
// Ensure the template has a valid payment address associated // Ensure the template has a valid payment address associated
// with it when a full coinbase is requested. // with it when a full coinbase is requested.
@ -1811,7 +1811,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
} }
// Serialize the transaction for conversion to hex. // Serialize the transaction for conversion to hex.
tx := msgBlock.Transactions[0] tx := msgBlock.Transactions[util.CoinbaseTransactionIndex]
txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(txBuf); err != nil { if err := tx.Serialize(txBuf); err != nil {
context := "Failed to serialize transaction" context := "Failed to serialize transaction"
@ -2082,14 +2082,8 @@ func chainErrToGBTErrString(err error) string {
return "bad-txns-nocoinbase" return "bad-txns-nocoinbase"
case blockdag.ErrMultipleCoinbases: case blockdag.ErrMultipleCoinbases:
return "bad-txns-multicoinbase" return "bad-txns-multicoinbase"
case blockdag.ErrBadCoinbaseScriptLen: case blockdag.ErrBadCoinbasePayloadLen:
return "bad-cb-length" return "bad-cb-length"
case blockdag.ErrBadCoinbaseValue:
return "bad-cb-value"
case blockdag.ErrMissingCoinbaseBlueScore:
return "bad-cb-height"
case blockdag.ErrBadCoinbaseBlueScore:
return "bad-cb-height"
case blockdag.ErrScriptMalformed: case blockdag.ErrScriptMalformed:
return "bad-script-malformed" return "bad-script-malformed"
case blockdag.ErrScriptValidation: case blockdag.ErrScriptValidation:
@ -2746,7 +2740,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
bestBlockHash = s.cfg.DAG.SelectedTipHash().String() bestBlockHash = s.cfg.DAG.SelectedTipHash().String()
value = entry.Amount() value = entry.Amount()
pkScript = entry.PkScript() pkScript = entry.PkScript()
isCoinbase = entry.IsBlockReward() isCoinbase = entry.IsCoinbase()
} }
// Disassemble script into single line printable format. // Disassemble script into single line printable format.
@ -2940,30 +2934,14 @@ func txConfirmations(s *Server, txID *daghash.TxID) (uint64, error) {
// createVinListPrevOut returns a slice of JSON objects for the inputs of the // createVinListPrevOut returns a slice of JSON objects for the inputs of the
// passed transaction. // passed transaction.
func createVinListPrevOut(s *Server, mtx *wire.MsgTx, chainParams *dagconfig.Params, vinExtra bool, filterAddrMap map[string]struct{}) ([]btcjson.VinPrevOut, error) { func createVinListPrevOut(s *Server, mtx *wire.MsgTx, chainParams *dagconfig.Params, vinExtra bool, filterAddrMap map[string]struct{}) ([]btcjson.VinPrevOut, error) {
// Coinbase transactions only have a single txin by definition.
if mtx.IsCoinBase() {
// Only include the transaction if the filter map is empty
// because a coinbase input has no addresses and so would never
// match a non-empty filter.
if len(filterAddrMap) != 0 {
return nil, nil
}
txIn := mtx.TxIn[0]
vinList := make([]btcjson.VinPrevOut, 1)
vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript)
vinList[0].Sequence = txIn.Sequence
return vinList, nil
}
// Use a dynamically sized list to accommodate the address filter. // Use a dynamically sized list to accommodate the address filter.
vinList := make([]btcjson.VinPrevOut, 0, len(mtx.TxIn)) vinList := make([]btcjson.VinPrevOut, 0, len(mtx.TxIn))
// Lookup all of the referenced transaction outputs needed to populate the // Lookup all of the referenced transaction outputs needed to populate the
// previous output information if requested. Fee transactions do not contain // previous output information if requested. Coinbase transactions do not contain
// valid inputs: block hash instead of transaction ID. // valid inputs: block hash instead of transaction ID.
var originOutputs map[wire.Outpoint]wire.TxOut var originOutputs map[wire.Outpoint]wire.TxOut
if !mtx.IsFeeTransaction() && (vinExtra || len(filterAddrMap) > 0) { if !mtx.IsCoinBase() && (vinExtra || len(filterAddrMap) > 0) {
var err error var err error
originOutputs, err = fetchInputTxos(s, mtx) originOutputs, err = fetchInputTxos(s, mtx)
if err != nil { if err != nil {

View File

@ -26,10 +26,6 @@ const (
// CoinbaseTransactionIndex is the index of the coinbase transaction in every block // CoinbaseTransactionIndex is the index of the coinbase transaction in every block
CoinbaseTransactionIndex = 0 CoinbaseTransactionIndex = 0
// FeeTransactionIndex is the index of the fee transaction in every block (except genesis,
// which doesn't have a fee transaction)
FeeTransactionIndex = 1
) )
// Error satisfies the error interface and prints human-readable errors. // Error satisfies the error interface and prints human-readable errors.
@ -214,16 +210,6 @@ func (b *Block) CoinbaseTransaction() *Tx {
return b.Transactions()[CoinbaseTransactionIndex] return b.Transactions()[CoinbaseTransactionIndex]
} }
// FeeTransaction returns this block's fee transaction
// If this block is a genesis block, it has no fee transaction, and therefore
// nil is returned.
func (b *Block) FeeTransaction() *Tx {
if b.IsGenesis() {
return nil
}
return b.Transactions()[FeeTransactionIndex]
}
// Timestamp returns this block's timestamp // Timestamp returns this block's timestamp
func (b *Block) Timestamp() time.Time { func (b *Block) Timestamp() time.Time {
return b.msgBlock.Header.Timestamp return b.msgBlock.Header.Timestamp

View File

@ -548,7 +548,7 @@ func TestFilterInsertP2PubKeyOnly(t *testing.T) {
0x4C, 0x86, 0x04, 0x1B, // Bits 0x4C, 0x86, 0x04, 0x1B, // Bits
0x55, 0x4B, 0x85, 0x29, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce 0x55, 0x4B, 0x85, 0x29, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce
0x07, // NumTxns 0x07, // NumTxns
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //Txs[0] 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Txs[0]
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x04, 0x4C, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x04, 0x4C,
@ -560,9 +560,13 @@ func TestFilterInsertP2PubKeyOnly(t *testing.T) {
0x1E, 0xC0, 0xF0, 0xDD, 0x5B, 0x2E, 0x86, 0xE7, 0x16, 0x8C, 0xEF, 0x1E, 0xC0, 0xF0, 0xDD, 0x5B, 0x2E, 0x86, 0xE7, 0x16, 0x8C, 0xEF,
0xE0, 0xD8, 0x11, 0x13, 0xC3, 0x80, 0x74, 0x20, 0xCE, 0x13, 0xAD, 0xE0, 0xD8, 0x11, 0x13, 0xC3, 0x80, 0x74, 0x20, 0xCE, 0x13, 0xAD,
0x13, 0x57, 0x23, 0x1A, 0x22, 0x52, 0x24, 0x7D, 0x97, 0xA4, 0x6A, 0x13, 0x57, 0x23, 0x1A, 0x22, 0x52, 0x24, 0x7D, 0x97, 0xA4, 0x6A,
0x91, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0xBC, 0xAD, 0x20, 0xA6, 0xA2, 0x98, // Txs[1] 0x01, 0x00, 0x00, 0x00, 0x01, 0xBC, 0xAD, 0x20, 0xA6, 0xA2, 0x98, // Txs[1]
0x27, 0xD1, 0x42, 0x4F, 0x08, 0x98, 0x92, 0x55, 0x12, 0x0B, 0xF7, 0x27, 0xD1, 0x42, 0x4F, 0x08, 0x98, 0x92, 0x55, 0x12, 0x0B, 0xF7,
@ -830,15 +834,15 @@ func TestFilterInsertP2PubKeyOnly(t *testing.T) {
_, _ = bloom.NewMerkleBlock(block, f) _, _ = bloom.NewMerkleBlock(block, f)
// We should match the generation pubkey // We should match the generation pubkey
inputStr = "5f8261e729a931a6258f291d88ad62c1657c444f41acb829843b4cbdd7018944" //0st tx ID inputStr = "c2254e4d610867ee48decf60d8bd8e1d361eeeab5d1052ce3e98184a5b4d0923" // 0st tx ID
txID, err := daghash.NewTxIDFromStr(inputStr) txID, err := daghash.NewTxIDFromStr(inputStr)
if err != nil { if err != nil {
t.Errorf("TestMerkleBlockP2PubKeyOnly NewHashFromStr failed: %v", err) t.Errorf("TestFilterInsertP2PubKeyOnly NewHashFromStr failed: %v", err)
return return
} }
outpoint := wire.NewOutpoint(txID, 0) outpoint := wire.NewOutpoint(txID, 0)
if !f.MatchesOutpoint(outpoint) { if !f.MatchesOutpoint(outpoint) {
t.Errorf("TestMerkleBlockP2PubKeyOnly didn't match the generation "+ t.Errorf("TestFilterInsertP2PubKeyOnly didn't match the generation "+
"outpoint %s", inputStr) "outpoint %s", inputStr)
return return
} }
@ -847,12 +851,12 @@ func TestFilterInsertP2PubKeyOnly(t *testing.T) {
inputStr = "f9a116ecc107b6b1b0bdcd0d727bfaa3355f27f8fed08347bf0004244949d9eb" inputStr = "f9a116ecc107b6b1b0bdcd0d727bfaa3355f27f8fed08347bf0004244949d9eb"
txID, err = daghash.NewTxIDFromStr(inputStr) txID, err = daghash.NewTxIDFromStr(inputStr)
if err != nil { if err != nil {
t.Errorf("TestMerkleBlockP2PubKeyOnly NewHashFromStr failed: %v", err) t.Errorf("TestFilterInsertP2PubKeyOnly NewHashFromStr failed: %v", err)
return return
} }
outpoint = wire.NewOutpoint(txID, 0) outpoint = wire.NewOutpoint(txID, 0)
if f.MatchesOutpoint(outpoint) { if f.MatchesOutpoint(outpoint) {
t.Errorf("TestMerkleBlockP2PubKeyOnly matched outpoint %s", inputStr) t.Errorf("TestFilterInsertP2PubKeyOnly matched outpoint %s", inputStr)
return return
} }
} }

View File

@ -35,22 +35,27 @@ func TestMerkleBlock3(t *testing.T) {
0x67, 0x29, 0x1B, 0x4D, 0x00, 0x00, 0x00, 0x00, //Time 0x67, 0x29, 0x1B, 0x4D, 0x00, 0x00, 0x00, 0x00, //Time
0x4C, 0x86, 0x04, 0x1B, // Bits 0x4C, 0x86, 0x04, 0x1B, // Bits
0x8F, 0xA4, 0x5D, 0x63, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce 0x8F, 0xA4, 0x5D, 0x63, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce
0x01, // NumTxs 0x01, // NumTxs
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Tx 0x01, 0x00, 0x00, 0x00, 0x01, 0x9b, 0x22, 0x59,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x66, 0xf0, 0xbe, 0x50, 0x7c, 0x1c, 0x8a, // Tx
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x06, 0x27, 0xe6, 0x33, 0x38, 0x7e, 0xd1,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x04, 0x4C, 0xd5, 0x8c, 0x42, 0x59, 0x1a, 0x31, 0xac, 0x9a,
0x86, 0x04, 0x1B, 0x02, 0x0A, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f, 0xff, 0xff, 0xff,
0x00, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xEC, 0xD3, 0x22, 0x9B, 0x05, 0x71, 0xC3, 0xBE, 0x87, 0x6F, 0xEA, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01,
0xAC, 0x04, 0x42, 0xA9, 0xF1, 0x3C, 0x5A, 0x57, 0x27, 0x42, 0x92, 0x00, 0x00, 0x00, 0x17, 0xa9, 0x14, 0xda, 0x17,
0x7A, 0xF1, 0xDC, 0x62, 0x33, 0x53, 0xEC, 0xF8, 0xC2, 0x02, 0x22, 0x45, 0xe9, 0xb5, 0x49, 0xbd, 0x0b, 0xfa, 0x1a,
0x5F, 0x64, 0x86, 0x81, 0x37, 0xA1, 0x8C, 0xDD, 0x85, 0xCB, 0xBB, 0x56, 0x99, 0x71, 0xc7, 0x7e, 0xba, 0x30, 0xcd,
0x4C, 0x74, 0xFB, 0xCC, 0xFD, 0x4F, 0x49, 0x63, 0x9C, 0xF1, 0xBD, 0x5a, 0x4b, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC9, 0x4A, 0x56, 0x72, 0xBB, 0x15, 0xAD, 0x5D, 0x4C, 0xAC, 0x00, 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, 0x14,
0x06, 0xe0, 0x58, 0x81, 0xe2, 0x99, 0x36, 0x77,
0x66, 0xd3, 0x13, 0xe2, 0x6c, 0x05, 0x56, 0x4e,
0xc9, 0x1b, 0xf7, 0x21, 0xd3, 0x17, 0x26, 0xbd,
0x6e, 0x46, 0xe6, 0x06, 0x89, 0x53, 0x9a, 0x01,
0x00,
} }
blk, err := util.NewBlockFromBytes(blockBytes) blk, err := util.NewBlockFromBytes(blockBytes)
@ -73,35 +78,18 @@ func TestMerkleBlock3(t *testing.T) {
mBlock, _ := bloom.NewMerkleBlock(blk, f) mBlock, _ := bloom.NewMerkleBlock(blk, f)
want := []byte{ want := []byte{
0x01, 0x00, 0x00, 0x00, 0x01, 0x79, 0xcd, 0xa8, 0x01, 0x00, 0x00, 0x00, 0x01, 0x79, 0xcd, 0xa8, 0x56, 0xb1, 0x43, 0xd9, 0xdb, 0x2c, 0x1c, 0xaf,
0x56, 0xb1, 0x43, 0xd9, 0xdb, 0x2c, 0x1c, 0xaf, 0xf0, 0x1d, 0x1a, 0xec, 0xc8, 0x63, 0x0d, 0x30, 0x62, 0x5d, 0x10, 0xe8, 0xb4, 0xb8, 0xb0, 0x00,
0xf0, 0x1d, 0x1a, 0xec, 0xc8, 0x63, 0x0d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x0c, 0xc0, 0x69, 0xd6, 0xa3, 0xe3, 0x3e, 0x3f, 0xf8, 0x4a,
0x62, 0x5d, 0x10, 0xe8, 0xb4, 0xb8, 0xb0, 0x00, 0x5c, 0x41, 0xd9, 0xd3, 0xfe, 0xbe, 0x7c, 0x77, 0x0f, 0xdc, 0xc9, 0x6b, 0x2c, 0x3f, 0xf6, 0x0a,
0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x0c, 0xc0, 0xbe, 0x18, 0x4f, 0x19, 0x63, 0x3c, 0xe3, 0x70, 0xd9, 0x5f, 0x09, 0x3b, 0xc7, 0xe3, 0x67, 0x11,
0x69, 0xd6, 0xa3, 0xe3, 0x3e, 0x3f, 0xf8, 0x4a, 0x7f, 0x16, 0xc5, 0x96, 0x2e, 0x8b, 0xd9, 0x63, 0x65, 0x9c, 0x79, 0x7b, 0x3c, 0x30, 0xc1, 0xf8,
0x5c, 0x41, 0xd9, 0xd3, 0xfe, 0xbe, 0x7c, 0x77, 0xfd, 0xd0, 0xd9, 0x72, 0x87, 0x10, 0x3b, 0xc7, 0xe3, 0x67, 0x11, 0x7b, 0x3c, 0x30, 0xc1, 0xf8,
0x0f, 0xdc, 0xc9, 0x6b, 0x2c, 0x3f, 0xf6, 0x0a, 0xfd, 0xd0, 0xd9, 0x72, 0x87, 0x7f, 0x16, 0xc5, 0x96, 0x2e, 0x8b, 0xd9, 0x63, 0x65, 0x9c, 0x79,
0xbe, 0x18, 0x4f, 0x19, 0x63, 0x3c, 0xe3, 0x70, 0x3c, 0xe3, 0x70, 0xd9, 0x5f, 0x67, 0x29, 0x1b, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x86, 0x04,
0xd9, 0x5f, 0x09, 0x3b, 0xc7, 0xe3, 0x67, 0x11, 0x1b, 0x8f, 0xa4, 0x5d, 0x63, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x27, 0x8a,
0x7f, 0x16, 0xc5, 0x96, 0x2e, 0x8b, 0xd9, 0x63, 0xff, 0xcc, 0xbf, 0xd9, 0x15, 0x12, 0xf8, 0x91, 0xbd, 0xd3, 0x46, 0x91, 0x3d, 0xe6, 0x13, 0xdc,
0x65, 0x9c, 0x79, 0x7b, 0x3c, 0x30, 0xc1, 0xf8, 0x9c, 0xb9, 0x7e, 0xa3, 0xc5, 0x8f, 0xab, 0x6b, 0xd8, 0xb7, 0x2b, 0x97, 0x39, 0x03, 0x01, 0x00,
0xfd, 0xd0, 0xd9, 0x72, 0x87, 0x10, 0x3b, 0xc7,
0xe3, 0x67, 0x11, 0x7b, 0x3c, 0x30, 0xc1, 0xf8,
0xfd, 0xd0, 0xd9, 0x72, 0x87, 0x7f, 0x16, 0xc5,
0x96, 0x2e, 0x8b, 0xd9, 0x63, 0x65, 0x9c, 0x79,
0x3c, 0xe3, 0x70, 0xd9, 0x5f, 0x67, 0x29, 0x1b,
0x4d, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x86, 0x04,
0x1b, 0x8f, 0xa4, 0x5d, 0x63, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xe2, 0x03,
0xc3, 0xca, 0x45, 0x44, 0x5e, 0xcd, 0xbc, 0x90,
0xdf, 0x49, 0xa2, 0x91, 0x41, 0xdf, 0x31, 0x92,
0xee, 0xdb, 0x9d, 0x42, 0x33, 0xf5, 0x22, 0xff,
0x26, 0xca, 0x44, 0xde, 0x3e, 0x9e, 0x01, 0x00,
}
t.Log(spew.Sdump(want))
if err != nil {
t.Errorf("TestMerkleBlock3 DecodeString failed: %v", err)
return
} }
got := bytes.NewBuffer(nil) got := bytes.NewBuffer(nil)

View File

@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"sort" "sort"
"strings"
) )
// IDLength of array used to store the subnetwork ID. See SubnetworkID. // IDLength of array used to store the subnetwork ID. See SubnetworkID.
@ -31,8 +30,11 @@ var (
// SubnetworkIDNative is the default subnetwork ID which is used for transactions without related payload data // SubnetworkIDNative is the default subnetwork ID which is used for transactions without related payload data
SubnetworkIDNative = &SubnetworkID{} SubnetworkIDNative = &SubnetworkID{}
// SubnetworkIDCoinbase is the subnetwork ID which is used for the coinbase transaction
SubnetworkIDCoinbase = &SubnetworkID{1}
// SubnetworkIDRegistry is the subnetwork ID which is used for adding new sub networks to the registry // SubnetworkIDRegistry is the subnetwork ID which is used for adding new sub networks to the registry
SubnetworkIDRegistry = &SubnetworkID{1} SubnetworkIDRegistry = &SubnetworkID{2}
) )
// String returns the SubnetworkID as the hexadecimal string of the byte-reversed // String returns the SubnetworkID as the hexadecimal string of the byte-reversed
@ -188,16 +190,18 @@ func (id *SubnetworkID) Cmp(target *SubnetworkID) int {
return ToBig(id).Cmp(ToBig(target)) return ToBig(id).Cmp(ToBig(target))
} }
// IsBuiltIn returns true if the subnetwork is a built in subnetwork, which
// means all nodes, including partial nodes, must validate it, and its transactions
// always use 0 gas.
func (id *SubnetworkID) IsBuiltIn() bool {
return id.IsEqual(SubnetworkIDCoinbase) || id.IsEqual(SubnetworkIDRegistry)
}
// Less returns true iff id a is less than id b // Less returns true iff id a is less than id b
func Less(a *SubnetworkID, b *SubnetworkID) bool { func Less(a *SubnetworkID, b *SubnetworkID) bool {
return a.Cmp(b) < 0 return a.Cmp(b) < 0
} }
// JoinIDsStrings joins all the stringified IDs separated by a separator
func JoinIDsStrings(ids []SubnetworkID, separator string) string {
return strings.Join(Strings(ids), separator)
}
// Sort sorts a slice of ids // Sort sorts a slice of ids
func Sort(ids []SubnetworkID) { func Sort(ids []SubnetworkID) {
sort.Slice(ids, func(i, j int) bool { sort.Slice(ids, func(i, j int) bool {

View File

@ -19,7 +19,7 @@ import (
// RegisterSubnetworkForTest is used to register network on DAG with specified gas limit // RegisterSubnetworkForTest is used to register network on DAG with specified gas limit
func RegisterSubnetworkForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, gasLimit uint64) (*subnetworkid.SubnetworkID, error) { func RegisterSubnetworkForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, gasLimit uint64) (*subnetworkid.SubnetworkID, error) {
buildNextBlock := func(parentHashes []*daghash.Hash, txs []*wire.MsgTx) (*util.Block, error) { buildNextBlock := func(parentHashes []*daghash.Hash, txs []*wire.MsgTx) (*util.Block, error) {
msgBlock, err := mining.PrepareBlockForTest(dag, params, parentHashes, txs, false, 1) msgBlock, err := mining.PrepareBlockForTest(dag, params, parentHashes, txs, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -75,6 +75,15 @@ func (t *Tx) SetIndex(index int) {
t.txIndex = index t.txIndex = index
} }
// IsCoinBase determines whether or not a transaction is a coinbase. A coinbase
// is a special transaction created by miners that has no inputs. This is
// represented in the block dag by a transaction with a single input that has
// a previous output transaction index set to the maximum value along with a
// zero hash.
func (t *Tx) IsCoinBase() bool {
return t.MsgTx().IsCoinBase()
}
// NewTx returns a new instance of a bitcoin transaction given an underlying // NewTx returns a new instance of a bitcoin transaction given an underlying
// wire.MsgTx. See Tx. // wire.MsgTx. See Tx.
func NewTx(msgTx *wire.MsgTx) *Tx { func NewTx(msgTx *wire.MsgTx) *Tx {

View File

@ -288,42 +288,14 @@ func (msg *MsgTx) AddTxOut(to *TxOut) {
msg.TxOut = append(msg.TxOut, to) msg.TxOut = append(msg.TxOut, to)
} }
// IsCoinBase determines whether or not a transaction is a coinbase. A coinbase // IsCoinBase determines whether or not a transaction is a coinbase transaction. A coinbase
// is a special transaction created by miners that has no inputs. This is // transaction is a special transaction created by miners that distributes fees and block subsidy
// represented in the block dag by a transaction with a single input that has // to the previous blocks' miners, and to specify the pkScript that will be used to pay the current
// a previous output transaction index set to the maximum value along with a // miner in future blocks. Each input of the coinbase transaction should set index to maximum
// zero TxID.
func (msg *MsgTx) IsCoinBase() bool {
// A coin base must only have one transaction input.
if len(msg.TxIn) != 1 {
return false
}
// The previous output of a coinbase must have a max value index and
// a zero TxID.
prevOut := &msg.TxIn[0].PreviousOutpoint
return prevOut.Index == math.MaxUint32 && prevOut.TxID == daghash.ZeroTxID
}
// IsFeeTransaction determines whether or not a transaction is a fee transaction. A fee
// transaction is a special transaction created by miners that distributes fees to the
// previous blocks' miners. Each input of the fee transaction should set index to maximum
// value and reference the relevant block id, instead of previous transaction id. // value and reference the relevant block id, instead of previous transaction id.
func (msg *MsgTx) IsFeeTransaction() bool { func (msg *MsgTx) IsCoinBase() bool {
for _, txIn := range msg.TxIn { // A coinbase transaction must have subnetwork id SubnetworkIDCoinbase
// The previous output of a fee transaction have a max value index and return msg.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDCoinbase)
// a non-zero TxID (to differentiate from coinbase).
prevOut := txIn.PreviousOutpoint
if prevOut.Index != math.MaxUint32 || prevOut.TxID == daghash.ZeroTxID {
return false
}
}
return true
}
// IsBlockReward determines whether or not a transaction is a block reward (a fee transaction or a coinbase)
func (msg *MsgTx) IsBlockReward() bool {
return msg.IsFeeTransaction() || msg.IsCoinBase()
} }
// TxHash generates the Hash for the transaction. // TxHash generates the Hash for the transaction.
@ -680,8 +652,8 @@ func (msg *MsgTx) encode(w io.Writer, pver uint32, encodingFlags txEncoding) err
} }
if !msg.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) { if !msg.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) {
if msg.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry) && msg.Gas != 0 { if msg.SubnetworkID.IsBuiltIn() && msg.Gas != 0 {
str := "Transactions from registry subnetwork should have 0 gas" str := "Transactions from built-in should have 0 gas"
return messageError("MsgTx.BtcEncode", str) return messageError("MsgTx.BtcEncode", str)
} }

View File

@ -129,18 +129,18 @@ func TestTx(t *testing.T) {
// TestTxHash tests the ability to generate the hash of a transaction accurately. // TestTxHash tests the ability to generate the hash of a transaction accurately.
func TestTxHashAndID(t *testing.T) { func TestTxHashAndID(t *testing.T) {
txID1Str := "5b92e6ed52bc78745905e0d104069e46407f62ea8d7d2bce78cd13f80ce220dc" txID1Str := "edca872f27279674c7a52192b32fd68b8b8be714bfea52d98b2c3c86c30e85c6"
wantTxID1, err := daghash.NewTxIDFromStr(txID1Str) wantTxID1, err := daghash.NewTxIDFromStr(txID1Str)
if err != nil { if err != nil {
t.Errorf("NewTxIDFromStr: %v", err) t.Errorf("NewTxIDFromStr: %v", err)
return return
} }
// First transaction from block 113875. // A coinbase transaction
txIn := &TxIn{ txIn := &TxIn{
PreviousOutpoint: Outpoint{ PreviousOutpoint: Outpoint{
TxID: daghash.TxID{}, TxID: daghash.TxID{},
Index: 0xffffffff, Index: math.MaxUint32,
}, },
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
Sequence: math.MaxUint64, Sequence: math.MaxUint64,
@ -161,7 +161,7 @@ func TestTxHashAndID(t *testing.T) {
0xac, // OP_CHECKSIG 0xac, // OP_CHECKSIG
}, },
} }
tx1 := NewNativeMsgTx(1, []*TxIn{txIn}, []*TxOut{txOut}) tx1 := NewSubnetworkMsgTx(1, []*TxIn{txIn}, []*TxOut{txOut}, subnetworkid.SubnetworkIDCoinbase, 0, nil)
// Ensure the hash produced is expected. // Ensure the hash produced is expected.
tx1Hash := tx1.TxHash() tx1Hash := tx1.TxHash()
@ -245,11 +245,10 @@ func TestTxHashAndID(t *testing.T) {
t.Errorf("tx2ID and tx2Hash shouldn't be the same for non-coinbase transaction with signature and/or payload") t.Errorf("tx2ID and tx2Hash shouldn't be the same for non-coinbase transaction with signature and/or payload")
} }
tx2.Payload = []byte{}
tx2.TxIn[0].SignatureScript = []byte{} tx2.TxIn[0].SignatureScript = []byte{}
newTx2Hash := tx2.TxHash() newTx2Hash := tx2.TxHash()
if !tx2ID.IsEqual((*daghash.TxID)(newTx2Hash)) { if !tx2ID.IsEqual((*daghash.TxID)(newTx2Hash)) {
t.Errorf("tx2ID and newTx2Hash should be the same for transaction without empty signature and payload") t.Errorf("tx2ID and newTx2Hash should be the same for transaction with an empty signature")
} }
} }
@ -403,7 +402,7 @@ func TestTxSerialize(t *testing.T) {
0x00, // Varint for number of input transactions 0x00, // Varint for number of input transactions
0x00, // Varint for number of output transactions 0x00, // Varint for number of output transactions
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // Sub Network ID 0x00, 0x00, 0x00, 0x00, // Sub Network ID
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Gas 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Gas
@ -590,7 +589,7 @@ func TestTxSerializeErrors(t *testing.T) {
w := bytes.NewBuffer(make([]byte, 0, registryTx.SerializeSize())) w := bytes.NewBuffer(make([]byte, 0, registryTx.SerializeSize()))
err := registryTx.Serialize(w) err := registryTx.Serialize(w)
str := "Transactions from registry subnetwork should have 0 gas" str := "Transactions from built-in should have 0 gas"
expectedErr := messageError("MsgTx.BtcEncode", str) expectedErr := messageError("MsgTx.BtcEncode", str)
if err == nil || err.Error() != expectedErr.Error() { if err == nil || err.Error() != expectedErr.Error() {
t.Errorf("TestTxSerializeErrors: expected error %v but got %v", expectedErr, err) t.Errorf("TestTxSerializeErrors: expected error %v but got %v", expectedErr, err)