[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()
dag.TestSetBlockRewardMaturity(1)
dag.TestSetCoinbaseMaturity(1)
// Test rejecting the block if its parents are missing
orphanBlockFile := "blk_3B.dat"

View File

@ -6,6 +6,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/daglabs/btcd/util/subnetworkid"
"io"
"math"
@ -80,7 +81,7 @@ var feeBucket = []byte("fees")
func (node *blockNode) getBluesFeeData(dag *BlockDAG) (map[daghash.Hash]compactFeeData, error) {
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 {
feeData, err := dbFetchFeeData(dbTx, blueBlock.hash)
if err != nil {
@ -92,6 +93,9 @@ func (node *blockNode) getBluesFeeData(dag *BlockDAG) (map[daghash.Hash]compactF
return nil
})
if err != nil {
return nil, err
}
return bluesFeeData, nil
}
@ -119,26 +123,31 @@ func dbFetchFeeData(dbTx database.Tx, blockHash *daghash.Hash) (compactFeeData,
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() {
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 {
return err
}
if !expectedFeeTransaction.TxHash().IsEqual(block.FeeTransaction().Hash()) {
return ruleError(ErrBadFeeTransaction, "Fee transaction is not built as expected")
if !expectedCoinbaseTransaction.Hash().IsEqual(block.CoinbaseTransaction().Hash()) {
return ruleError(ErrBadCoinbaseTransaction, "Coinbase transaction is not built as expected")
}
return nil
}
// buildFeeTransaction returns the expected fee transaction for the current block
func (node *blockNode) buildFeeTransaction(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData) (*wire.MsgTx, error) {
// expectedCoinbaseTransaction returns the coinbase transaction for the current block
func (node *blockNode) expectedCoinbaseTransaction(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData, pkScript []byte, extraData []byte) (*util.Tx, error) {
bluesFeeData, err := node.getBluesFeeData(dag)
if err != nil {
return nil, err
@ -148,7 +157,7 @@ func (node *blockNode) buildFeeTransaction(dag *BlockDAG, txsAcceptanceData Mult
txOuts := []*wire.TxOut{}
for _, blue := range node.blues {
txIn, txOut, err := feeInputAndOutputForBlueBlock(blue, txsAcceptanceData, bluesFeeData)
txIn, txOut, err := coinbaseInputAndOutputForBlueBlock(dag, blue, txsAcceptanceData, bluesFeeData)
if err != nil {
return nil, err
}
@ -157,13 +166,59 @@ func (node *blockNode) buildFeeTransaction(dag *BlockDAG, txsAcceptanceData Mult
txOuts = append(txOuts, txOut)
}
}
feeTx := wire.NewNativeMsgTx(wire.TxVersion, txIns, txOuts)
return txsort.Sort(feeTx), nil
payload, err := SerializeCoinbasePayload(pkScript, extraData)
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
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) {
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
}
// the scriptPubKey for the fee is the same as the coinbase's first scriptPubKey
pkScript := blockTxsAcceptanceData[0].Tx.MsgTx().TxOut[0].PkScript
// the PkScript for the coinbase is parsed from the coinbase payload
pkScript, _, err := DeserializeCoinbasePayload(blockTxsAcceptanceData[0].Tx.MsgTx())
if err != nil {
return nil, nil, err
}
txOut := &wire.TxOut{
Value: totalFees,
Value: totalReward,
PkScript: pkScript,
}

View File

@ -160,10 +160,10 @@ func loadUTXOSet(filename string) (UTXOSet, error) {
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.
func (dag *BlockDAG) TestSetBlockRewardMaturity(maturity uint64) {
dag.dagParams.BlockRewardMaturity = maturity
func (dag *BlockDAG) TestSetCoinbaseMaturity(maturity uint64) {
dag.dagParams.BlockCoinbaseMaturity = maturity
}
// 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.
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
// can be included within a block at any given height or time.
if IsBlockReward(tx) {
if tx.IsCoinBase() {
return sequenceLock, nil
}
@ -574,7 +574,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bo
return errors.New(newErrString)
}
err = node.validateFeeTransaction(dag, block, txsAcceptanceData)
err = node.validateCoinbaseTransaction(dag, block, txsAcceptanceData)
if err != nil {
return err
}
@ -667,7 +667,7 @@ func (dag *BlockDAG) validateGasLimit(block *util.Block) error {
msgTx := tx.MsgTx()
// 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.
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 += msgTx.Gas
if gasUsageInSubnetwork < gasUsageInAllSubnetworks[msgTx.SubnetworkID] { // protect from overflows
@ -741,25 +741,25 @@ func (dag *BlockDAG) updateFinalityPoint() {
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.
func (dag *BlockDAG) NextBlockFeeTransaction() (*wire.MsgTx, error) {
func (dag *BlockDAG) NextBlockCoinbaseTransaction(pkScript []byte, extraData []byte) (*util.Tx, error) {
dag.dagLock.RLock()
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
func (dag *BlockDAG) NextBlockFeeTransactionNoLock() (*wire.MsgTx, error) {
func (dag *BlockDAG) NextBlockCoinbaseTransactionNoLock(pkScript []byte, extraData []byte) (*util.Tx, error) {
txsAcceptanceData, err := dag.TxsAcceptedByVirtual()
if err != nil {
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
@ -1010,12 +1010,18 @@ func (node *blockNode) updateParentsDiffs(dag *BlockDAG, newBlockUTXO UTXOSet) e
if err != nil {
return err
}
dag.utxoDiffStore.setBlockDiffChild(parent, node)
err = dag.utxoDiffStore.setBlockDiffChild(parent, node)
if err != nil {
return err
}
diff, err := newBlockUTXO.diffFrom(parentUTXO)
if err != nil {
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()
// 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.
dag.TestSetBlockRewardMaturity(1)
dag.TestSetCoinbaseMaturity(1)
for i := 1; i < len(blocks); i++ {
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
@ -103,9 +103,9 @@ func TestHaveBlock(t *testing.T) {
}
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.
dag.TestSetBlockRewardMaturity(1)
dag.TestSetCoinbaseMaturity(1)
for i := 1; i < len(blocks); i++ {
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
@ -189,10 +189,10 @@ func TestHaveBlock(t *testing.T) {
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
// Block 3b should be present (as a second child of Block 2).
{hash: "4bb2e2f55fabd67e217126dbc41e7101d0d6058800368c428cd6e397c111ee47", want: true},
{hash: "13580c9c2ed13caeedbad15167ee47bc1d26b4f88cc13054893a6d795c3baa7b", want: true},
// Block 100000 should be present (as an orphan).
{hash: "4e530ee9f967de3b2cd47ac5cd00109bb9ed7b0e30a60485c94badad29ecb4ce", want: true},
{hash: "01b4758d5e637d232b716cdc937335ebc5b8ea7f912b6a7fd2fc42b66b1d4d3e", want: true},
// Random hashes should not be available.
{hash: "123", want: false},
@ -802,9 +802,9 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
}
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.
dag.TestSetBlockRewardMaturity(1)
dag.TestSetCoinbaseMaturity(1)
guard := monkey.Patch(targetFunction, replacementFunction)
defer guard.Unpatch()
@ -884,7 +884,7 @@ func TestConfirmations(t *testing.T) {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
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.
genesisConfirmations, err := dag.blockConfirmations(dag.genesis)
@ -999,7 +999,7 @@ func TestAcceptingBlock(t *testing.T) {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
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.
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
//
// 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
//
// Example 1:

View File

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

View File

@ -141,7 +141,7 @@ const (
ErrOverwriteTx
// 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
// ErrSpendTooHigh indicates a transaction is attempting to spend more
@ -164,34 +164,12 @@ const (
// coinbase transaction.
ErrMultipleCoinbases
// ErrBadCoinbaseScriptLen indicates the length of the signature script
// for a coinbase transaction is not within the valid range.
ErrBadCoinbaseScriptLen
// ErrBadCoinbasePayloadLen indicates the length of the payload
// for a coinbase transaction is too high.
ErrBadCoinbasePayloadLen
// ErrBadCoinbaseValue indicates the amount of a coinbase value does
// not match the expected value of the subsidy plus the sum of all fees.
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
// ErrBadCoinbaseTransaction indicates that the block's coinbase transaction is not build as expected
ErrBadCoinbaseTransaction
// ErrScriptMalformed indicates a transaction script is malformed in
// some way. For example, it might be longer than the maximum allowed
@ -274,13 +252,8 @@ var errorCodeStrings = map[ErrorCode]string{
ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen",
ErrBadCoinbaseValue: "ErrBadCoinbaseValue",
ErrMissingCoinbaseBlueScore: "ErrMissingCoinbaseBlueScore",
ErrBadCoinbaseBlueScore: "ErrBadCoinbaseBlueScore",
ErrSecondTxNotFeeTransaction: "ErrSecondTxNotFeeTransaction",
ErrBadFeeTransaction: "ErrBadFeeTransaction",
ErrMultipleFeeTransactions: "ErrMultipleFeeTransactions",
ErrBadCoinbasePayloadLen: "ErrBadCoinbasePayloadLen",
ErrBadCoinbaseTransaction: "ErrBadCoinbaseTransaction",
ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation",
ErrParentBlockUnknown: "ErrParentBlockUnknown",

View File

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

View File

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

View File

@ -46,7 +46,7 @@ func TestFinality(t *testing.T) {
}
defer teardownFunc()
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 {
return nil, err
}
@ -145,7 +145,7 @@ func TestFinality(t *testing.T) {
func TestSubnetworkRegistry(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
params.BlockRewardMaturity = 1
params.BlockCoinbaseMaturity = 1
dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{
DAGParams: &params,
})
@ -170,7 +170,7 @@ func TestSubnetworkRegistry(t *testing.T) {
func TestChainedTransactions(t *testing.T) {
params := dagconfig.SimNetParams
params.BlockRewardMaturity = 1
params.BlockCoinbaseMaturity = 1
// Create a new database and dag instance to run tests against.
dag, teardownFunc, err := blockdag.DAGSetup("TestChainedTransactions", blockdag.Config{
DAGParams: &params,
@ -180,7 +180,7 @@ func TestChainedTransactions(t *testing.T) {
}
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 {
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})
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 {
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})
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 {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@ -274,7 +274,7 @@ func TestChainedTransactions(t *testing.T) {
func TestGasLimit(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
params.BlockRewardMaturity = 1
params.BlockCoinbaseMaturity = 1
dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{
DAGParams: &params,
})
@ -290,16 +290,21 @@ func TestGasLimit(t *testing.T) {
t.Fatalf("could not register network: %s", err)
}
fundsBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), nil, false, 2)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, err := dag.ProcessBlock(util.NewBlock(fundsBlock), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if isOrphan {
t.Fatalf("ProcessBlock: funds block got unexpectedly orphan")
cbTxs := []*wire.MsgTx{}
for i := 0; i < 4; i++ {
fundsBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), nil, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, err := dag.ProcessBlock(util.NewBlock(fundsBlock), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if isOrphan {
t.Fatalf("ProcessBlock: fundsBlock got unexpectedly orphan")
}
cbTxs = append(cbTxs, fundsBlock.Transactions[util.CoinbaseTransactionIndex])
}
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)
}
cbTxValue := fundsBlock.Transactions[0].TxOut[0].Value
cbTxID := fundsBlock.Transactions[0].TxID()
tx1In := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 0),
PreviousOutpoint: *wire.NewOutpoint(cbTxs[0].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
tx1Out := &wire.TxOut{
Value: cbTxValue,
Value: cbTxs[0].TxOut[0].Value,
PkScript: pkScript,
}
tx1 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx1In}, []*wire.TxOut{tx1Out}, subnetworkID, 10000, []byte{})
tx2In := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 1),
PreviousOutpoint: *wire.NewOutpoint(cbTxs[1].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
tx2Out := &wire.TxOut{
Value: cbTxValue,
Value: cbTxs[1].TxOut[0].Value,
PkScript: pkScript,
}
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
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 {
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 {
t.Fatalf("ProcessBlock expected to have an error")
}
@ -357,19 +359,19 @@ func TestGasLimit(t *testing.T) {
}
overflowGasTxIn := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 1),
PreviousOutpoint: *wire.NewOutpoint(cbTxs[2].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
overflowGasTxOut := &wire.TxOut{
Value: cbTxValue,
Value: cbTxs[2].TxOut[0].Value,
PkScript: pkScript,
}
overflowGasTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{overflowGasTxIn}, []*wire.TxOut{overflowGasTxOut},
subnetworkID, math.MaxUint64, []byte{})
// 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 {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@ -389,18 +391,18 @@ func TestGasLimit(t *testing.T) {
nonExistentSubnetwork := &subnetworkid.SubnetworkID{123}
nonExistentSubnetworkTxIn := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxID, 0),
PreviousOutpoint: *wire.NewOutpoint(cbTxs[3].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
nonExistentSubnetworkTxOut := &wire.TxOut{
Value: cbTxValue,
Value: cbTxs[3].TxOut[0].Value,
PkScript: pkScript,
}
nonExistentSubnetworkTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{nonExistentSubnetworkTxIn},
[]*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 {
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
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 {
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
// signature script of the coinbase transaction of a new block.
func standardCoinbaseScript(blueScore uint64, extraNonce uint64) ([]byte, error) {
return txscript.NewScriptBuilder().AddInt64(int64(blueScore)).
// signature script of the coinbase transaction of a new block. In particular,
// it starts with the block height that is required by version 2 blocks.
func standardCoinbaseScript(extraNonce uint64) ([]byte, error) {
return txscript.NewScriptBuilder().
AddInt64(int64(extraNonce)).Script()
}
@ -272,11 +273,10 @@ func uniqueOpReturnScript() []byte {
}
// createCoinbaseTx returns a coinbase transaction paying an appropriate
// subsidy based on the passed block height. The coinbase signature script
// conforms to the requirements of version 2 blocks.
// subsidy based on the passed block height.
func (g *testGenerator) createCoinbaseTx(blueScore uint64) *wire.MsgTx {
extraNonce := uint64(0)
coinbaseScript, err := standardCoinbaseScript(blueScore, extraNonce)
coinbaseScript, err := standardCoinbaseScript(extraNonce)
if err != nil {
panic(err)
}
@ -910,7 +910,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
// genesis -> bm0 -> bm1 -> ... -> bm99
// ---------------------------------------------------------------------
coinbaseMaturity := g.params.BlockRewardMaturity
coinbaseMaturity := g.params.BlockCoinbaseMaturity
var testInstances []TestInstance
for i := uint64(0); i < coinbaseMaturity; 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.
// ---------------------------------------------------------------------
// 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.
// ---------------------------------------------------------------------
@ -1161,7 +1122,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
g.setTip("b15")
tooSmallCbScript := repeatOpcode(0x00, minCoinbaseScriptLen-1)
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
// outright rejected due to an invalid parent.
@ -1177,7 +1138,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
g.setTip("b15")
tooLargeCbScript := repeatOpcode(0x00, maxCoinbaseScriptLen+1)
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
// outright rejected due to an invalid parent.
@ -1847,7 +1808,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
// \-> b68(20)
g.setTip("b65")
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
// the extra 10 fee.

View File

@ -119,7 +119,7 @@ var regressionNetParams = &dagconfig.Params{
GenesisHash: newHashFromStr("5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"),
PowLimit: regressionPowLimit,
PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 150,
TargetTimespan: time.Hour * 24 * 14, // 14 days
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
// required to have already gone through full validation, it has
// already been proven on the first transaction in the block is
// a coinbase, and the second one is a fee transaction.
if txIdx > 1 {
// a coinbase.
if txIdx > util.CoinbaseTransactionIndex {
for _, txIn := range tx.MsgTx().TxIn {
// The UTXO should always have the input since
// the index contract requires it, however, be

View File

@ -40,7 +40,7 @@ func TestTxIndexConnectBlock(t *testing.T) {
indexManager := NewManager([]Indexer{txIndex})
params := dagconfig.SimNetParams
params.BlockRewardMaturity = 1
params.BlockCoinbaseMaturity = 1
params.K = 1
config := blockdag.Config{
@ -57,7 +57,7 @@ func TestTxIndexConnectBlock(t *testing.T) {
}
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 {
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())
calculatedIDMerkleRoot := idMerkleTree.Root()
wantIDMerkleRoot, err := daghash.NewHashFromStr("65308857c92c4e5dd3c5e61b73d6b78a87456b5f8f16b13c1e02c47768a0b881")
wantIDMerkleRoot, err := daghash.NewHashFromStr("3f69feb7edf5d0d67930afc990c8ec931e3428d7c7a65d7af6b81079319eb110")
if err != nil {
t.Errorf("BuildIDMerkleTreeStore: unexpected error: %s", err)
}

View File

@ -6,7 +6,6 @@ package blockdag
import (
"fmt"
"math"
"runtime"
"time"
@ -180,16 +179,16 @@ func newTxValidator(utxoSet UTXOSet, flags txscript.ScriptFlags, sigCache *txscr
// ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines.
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
// validation.
txIns := tx.MsgTx().TxIn
txValItems := make([]*txValidateItem, 0, len(txIns))
for txInIdx, txIn := range txIns {
// Skip block reward transactions.
if txIn.PreviousOutpoint.Index == math.MaxUint32 {
continue
}
txVI := &txValidateItem{
txInIndex: txInIdx,
txIn: txIn,
@ -214,12 +213,11 @@ func checkBlockScripts(block *blockNode, utxoSet UTXOSet, transactions []*util.T
}
txValItems := make([]*txValidateItem, 0, numInputs)
for _, tx := range transactions {
// Skip coinbase transactions.
if tx.IsCoinBase() {
continue
}
for txInIdx, txIn := range tx.MsgTx().TxIn {
// Skip block reward transactions.
if txIn.PreviousOutpoint.Index == math.MaxUint32 {
continue
}
txVI := &txValidateItem{
txInIndex: txInIdx,
txIn: txIn,

View File

@ -9,7 +9,6 @@ import (
"github.com/daglabs/btcd/util/subnetworkid"
"github.com/daglabs/btcd/dagconfig"
"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/txscript"
@ -136,48 +135,6 @@ func createTxForTest(numInputs uint32, numOutputs uint32, outputValue uint64, su
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
func SetVirtualForTest(dag *BlockDAG, virtual *virtualBlock) *virtualBlock {
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
// in the lowest bit.
headerCode := uint64(entry.BlockBlueScore()) << 1
if entry.IsBlockReward() {
if entry.IsCoinbase() {
headerCode |= 0x01
}
@ -282,9 +282,9 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) {
// Decode the header code.
//
// Bit 0 indicates whether the containing transaction is a block reward.
// Bits 1-x encode the blue score of the containing transaction.
isBlockReward := code&0x01 != 0
// Bit 0 indicates whether the containing transaction is a coinbase.
// Bits 1-x encode blue score of the containing transaction.
isCoinbase := code&0x01 != 0
blockBlueScore := code >> 1
// Decode the compressed unspent transaction output.
@ -300,8 +300,8 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) {
blockBlueScore: blockBlueScore,
packedFlags: 0,
}
if isBlockReward {
entry.packedFlags |= tfBlockReward
if isCoinbase {
entry.packedFlags |= tfCoinbase
}
return entry, nil

View File

@ -20,7 +20,7 @@ const (
)
// 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
// much it pays.
type UTXOEntry struct {
@ -35,16 +35,16 @@ type UTXOEntry struct {
blockBlueScore uint64 // Blue score of the block containing the tx.
// 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
// usage since there will be a lot of these in memory.
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.
func (entry *UTXOEntry) IsBlockReward() bool {
return entry.packedFlags&tfBlockReward == tfBlockReward
func (entry *UTXOEntry) IsCoinbase() bool {
return entry.packedFlags&tfCoinbase == tfCoinbase
}
// BlockBlueScore returns the blue score of the block containing the output.
@ -73,20 +73,20 @@ func (entry *UTXOEntry) IsUnmined() bool {
type txoFlags uint8
const (
// tfBlockReward indicates that a txout was contained in a block reward tx (coinbase or fee transaction).
tfBlockReward txoFlags = 1 << iota
// tfCoinbase indicates that a txout was contained in a coinbase tx.
tfCoinbase txoFlags = 1 << iota
)
// 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{
amount: txOut.Value,
pkScript: txOut.PkScript,
blockBlueScore: blockBlueScore,
}
if isBlockReward {
entry.packedFlags |= tfBlockReward
if isCoinbase {
entry.packedFlags |= tfCoinbase
}
return entry
@ -407,8 +407,8 @@ type UTXOSet interface {
// 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) {
diff := NewUTXODiff()
isBlockReward := tx.IsBlockReward()
if !isBlockReward {
isCoinbase := tx.IsCoinBase()
if !isCoinbase {
for _, txIn := range tx.TxIn {
if entry, ok := u.Get(txIn.PreviousOutpoint); ok {
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 {
entry := NewUTXOEntry(txOut, isBlockReward, containingNode.blueScore)
entry := NewUTXOEntry(txOut, isCoinbase, containingNode.blueScore)
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
err := diff.AddEntry(outpoint, entry)
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
// necessarily means there's an error).
func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool, err error) {
isBlockReward := tx.IsBlockReward()
if !isBlockReward {
isCoinbase := tx.IsCoinBase()
if !isCoinbase {
if !fus.containsInputs(tx) {
return false, nil
}
@ -488,7 +488,7 @@ func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool
for i, txOut := range tx.TxOut {
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
entry := NewUTXOEntry(txOut, isBlockReward, blueScore)
entry := NewUTXOEntry(txOut, isCoinbase, blueScore)
err := fus.addAndUpdateMultiset(outpoint, entry)
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
func (dus *DiffUTXOSet) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (bool, error) {
isBlockReward := tx.IsBlockReward()
if !isBlockReward && !dus.containsInputs(tx) {
isCoinbase := tx.IsCoinBase()
if !isCoinbase && !dus.containsInputs(tx) {
return false, nil
}
err := dus.appendTx(tx, blockBlueScore, isBlockReward)
err := dus.appendTx(tx, blockBlueScore, isCoinbase)
if err != nil {
return false, err
}
@ -628,8 +628,8 @@ func (dus *DiffUTXOSet) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (bool, erro
return true, nil
}
func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isBlockReward bool) error {
if !isBlockReward {
func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isCoinbase bool) error {
if !isCoinbase {
for _, txIn := range tx.TxIn {
outpoint := *wire.NewOutpoint(&txIn.PreviousOutpoint.TxID, txIn.PreviousOutpoint.Index)
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 {
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
entry := NewUTXOEntry(txOut, isBlockReward, blockBlueScore)
entry := NewUTXOEntry(txOut, isCoinbase, blockBlueScore)
err := dus.UTXODiff.AddEntry(outpoint, entry)
if err != nil {

View File

@ -1,12 +1,12 @@
package blockdag
import (
"github.com/daglabs/btcd/util/subnetworkid"
"math"
"reflect"
"testing"
"github.com/daglabs/btcd/btcec"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/util/daghash"
"github.com/daglabs/btcd/wire"
)
@ -723,15 +723,15 @@ func TestUTXOSetDiffRules(t *testing.T) {
// TestDiffUTXOSet_addTx makes sure that diffUTXOSet addTx works as expected
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")
txIn0 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: wire.Outpoint{TxID: *txID0, Index: math.MaxUint32}, Sequence: 0}
txOut0 := &wire.TxOut{PkScript: []byte{0}, Value: 10}
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
id1 := transaction0.TxID()
// transaction1 spends coinbaseTX
id1 := coinbaseTX.TxID()
outpoint1 := *wire.NewOutpoint(id1, 0)
txIn1 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: outpoint1, Sequence: 0}
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",
startSet: NewDiffUTXOSet(NewFullUTXOSet(), NewUTXODiff()),
startHeight: 0,
toAdd: []*wire.MsgTx{transaction0},
toAdd: []*wire.MsgTx{coinbaseTX},
expectedSet: &DiffUTXOSet{
base: &FullUTXOSet{utxoCollection: utxoCollection{}},
UTXODiff: &UTXODiff{
@ -893,10 +893,11 @@ func TestDiffFromTx(t *testing.T) {
fus := addMultisetToFullUTXOSet(t, &FullUTXOSet{
utxoCollection: utxoCollection{},
})
cbTx, err := createCoinbaseTxForTest(1, 1, 0, &dagconfig.SimNetParams)
if err != nil {
t.Errorf("createCoinbaseTxForTest: %v", err)
}
txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
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 {
t.Fatalf("AddTx unexpectedly failed. Error: %s", err)
} else if !isAccepted {

View File

@ -29,11 +29,8 @@ const (
// hours.
MaxTimeOffsetSeconds = 2 * 60 * 60
// MinCoinbaseScriptLen is the minimum length a coinbase script can be.
MinCoinbaseScriptLen = 2
// MaxCoinbaseScriptLen is the maximum length a coinbase script can be.
MaxCoinbaseScriptLen = 100
// MaxCoinbasePayloadLen is the maximum length a coinbase payload can be.
MaxCoinbasePayloadLen = 150
// medianTimeBlocks is the number of previous blocks which should be
// used to calculate the median time used to validate block timestamps.
@ -53,28 +50,6 @@ func isNullOutpoint(outpoint *wire.Outpoint) bool {
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
// met, meaning that all the inputs of a given transaction have reached a
// 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
// ensure it is sane. These checks are context free.
func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID) error {
isCoinbase := tx.IsCoinBase()
// A transaction must have at least one input.
msgTx := tx.MsgTx()
if len(msgTx.TxIn) == 0 {
if !isCoinbase && len(msgTx.TxIn) == 0 {
return ruleError(ErrNoTxInputs, "transaction has no inputs")
}
@ -210,14 +186,14 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
existingTxOut[txIn.PreviousOutpoint] = struct{}{}
}
// Coinbase script length must be between min and max length.
if IsCoinBase(tx) {
slen := len(msgTx.TxIn[0].SignatureScript)
if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen {
str := fmt.Sprintf("coinbase transaction script length "+
"of %d is out of range (min: %d, max: %d)",
slen, MinCoinbaseScriptLen, MaxCoinbaseScriptLen)
return ruleError(ErrBadCoinbaseScriptLen, str)
// Coinbase payload length must not exceed the max length.
if isCoinbase {
payloadLen := len(msgTx.Payload)
if payloadLen > MaxCoinbasePayloadLen {
str := fmt.Sprintf("coinbase transaction payload length "+
"of %d is out of range (max: %d)",
payloadLen, MaxCoinbasePayloadLen)
return ruleError(ErrBadCoinbasePayloadLen, str)
}
} else {
// 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")
}
// 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) ||
msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry)) &&
msgTx.SubnetworkID.IsBuiltIn()) &&
msgTx.Gas > 0 {
return ruleError(ErrInvalidGas, "transaction in the native or "+
@ -265,10 +241,10 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
"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
isLocalNodeFull := subnetworkID == nil
shouldTxBeFull := msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry) ||
shouldTxBeFull := msgTx.SubnetworkID.IsBuiltIn() ||
msgTx.SubnetworkID.IsEqual(subnetworkID)
if !isLocalNodeFull && !shouldTxBeFull && len(msgTx.Payload) > 0 {
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
// precise, signature operation counting mechanism from the script engine which
// requires access to the input transaction scripts.
func CountP2SHSigOps(tx *util.Tx, isBlockReward bool, utxoSet UTXOSet) (int, error) {
// Block reward transactions have no interesting inputs.
if isBlockReward {
func CountP2SHSigOps(tx *util.Tx, isCoinbase bool, utxoSet UTXOSet) (int, error) {
// Coinbase transactions have no interesting inputs.
if isCoinbase {
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.
transactions := block.Transactions()
if !IsCoinBase(transactions[0]) {
if !transactions[util.CoinbaseTransactionIndex].IsCoinBase() {
return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
"block is not a coinbase")
}
isGenesis := block.MsgBlock().Header.IsGenesis()
if !isGenesis && !IsFeeTransaction(transactions[1]) {
return ruleError(ErrSecondTxNotFeeTransaction, "second transaction in "+
"block is not a fee transaction")
}
txOffset := 2
if isGenesis {
txOffset = 1
}
txOffset := util.CoinbaseTransactionIndex + 1
// A block must not have more than one coinbase. And transactions must be
// ordered by subnetwork
for i, tx := range transactions[txOffset:] {
if IsCoinBase(tx) {
if tx.IsCoinBase() {
str := fmt.Sprintf("block contains second coinbase at "+
"index %d", i+2)
return ruleError(ErrMultipleCoinbases, str)
}
if IsFeeTransaction(tx) {
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) {
if i != 0 && subnetworkid.Less(&tx.MsgTx().SubnetworkID, &transactions[i].MsgTx().SubnetworkID) {
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) (
txFeeInSatoshi uint64, err error) {
// Block reward transactions (a.k.a. coinbase or fee transactions)
// have no standard inputs to validate.
if IsBlockReward(tx) {
// Coinbase transactions have no standard inputs to validate.
if tx.IsCoinBase() {
return 0, nil
}
@ -815,7 +776,7 @@ func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txBlueScore uint64, utxoS
}
if !fastAdd {
if err = validateBlockRewardMaturity(dagParams, entry, txBlueScore, txIn); err != nil {
if err = validateCoinbaseMaturity(dagParams, entry, txBlueScore, txIn); err != nil {
return 0, err
}
}
@ -873,19 +834,19 @@ func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txBlueScore uint64, utxoS
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
// yet reached the required block reward maturity.
if entry.IsBlockReward() {
// yet reached the required coinbase maturity.
if entry.IsCoinbase() {
originBlueScore := entry.BlockBlueScore()
BlueScoreSincePrev := txBlueScore - originBlueScore
if BlueScoreSincePrev < dagParams.BlockRewardMaturity {
str := fmt.Sprintf("tried to spend block reward "+
blueScoreSincePrev := txBlueScore - originBlueScore
if blueScoreSincePrev < dagParams.BlockCoinbaseMaturity {
str := fmt.Sprintf("tried to spend coinbase "+
"transaction output %s from blue score %d "+
"to blue score %d before required maturity "+
"of %d", txIn.PreviousOutpoint,
originBlueScore, txBlueScore,
dagParams.BlockRewardMaturity)
dagParams.BlockCoinbaseMaturity)
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
// bounds.
// 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
compactFeeFactory := newCompactFeeFactory()
@ -957,22 +918,6 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet,
}
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
// 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 {
numsigOps := CountSigOps(tx)
// Since the first transaction has already been verified to be a
// coinbase transaction, and the second transaction has already
// been verified to be a fee transaction, use i < 2 as an
// optimization for the flag to countP2SHSigOps for whether or
// not the transaction is a block reward transaction rather than
// having to do a full coinbase and fee transaction check again.
numP2SHSigOps, err := CountP2SHSigOps(tx, i < 2, pastUTXO)
// coinbase transaction, use i != util.CoinbaseTransactionIndex
// as an optimization for the flag to countP2SHSigOps for whether
// or not the transaction is a coinbase transaction rather than
// having to do a full coinbase check again.
numP2SHSigOps, err := CountP2SHSigOps(tx, i == util.CoinbaseTransactionIndex, pastUTXO)
if err != nil {
return err
}

View File

@ -76,9 +76,9 @@ func TestCheckConnectBlockTemplate(t *testing.T) {
}
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.
dag.TestSetBlockRewardMaturity(1)
dag.TestSetCoinbaseMaturity(1)
// Load up blocks such that there is a side chain.
// (genesis block) -> 1 -> 2 -> 3 -> 4
@ -611,6 +611,30 @@ func TestCheckTransactionSanity(t *testing.T) {
nil,
func(tx *wire.MsgTx) { tx.TxIn[1].PreviousOutpoint.Index = 0 },
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,
*subnetworkid.SubnetworkIDNative,
&txSubnetworkData{subnetworkid.SubnetworkIDNative, 1, []byte{}},
@ -687,10 +711,10 @@ var Block100000 = wire.MsgBlock{
},
},
HashMerkleRoot: &daghash.Hash{
0x30, 0xed, 0xf5, 0xbd, 0xd1, 0x4f, 0x8f, 0xb2,
0x0b, 0x6c, 0x92, 0xac, 0xd2, 0x47, 0xb7, 0xd6,
0x6f, 0x22, 0xfa, 0x60, 0x36, 0x80, 0x99, 0xc3,
0x6e, 0x39, 0x14, 0x9b, 0xcc, 0x1f, 0x31, 0xa9,
0x32, 0x30, 0x46, 0x39, 0x5e, 0x27, 0x6d, 0x5a,
0xc9, 0x64, 0x16, 0x29, 0x5b, 0xa4, 0x5a, 0xf3,
0xc0, 0xfc, 0x1a, 0xa9, 0xcb, 0x2a, 0xd2, 0x9f,
0xbe, 0x07, 0x0c, 0x47, 0xc9, 0x84, 0x39, 0x15,
},
AcceptedIDMerkleRoot: &daghash.Hash{
0x8a, 0xb7, 0xd6, 0x73, 0x1b, 0xe6, 0xc5, 0xd3,
@ -701,7 +725,7 @@ var Block100000 = wire.MsgBlock{
UTXOCommitment: &daghash.ZeroHash,
Timestamp: time.Unix(0x5c404bc3, 0),
Bits: 0x207fffff,
Nonce: 0xdffffffffffffff9,
Nonce: 0x00000001,
},
Transactions: []*wire.MsgTx{
{
@ -709,27 +733,37 @@ var Block100000 = wire.MsgBlock{
TxIn: []*wire.TxIn{
{
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,
},
SignatureScript: []byte{
0x02, 0x10, 0x27, 0x08, 0x8f, 0x22, 0xfb, 0x88,
0x45, 0x7b, 0xee, 0xeb, 0x0b, 0x2f, 0x50, 0x32,
0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, 0x64, 0x2f,
},
Sequence: math.MaxUint64,
SignatureScript: nil,
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x12a05f200, // 5000000000
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,
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,
@ -1006,34 +1040,37 @@ var BlockWithWrongTxOrder = wire.MsgBlock{
TxIn: []*wire.TxIn{
{
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,
},
SignatureScript: []byte{
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
},
Sequence: math.MaxUint64,
SignatureScript: nil,
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x12a05f200, // 5000000000
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
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
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.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,

View File

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

View File

@ -30,13 +30,13 @@ const (
// PkScript bytes.
outputSize uint64 = 8 + 1 + 25
txLifeSpan = 1000
requiredConfirmations = 10
approximateConfirmationsForBlockRewardMaturity = 150
searchRawTransactionResultCount = 1000
searchRawTransactionMaxResults = 5000
txMaxQueueLength = 10000
maxResendDepth = 500
txLifeSpan = 1000
requiredConfirmations = 10
approximateConfirmationsForCoinbaseMaturity = 150
searchRawTransactionResultCount = 1000
searchRawTransactionMaxResults = 5000
txMaxQueueLength = 10000
maxResendDepth = 500
)
type walletTransaction struct {
@ -386,10 +386,10 @@ func addTxOutsToUTXOSet(walletUTXOSet utxoSet, tx *wire.MsgTx) {
}
func isTxMatured(tx *wire.MsgTx, confirmations uint64) bool {
if !tx.IsBlockReward() {
if !tx.IsCoinBase() {
return confirmations >= requiredConfirmations
}
return confirmations >= approximateConfirmationsForBlockRewardMaturity
return confirmations >= approximateConfirmationsForCoinbaseMaturity
}
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")
}
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 {
t.Errorf("subnetworkid.SubnetworkIDRegistry value was changed from 1, therefore you probably need to update the help text for SubnetworkID")
if *subnetworkid.SubnetworkIDCoinbase != one {
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"
"github.com/daglabs/btcd/util/daghash"
"github.com/daglabs/btcd/util/subnetworkid"
"github.com/daglabs/btcd/wire"
)
@ -16,7 +17,7 @@ var genesisTxIns = []*wire.TxIn{
{
PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID{},
Index: 0xffffffff,
Index: math.MaxUint32,
},
SignatureScript: []byte{
0x00, 0x00, 0x0b, 0x2f, 0x50, 0x32, 0x53, 0x48,
@ -25,35 +26,35 @@ var genesisTxIns = []*wire.TxIn{
Sequence: math.MaxUint64,
},
}
var genesisTxOuts = []*wire.TxOut{
{
Value: 0x12a05f200,
PkScript: []byte{
0x51,
},
},
var genesisTxOuts = []*wire.TxOut{}
var genesisTxPayload = []byte{
0x17, // Varint
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
// 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).
var genesisHash = daghash.Hash([daghash.HashSize]byte{
0x65, 0xf3, 0x2f, 0x2d, 0x93, 0xb8, 0xff, 0x42,
0x20, 0x1a, 0x31, 0xa7, 0xf9, 0x1c, 0x1a, 0x8c,
0x27, 0x7e, 0xa1, 0x7b, 0xa7, 0x5a, 0x01, 0xfc,
0x21, 0xb4, 0xbf, 0x7d, 0xc1, 0x81, 0xc0, 0x09,
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,
})
// genesisMerkleRoot is the hash of the first transaction in the genesis block
// for the main network.
var genesisMerkleRoot = daghash.Hash([daghash.HashSize]byte{
0xd4, 0xdc, 0x8b, 0xb8, 0x76, 0x57, 0x9d, 0x7d,
0xe9, 0x9d, 0xae, 0xdb, 0xf8, 0x22, 0xd2, 0x0d,
0xa2, 0xe0, 0xbb, 0xbe, 0xed, 0xb0, 0xdb, 0xba,
0xeb, 0x18, 0x4d, 0x42, 0x01, 0xff, 0xed, 0x9d,
0x72, 0x10, 0x35, 0x85, 0xdd, 0xac, 0x82, 0x5c,
0x49, 0x13, 0x9f, 0xc0, 0x0e, 0x37, 0xc0, 0x45,
0x71, 0xdf, 0xd9, 0xf6, 0x36, 0xdf, 0x4c, 0x42,
0x72, 0x7b, 0x9e, 0x86, 0xdd, 0x37, 0xd2, 0xbd,
})
// genesisBlock defines the genesis block of the block DAG which serves as the
@ -64,15 +65,10 @@ var genesisBlock = wire.MsgBlock{
ParentHashes: []*daghash.Hash{},
HashMerkleRoot: &genesisMerkleRoot,
AcceptedIDMerkleRoot: &daghash.Hash{},
UTXOCommitment: &daghash.Hash{
0x51, 0x9f, 0x81, 0xbb, 0x93, 0xfd, 0x72, 0x9d,
0x9d, 0xe0, 0x60, 0x80, 0xfa, 0x01, 0xe6, 0x34,
0x93, 0x22, 0xed, 0x67, 0x69, 0x14, 0x52, 0xca,
0x95, 0xd1, 0x9b, 0x77, 0xdb, 0xb8, 0x12, 0x80,
},
Timestamp: time.Unix(0x5cdac4b0, 0),
Bits: 0x207fffff,
Nonce: 0x3,
UTXOCommitment: &daghash.ZeroHash,
Timestamp: time.Unix(0x5cdac4b0, 0),
Bits: 0x207fffff,
Nonce: 0x1,
},
Transactions: []*wire.MsgTx{genesisCoinbaseTx},
}
@ -120,13 +116,13 @@ var simNetGenesisBlock = genesisBlock
// the main network, regression test network, and test network (version 3).
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).
var devNetGenesisHash = daghash.Hash([daghash.HashSize]byte{
0x7b, 0xea, 0x60, 0xdc, 0xaf, 0x9f, 0xc8, 0xf1,
0x28, 0x9d, 0xbf, 0xde, 0xf6, 0x3b, 0x1e, 0xe8,
0x4d, 0x4c, 0xc1, 0x8c, 0xaa, 0xe7, 0xdf, 0x08,
0xe2, 0xe7, 0xa3, 0x6a, 0x4c, 0x7c, 0x00, 0x00,
0xf4, 0xd6, 0x37, 0x0b, 0xc6, 0x67, 0x41, 0x90,
0x06, 0x57, 0xef, 0x65, 0x45, 0x07, 0x3a, 0x50,
0xb7, 0x85, 0x21, 0xb9, 0xa2, 0xe3, 0xf5, 0x9e,
0xe0, 0x45, 0x9b, 0xb0, 0xab, 0x33, 0x00, 0x00,
})
// devNetGenesisMerkleRoot is the hash of the first transaction in the genesis block
@ -141,15 +137,10 @@ var devNetGenesisBlock = wire.MsgBlock{
ParentHashes: []*daghash.Hash{},
HashMerkleRoot: &devNetGenesisMerkleRoot,
AcceptedIDMerkleRoot: &daghash.Hash{},
UTXOCommitment: &daghash.Hash{
0x51, 0x9f, 0x81, 0xbb, 0x93, 0xfd, 0x72, 0x9d,
0x9d, 0xe0, 0x60, 0x80, 0xfa, 0x01, 0xe6, 0x34,
0x93, 0x22, 0xed, 0x67, 0x69, 0x14, 0x52, 0xca,
0x95, 0xd1, 0x9b, 0x77, 0xdb, 0xb8, 0x12, 0x80,
},
Timestamp: time.Unix(0x5cee6864, 0),
Bits: 0x1e7fffff,
Nonce: 0xe834,
UTXOCommitment: &daghash.ZeroHash,
Timestamp: time.Unix(0x5d021d3e, 0),
Bits: 0x1e7fffff,
Nonce: 0x5221,
},
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
// main network as of protocol version 60002.
var genesisBlockBytes = []byte{
0x01, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xdc, 0x8b,
0xb8, 0x76, 0x57, 0x9d, 0x7d, 0xe9, 0x9d, 0xae,
0xdb, 0xf8, 0x22, 0xd2, 0x0d, 0xa2, 0xe0, 0xbb,
0xbe, 0xed, 0xb0, 0xdb, 0xba, 0xeb, 0x18, 0x4d,
0x42, 0x01, 0xff, 0xed, 0x9d, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x9f, 0x81,
0xbb, 0x93, 0xfd, 0x72, 0x9d, 0x9d, 0xe0, 0x60,
0x80, 0xfa, 0x01, 0xe6, 0x34, 0x93, 0x22, 0xed,
0x67, 0x69, 0x14, 0x52, 0xca, 0x95, 0xd1, 0x9b,
0x77, 0xdb, 0xb8, 0x12, 0x80, 0xb0, 0xc4, 0xda,
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f,
0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63,
0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, 0x2a, 0x01,
0x00, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x72, 0x10, 0x35, 0x85, 0xdd, 0xac, 0x82, 0x5c, 0x49, 0x13, 0x9f,
0xc0, 0x0e, 0x37, 0xc0, 0x45, 0x71, 0xdf, 0xd9, 0xf6, 0x36, 0xdf, 0x4c, 0x42, 0x72, 0x7b, 0x9e,
0x86, 0xdd, 0x37, 0xd2, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xc4, 0xda, 0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x0b, 0x2f, 0x50, 0x32, 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63,
0x64, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 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, 0xd2,
0xea, 0x82, 0x4e, 0xb8, 0x87, 0x42, 0xd0, 0x6d, 0x1f, 0x8d, 0xc3, 0xad, 0x9f, 0x43, 0x9e, 0xed,
0x6f, 0x43, 0x3c, 0x02, 0x71, 0x71, 0x69, 0xfb, 0xbc, 0x91, 0x44, 0xac, 0xf1, 0x93, 0xd3, 0x18,
0x17, 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71,
0xc7, 0x7e, 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87,
}
// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of

View File

@ -128,9 +128,9 @@ type Params struct {
// block in compact form.
PowLimitBits uint32
// BlockRewardMaturity is the number of blocks required before newly mined
// coins (coinbase or fee transactions) can be spent.
BlockRewardMaturity uint64
// BlockCoinbaseMaturity is the number of blocks required before newly mined
// coins can be spent.
BlockCoinbaseMaturity uint64
// SubsidyReductionInterval is the interval of blocks before the subsidy
// is reduced.
@ -221,7 +221,7 @@ var MainNetParams = Params{
GenesisHash: &genesisHash,
PowLimit: mainPowLimit,
PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second
@ -284,7 +284,7 @@ var RegressionNetParams = Params{
GenesisHash: &regTestGenesisHash,
PowLimit: regressionPowLimit,
PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 150,
TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second
@ -347,7 +347,7 @@ var TestNet3Params = Params{
GenesisHash: &testNet3GenesisHash,
PowLimit: testNet3PowLimit,
PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second
@ -414,7 +414,7 @@ var SimNetParams = Params{
GenesisHash: &simNetGenesisHash,
PowLimit: simNetPowLimit,
PowLimitBits: 0x207fffff,
BlockRewardMaturity: 100,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour
TargetTimePerBlock: time.Second * 1, // 1 second
@ -473,7 +473,7 @@ var DevNetParams = Params{
GenesisHash: &devNetGenesisHash,
PowLimit: devNetPowLimit,
PowLimitBits: util.BigToCompact(devNetPowLimit), // 0x1e7fffff
BlockRewardMaturity: 100,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 210000,
TargetTimespan: time.Hour * 1, // 1 hour
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))
// 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.
var maturityHeight uint64
if isCoinbase {
maturityHeight = m.currentHeight + m.net.BlockRewardMaturity
maturityHeight = m.currentHeight + m.net.BlockCoinbaseMaturity
}
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
// outputs.
if createTestChain && numMatureOutputs != 0 {
numToGenerate := (uint32(h.ActiveNet.BlockRewardMaturity) +
numToGenerate := (uint32(h.ActiveNet.BlockCoinbaseMaturity) +
numMatureOutputs)
_, err := h.Node.Generate(numToGenerate)
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.
if blockdag.IsBlockReward(tx) {
str := fmt.Sprintf("transaction %s is an individual block reward transaction",
// A standalone transaction must not be a coinbase transaction.
if tx.IsCoinBase() {
str := fmt.Sprintf("transaction %s is an individual coinbase transaction",
txID)
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
// transaction are NOT removed recursively because they are still
// valid.
err := mp.RemoveTransactions(block.Transactions()[util.FeeTransactionIndex+1:])
err := mp.RemoveTransactions(block.Transactions()[util.CoinbaseTransactionIndex+1:])
if err != nil {
mp.mpUTXOSet = oldUTXOSet
return err
}
for _, tx := range block.Transactions()[util.FeeTransactionIndex+1:] {
for _, tx := range block.Transactions()[util.CoinbaseTransactionIndex+1:] {
mp.RemoveDoubleSpends(tx)
mp.RemoveOrphan(tx)
acceptedTxs := mp.ProcessOrphans(tx)

View File

@ -121,7 +121,7 @@ func (p *poolHarness) CreateCoinbaseTx(blueScore uint64, numOutputs uint32) (*ut
// Create standard coinbase script.
extraNonce := int64(0)
coinbaseScript, err := txscript.NewScriptBuilder().
AddInt64(int64(blueScore)).AddInt64(extraNonce).Script()
AddInt64(extraNonce).Script()
if err != nil {
return nil, err
}
@ -245,39 +245,55 @@ func (p *poolHarness) CreateTxChain(firstOutput spendableOutpoint, numTxns uint3
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))
for i, tx := range transactions {
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)
if err != nil {
tc.t.Fatalf("PrepareBlockForTest: %s", err)
}
utilBlock := util.NewBlock(block)
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())
}
for i := uint64(0); i < numberOfBlocks; i++ {
var blockTxs []*wire.MsgTx
if i == 0 {
blockTxs = msgTxs
}
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 {
tc.t.Fatalf("PrepareBlockForTest: %s", err)
}
utilBlock := util.NewBlock(block)
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
ch := make(chan NewBlockMsg)
go func() {
err = tc.harness.txPool.HandleNewBlock(utilBlock, ch)
close(ch)
}()
// Handle new block by pool
ch := make(chan NewBlockMsg)
go func() {
err = tc.harness.txPool.HandleNewBlock(utilBlock, ch)
close(ch)
}()
// process messages pushed by HandleNewBlock
for range ch {
// process messages pushed by HandleNewBlock
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
if err != nil {
tc.t.Fatalf("HandleNewBlock failed to handle block %s", err)
}
return utilBlock
return outpoints
}
// 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.BlockRewardMaturity = 0
params.BlockCoinbaseMaturity = 0
// Create a new database and chain instance to run tests against.
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}
block := tc.mineTransactions(nil, uint64(numOutputs))
coinbase := block.Transactions()[0]
// Create a single coinbase transaction and add it to the harness
// chain's utxo set and set the harness chain height such that the
// 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)
// Mine numOutputs blocks to get numOutputs coinbase outpoints
outpoints := tc.mineTransactions(nil, uint64(numOutputs))
curHeight := harness.chain.BestHeight()
for i := uint32(0); i < numOutputs; i++ {
outpoints = append(outpoints, txOutToSpendableOutpoint(coinbase, i))
}
if dagParams.BlockRewardMaturity != 0 {
harness.chain.SetHeight(dagParams.BlockRewardMaturity + curHeight)
if params.BlockCoinbaseMaturity != 0 {
harness.chain.SetHeight(params.BlockCoinbaseMaturity + curHeight)
} else {
harness.chain.SetHeight(curHeight + 1)
}
@ -472,7 +479,7 @@ func (p *poolHarness) createTx(outpoint spendableOutpoint, fee uint64, numOutput
func TestProcessTransaction(t *testing.T) {
params := dagconfig.SimNetParams
params.BlockRewardMaturity = 0
params.BlockCoinbaseMaturity = 0
tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &params, 6, "TestProcessTransaction")
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
@ -716,7 +723,7 @@ func TestProcessTransaction(t *testing.T) {
harness.txPool.cfg.CalcSequenceLockNoLock = calcSequenceLock
//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 {
t.Fatalf("unable to create transaction: %v", err)
}
@ -791,7 +798,7 @@ func TestAddrIndex(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 {
t.Fatalf("unable to create test pool: %v", err)
}
@ -803,13 +810,19 @@ func TestDoubleSpends(t *testing.T) {
if err != nil {
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)
if err != nil {
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, tx2, false, true, false)
@ -1761,60 +1774,44 @@ var dummyBlock = wire.MsgBlock{
TxIn: []*wire.TxIn{
{
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,
},
SignatureScript: []byte{
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
},
Sequence: math.MaxUint64,
SignatureScript: nil,
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x12a05f200, // 5000000000
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
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
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,
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutpoint: wire.Outpoint{
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.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,
},
SubnetworkID: *subnetworkid.SubnetworkIDNative,
},
},
}
func TestTransactionGas(t *testing.T) {
params := dagconfig.SimNetParams
params.BlockRewardMaturity = 1
params.BlockCoinbaseMaturity = 1
tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &params, 6, "TestTransactionGas")
if err != nil {
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.
coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0]
log.Infof("Block submitted via CPU miner accepted (hash %s, "+
"amount %d)", block.Hash(), util.Amount(coinbaseTx.Value))
log.Infof("Block submitted via CPU miner accepted (hash %s)", block.Hash())
return true
}

View File

@ -5,7 +5,9 @@
package mining
import (
"bytes"
"container/heap"
"encoding/binary"
"fmt"
"sort"
"time"
@ -180,63 +182,9 @@ type BlockTemplate struct {
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
// on the end of the provided best chain. In particular, it is one second after
// the median timestamp of the last several blocks per the chain consensus
// on the end of the DAG. In particular, it is one second after
// the median timestamp of the last several blocks per the DAG consensus
// rules.
func MinimumMedianTime(dagMedianTime time.Time) time.Time {
return dagMedianTime.Add(time.Second)
@ -361,36 +309,27 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
g.dag.RLock()
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()
coinbasePayloadPkScript, err := txscript.PayToAddrScript(payToAddress)
if err != nil {
return nil, err
}
extraNonce, err := random.Uint64()
if err != nil {
return nil, err
}
coinbaseScript, err := StandardCoinbaseScript(nextBlockBlueScore, extraNonce)
coinbasePayloadExtraData, err := CoinbasePayloadExtraData(extraNonce)
if err != nil {
return nil, err
}
coinbaseTx, err := CreateCoinbaseTx(g.dagParams, coinbaseScript, nextBlockBlueScore, payToAddress)
coinbaseTx, err := g.dag.NextBlockCoinbaseTransactionNoLock(coinbasePayloadPkScript, coinbasePayloadExtraData)
if err != nil {
return nil, err
}
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
// hold the transactions which are ready for inclusion into a block
// 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
// house all of the input transactions so multiple lookups can be
// avoided.
blockTxns := make([]*util.Tx, 0, len(sourceTxns)+2)
blockTxns = append(blockTxns, coinbaseTx, feeTransaction)
blockTxns := make([]*util.Tx, 0, len(sourceTxns)+1)
blockTxns = append(blockTxns, coinbaseTx)
// The starting block size is the size of the block header plus the max
// possible transaction count size, plus the size of the coinbase
// transaction.
blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize())
blockSigOps := numCoinbaseSigOps + feeTxSigOps
blockSigOps := numCoinbaseSigOps
totalFees := uint64(0)
// 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.
// However, since the total fees aren't known yet, use a dummy value for
// the coinbase fee which will be updated later.
txFees := make([]uint64, 0, len(sourceTxns)+2)
txSigOpCounts := make([]int64, 0, len(sourceTxns)+2)
txFees = append(txFees, 0, 0) // For coinbase and fee txs
txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps, feeTxSigOps)
txFees := make([]uint64, 0, len(sourceTxns)+1)
txSigOpCounts := make([]int64, 0, len(sourceTxns)+1)
txFees = append(txFees, 0) // For coinbase tx
txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps)
log.Debugf("Considering %d transactions for inclusion to new block",
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
// non-finalized transactions.
tx := txDesc.Tx
if blockdag.IsBlockReward(tx) {
log.Tracef("Skipping block reward tx %s", tx.ID())
if tx.IsCoinBase() {
log.Tracef("Skipping coinbase tx %s", tx.ID())
continue
}
if !blockdag.IsFinalizedTransaction(tx, nextBlockBlueScore,
@ -464,7 +403,7 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
prioItem := heap.Pop(priorityQueue).(*txPrioItem)
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
gasUsage, ok := gasUsageMap[subnetworkID]
if !ok {
@ -574,6 +513,12 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
// Sort transactions by subnetwork ID before building Merkle tree
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)
})
@ -623,6 +568,23 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
}, 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) {
utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, nextBlueScore, false)
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
// from changing the coinbase script.
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 {
return err
}
if len(coinbaseScript) > blockdag.MaxCoinbaseScriptLen {
return fmt.Errorf("coinbase transaction script length "+
"of %d is out of range (min: %d, max: %d)",
len(coinbaseScript), blockdag.MinCoinbaseScriptLen,
blockdag.MaxCoinbaseScriptLen)
coinbasePayloadExtraData, err := CoinbasePayloadExtraData(extraNonce)
if err != nil {
return err
}
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
// 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.
block := util.NewBlock(msgBlock)

View File

@ -73,7 +73,7 @@ func TestTxFeePrioHeap(t *testing.T) {
func TestNewBlockTemplate(t *testing.T) {
params := dagconfig.SimNetParams
params.BlockRewardMaturity = 0
params.BlockCoinbaseMaturity = 0
dag, teardownFunc, err := blockdag.DAGSetup("TestNewBlockTemplate", blockdag.Config{
DAGParams: &params,
@ -97,30 +97,14 @@ func TestNewBlockTemplate(t *testing.T) {
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,
&params, txSource, dag, blockdag.NewMedianTime(), txscript.NewSigCache(100000))
template1, err := blockTemplateGenerator.NewBlockTemplate(nil)
createCoinbaseTxPatch.Unpatch()
OpTrueAddr, err := OpTrueAddress(params.Prefix)
if err != nil {
t.Fatalf("OpTrueAddress: %s", err)
}
template1, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
if err != nil {
t.Fatalf("NewBlockTemplate: %v", err)
}
@ -129,24 +113,33 @@ func TestNewBlockTemplate(t *testing.T) {
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if isOrphan {
t.Fatalf("ProcessBlock: template1 got unexpectedly orphan")
}
cbScript, err := StandardCoinbaseScript(dag.VirtualBlueScore(), 0)
if err != nil {
t.Fatalf("standardCoinbaseScript: %v", err)
// We create another 4 blocks to in order to create more funds for tests.
cbTxs := []*wire.MsgTx{template1.Block.Transactions[util.CoinbaseTransactionIndex]}
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
cbTx, err := CreateCoinbaseTx(&params, cbScript, dag.VirtualBlueScore(), nil)
cbTx, err := dag.NextBlockCoinbaseTransaction(nil, nil)
if err != nil {
t.Fatalf("createCoinbaseTx: %v", err)
}
template1CbTx := template1.Block.Transactions[0]
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
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
txIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(),
TxID: *cbTxs[0].TxID(),
Index: 0,
},
Sequence: wire.MaxTxInSequenceNum,
@ -170,8 +163,8 @@ func TestNewBlockTemplate(t *testing.T) {
// We want to check that the miner filters non finalized transactions
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(),
Index: 1,
TxID: *cbTxs[1].TxID(),
Index: 0,
},
Sequence: 0,
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)
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(),
Index: 2,
TxID: *cbTxs[2].TxID(),
Index: 0,
},
Sequence: 0,
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
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(),
Index: 3,
TxID: *cbTxs[3].TxID(),
Index: 0,
},
Sequence: 0,
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)
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *template1CbTx.TxID(),
Index: 4,
TxID: *cbTxs[4].TxID(),
},
Sequence: 0,
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.
popReturnedUnexpectedValue := false
expectedPops := map[daghash.TxID]bool{
@ -306,7 +279,7 @@ func TestNewBlockTemplate(t *testing.T) {
})
defer gasLimitPatch.Unpatch()
template2, err := blockTemplateGenerator.NewBlockTemplate(nil)
template3, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
popPatch.Unpatch()
gasLimitPatch.Unpatch()
@ -329,17 +302,17 @@ func TestNewBlockTemplate(t *testing.T) {
*subnetworkTx1.TxID(): false,
}
for _, tx := range template2.Block.Transactions[2:] {
for _, tx := range template3.Block.Transactions[util.CoinbaseTransactionIndex+1:] {
id := *tx.TxID()
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
}
for id, exists := range expectedTxs {
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
}
// PrepareBlockForTest generates a block with the proper merkle roots, coinbase/fee transactions 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) {
// 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) (*wire.MsgBlock, error) {
newVirtual, err := blockdag.GetVirtualFromParentsForTest(dag, parentHashes)
if err != nil {
return nil, err
@ -62,7 +62,12 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
blockTemplateGenerator := NewBlkTmplGenerator(&policy,
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 {
return nil, err
}
@ -89,27 +94,12 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
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 {
for _, tx := range txsToAdd {
template.Block.Transactions = append(template.Block.Transactions, tx)
}
}
updateHeaderFields := coinbaseOutputs != 1 || (forceTransactions && len(txsToAdd) > 0)
updateHeaderFields := forceTransactions && len(txsToAdd) > 0
if updateHeaderFields {
utilTxs := make([]*util.Tx, len(template.Block.Transactions))
for i, tx := range template.Block.Transactions {
@ -131,4 +121,8 @@ func GenerateDeterministicExtraNonceForTest() uint64 {
return extraNonceForTest
}
func OpTrueAddress(prefix util.Bech32Prefix) (util.Address, error) {
return util.NewAddressScriptHash(blockdag.OpTrueScript, prefix)
}
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"
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
// Update the merkle root.
@ -1796,7 +1796,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
if useCoinbaseValue {
reply.CoinbaseAux = gbtCoinbaseAux
reply.CoinbaseValue = &msgBlock.Transactions[0].TxOut[0].Value
reply.CoinbaseValue = &msgBlock.Transactions[util.CoinbaseTransactionIndex].TxOut[0].Value
} else {
// Ensure the template has a valid payment address associated
// 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.
tx := msgBlock.Transactions[0]
tx := msgBlock.Transactions[util.CoinbaseTransactionIndex]
txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(txBuf); err != nil {
context := "Failed to serialize transaction"
@ -2082,14 +2082,8 @@ func chainErrToGBTErrString(err error) string {
return "bad-txns-nocoinbase"
case blockdag.ErrMultipleCoinbases:
return "bad-txns-multicoinbase"
case blockdag.ErrBadCoinbaseScriptLen:
case blockdag.ErrBadCoinbasePayloadLen:
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:
return "bad-script-malformed"
case blockdag.ErrScriptValidation:
@ -2746,7 +2740,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
bestBlockHash = s.cfg.DAG.SelectedTipHash().String()
value = entry.Amount()
pkScript = entry.PkScript()
isCoinbase = entry.IsBlockReward()
isCoinbase = entry.IsCoinbase()
}
// 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
// passed transaction.
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.
vinList := make([]btcjson.VinPrevOut, 0, len(mtx.TxIn))
// 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.
var originOutputs map[wire.Outpoint]wire.TxOut
if !mtx.IsFeeTransaction() && (vinExtra || len(filterAddrMap) > 0) {
if !mtx.IsCoinBase() && (vinExtra || len(filterAddrMap) > 0) {
var err error
originOutputs, err = fetchInputTxos(s, mtx)
if err != nil {

View File

@ -26,10 +26,6 @@ const (
// CoinbaseTransactionIndex is the index of the coinbase transaction in every block
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.
@ -214,16 +210,6 @@ func (b *Block) CoinbaseTransaction() *Tx {
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
func (b *Block) Timestamp() time.Time {
return b.msgBlock.Header.Timestamp

View File

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

View File

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

View File

@ -10,7 +10,6 @@ import (
"fmt"
"math/big"
"sort"
"strings"
)
// 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 = &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 = &SubnetworkID{1}
SubnetworkIDRegistry = &SubnetworkID{2}
)
// 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))
}
// 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
func Less(a *SubnetworkID, b *SubnetworkID) bool {
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
func Sort(ids []SubnetworkID) {
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
func RegisterSubnetworkForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, gasLimit uint64) (*subnetworkid.SubnetworkID, 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 {
return nil, err
}

View File

@ -75,6 +75,15 @@ func (t *Tx) SetIndex(index int) {
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
// wire.MsgTx. See 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)
}
// 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 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
// IsCoinBase determines whether or not a transaction is a coinbase transaction. A coinbase
// transaction is a special transaction created by miners that distributes fees and block subsidy
// to the previous blocks' miners, and to specify the pkScript that will be used to pay the current
// miner in future blocks. Each input of the coinbase transaction should set index to maximum
// value and reference the relevant block id, instead of previous transaction id.
func (msg *MsgTx) IsFeeTransaction() bool {
for _, txIn := range msg.TxIn {
// The previous output of a fee transaction have a max value index and
// 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()
func (msg *MsgTx) IsCoinBase() bool {
// A coinbase transaction must have subnetwork id SubnetworkIDCoinbase
return msg.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDCoinbase)
}
// 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.SubnetworkIDRegistry) && msg.Gas != 0 {
str := "Transactions from registry subnetwork should have 0 gas"
if msg.SubnetworkID.IsBuiltIn() && msg.Gas != 0 {
str := "Transactions from built-in should have 0 gas"
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.
func TestTxHashAndID(t *testing.T) {
txID1Str := "5b92e6ed52bc78745905e0d104069e46407f62ea8d7d2bce78cd13f80ce220dc"
txID1Str := "edca872f27279674c7a52192b32fd68b8b8be714bfea52d98b2c3c86c30e85c6"
wantTxID1, err := daghash.NewTxIDFromStr(txID1Str)
if err != nil {
t.Errorf("NewTxIDFromStr: %v", err)
return
}
// First transaction from block 113875.
// A coinbase transaction
txIn := &TxIn{
PreviousOutpoint: Outpoint{
TxID: daghash.TxID{},
Index: 0xffffffff,
Index: math.MaxUint32,
},
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
Sequence: math.MaxUint64,
@ -161,7 +161,7 @@ func TestTxHashAndID(t *testing.T) {
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.
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")
}
tx2.Payload = []byte{}
tx2.TxIn[0].SignatureScript = []byte{}
newTx2Hash := tx2.TxHash()
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 output transactions
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, // Sub Network ID
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()))
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)
if err == nil || err.Error() != expectedErr.Error() {
t.Errorf("TestTxSerializeErrors: expected error %v but got %v", expectedErr, err)