mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 21:56:50 +00:00
[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:
parent
0c5f3d72bd
commit
263737b3fb
@ -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"
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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"},
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ func TestFinality(t *testing.T) {
|
||||
}
|
||||
defer teardownFunc()
|
||||
buildNodeToDag := func(parentHashes []*daghash.Hash) (*util.Block, error) {
|
||||
msgBlock, err := mining.PrepareBlockForTest(dag, ¶ms, parentHashes, nil, false, 1)
|
||||
msgBlock, err := mining.PrepareBlockForTest(dag, ¶ms, 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: ¶ms,
|
||||
})
|
||||
@ -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: ¶ms,
|
||||
@ -180,7 +180,7 @@ func TestChainedTransactions(t *testing.T) {
|
||||
}
|
||||
defer teardownFunc()
|
||||
|
||||
block1, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{params.GenesisHash}, nil, false, 1)
|
||||
block1, err := mining.PrepareBlockForTest(dag, ¶ms, []*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, ¶ms, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx, chainedTx}, true, 1)
|
||||
block2, err := mining.PrepareBlockForTest(dag, ¶ms, []*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, ¶ms, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{nonChainedTx}, false, 1)
|
||||
block3, err := mining.PrepareBlockForTest(dag, ¶ms, []*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: ¶ms,
|
||||
})
|
||||
@ -290,16 +290,21 @@ func TestGasLimit(t *testing.T) {
|
||||
t.Fatalf("could not register network: %s", err)
|
||||
}
|
||||
|
||||
fundsBlock, err := mining.PrepareBlockForTest(dag, ¶ms, 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, ¶ms, 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, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1, tx2}, true, 1)
|
||||
overLimitBlock, err := mining.PrepareBlockForTest(dag, ¶ms, 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, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1, overflowGasTx}, true, 1)
|
||||
overflowGasBlock, err := mining.PrepareBlockForTest(dag, ¶ms, 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, ¶ms, dag.TipHashes(), []*wire.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true, 1)
|
||||
nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, ¶ms, 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, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1}, true, 1)
|
||||
validBlock, err := mining.PrepareBlockForTest(dag, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1}, true)
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareBlockForTest: %v", err)
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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, ¶ms, parentHashes, transactions, false, 1)
|
||||
block, err := mining.PrepareBlockForTest(dag, ¶ms, parentHashes, transactions, false)
|
||||
if err != nil {
|
||||
t.Fatalf("TestTxIndexConnectBlock: block %v got unexpected error from PrepareBlockForTest: %v", blockName, err)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3A.dat
vendored
BIN
blockdag/testdata/blk_3A.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3B.dat
vendored
BIN
blockdag/testdata/blk_3B.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3C.dat
vendored
BIN
blockdag/testdata/blk_3C.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3D.dat
vendored
BIN
blockdag/testdata/blk_3D.dat
vendored
Binary file not shown.
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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},
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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: ®TestGenesisHash,
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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, ¶ms, 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, ¶ms, 6, "TestTransactionGas")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test pool: %v", err)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
154
mining/mining.go
154
mining/mining.go
@ -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)
|
||||
|
@ -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: ¶ms,
|
||||
@ -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,
|
||||
¶ms, 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(¶ms, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user