mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[DEV-348] Don't send transactions of wrong sub-network to partial peers, and reject messages of such (#164)
* [DEV-348] Removed a couple of unused methods. * [DEV-348] Implemented validating incoming transactions for bad partial transactions. * [DEV-348] Added a (incomplete) filter for propogation of transactions. * [DEV-348] Implemented filtering inventory by subnetwork. * [DEV-348] Fixed broken tests. * [DEV-348] Added test for non-zero payload partial transactions. * [DEV-348] Added a comment for Config.SubnetworkID. * [DEV-348] Fixed formatting. * [DEV-348] Renamed isRemoteTransactionFull to shouldTxBeFull. * [DEV-348] Added a check for invalid transaction in maybeAcceptTransaction. Added handling for native networks. * [DEV-348] Fixed formatting. * [DEV-348] Fixed a bug in transaction validation. * [DEV-348] Rephrased a comment. * [DEV-348] Extracted subnetwork compatibility to a method. Wrote a test for it. * [DEV-348] Removed an unnecessary check over the native subnetwork.
This commit is contained in:
parent
7e739a6430
commit
4be23bff07
@ -2,6 +2,7 @@ package blockdag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -14,7 +15,8 @@ import (
|
||||
func TestMaybeAcceptBlockErrors(t *testing.T) {
|
||||
// Create a new database and DAG instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
|
||||
|
@ -2,6 +2,7 @@ package blockdag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -23,7 +24,8 @@ func TestAncestorErrors(t *testing.T) {
|
||||
func TestFlushToDBErrors(t *testing.T) {
|
||||
// Create a new database and DAG instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("TestFlushToDBErrors", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFlushToDBErrors: Failed to setup DAG instance: %s", err)
|
||||
|
@ -7,6 +7,7 @@ package blockdag
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/daglabs/btcd/util/subnetworkid"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
@ -96,6 +97,9 @@ type BlockDAG struct {
|
||||
// virtual tracks the current tips.
|
||||
virtual *virtualBlock
|
||||
|
||||
// subnetworkID holds the subnetwork ID of the DAG
|
||||
subnetworkID *subnetworkid.SubnetworkID
|
||||
|
||||
// These fields are related to handling of orphan blocks. They are
|
||||
// protected by a combination of the chain lock and the orphan lock.
|
||||
orphanLock sync.RWMutex
|
||||
@ -1339,6 +1343,10 @@ func (dag *BlockDAG) LocateHeaders(locator BlockLocator, hashStop *daghash.Hash)
|
||||
return headers
|
||||
}
|
||||
|
||||
func (dag *BlockDAG) SubnetworkID() *subnetworkid.SubnetworkID {
|
||||
return dag.subnetworkID
|
||||
}
|
||||
|
||||
// IndexManager provides a generic interface that is called when blocks are
|
||||
// connected and disconnected to and from the tip of the main chain for the
|
||||
// purpose of supporting optional indexes.
|
||||
@ -1370,7 +1378,7 @@ type Config struct {
|
||||
// This field can be nil if the caller does not desire the behavior.
|
||||
Interrupt <-chan struct{}
|
||||
|
||||
// DAGParams identifies which chain parameters the chain is associated
|
||||
// DAGParams identifies which DAG parameters the DAG is associated
|
||||
// with.
|
||||
//
|
||||
// This field is required.
|
||||
@ -1407,19 +1415,28 @@ type Config struct {
|
||||
// This field can be nil if the caller does not wish to make use of an
|
||||
// index manager.
|
||||
IndexManager IndexManager
|
||||
|
||||
// SubnetworkID identifies which subnetwork the DAG is associated
|
||||
// with.
|
||||
//
|
||||
// This field is required.
|
||||
SubnetworkID *subnetworkid.SubnetworkID
|
||||
}
|
||||
|
||||
// New returns a BlockDAG instance using the provided configuration details.
|
||||
func New(config *Config) (*BlockDAG, error) {
|
||||
// Enforce required config fields.
|
||||
if config.DB == nil {
|
||||
return nil, AssertError("blockchain.New database is nil")
|
||||
return nil, AssertError("BlockDAG.New database is nil")
|
||||
}
|
||||
if config.DAGParams == nil {
|
||||
return nil, AssertError("blockchain.New chain parameters nil")
|
||||
return nil, AssertError("BlockDAG.New DAG parameters nil")
|
||||
}
|
||||
if config.TimeSource == nil {
|
||||
return nil, AssertError("blockchain.New timesource is nil")
|
||||
return nil, AssertError("BlockDAG.New timesource is nil")
|
||||
}
|
||||
if config.SubnetworkID == nil {
|
||||
return nil, AssertError("BlockDAG.New subnetworkID is nil")
|
||||
}
|
||||
|
||||
// Generate a checkpoint by height map from the provided checkpoints
|
||||
@ -1463,6 +1480,7 @@ func New(config *Config) (*BlockDAG, error) {
|
||||
warningCaches: newThresholdCaches(vbNumBits),
|
||||
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
|
||||
blockCount: 1,
|
||||
subnetworkID: config.SubnetworkID,
|
||||
}
|
||||
|
||||
// Initialize the chain state from the passed database. When the db
|
||||
|
@ -42,7 +42,8 @@ func TestBlockCount(t *testing.T) {
|
||||
|
||||
// Create a new database and DAG instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("haveblock", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||
@ -91,7 +92,8 @@ func TestHaveBlock(t *testing.T) {
|
||||
|
||||
// Create a new database and chain instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("haveblock", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||
@ -819,7 +821,8 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
|
||||
|
||||
// Create a new database and dag instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("testErrorThroughPatching", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup dag instance: %v", err)
|
||||
@ -876,7 +879,8 @@ func TestFinality(t *testing.T) {
|
||||
params := dagconfig.SimNetParams
|
||||
params.K = 1
|
||||
dag, teardownFunc, err := DAGSetup("TestFinality", Config{
|
||||
DAGParams: ¶ms,
|
||||
DAGParams: ¶ms,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||
|
@ -6,6 +6,7 @@ package blockdag_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -46,9 +47,10 @@ func ExampleBlockDAG_ProcessBlock() {
|
||||
// values obtained from other peers on the network so the local time is
|
||||
// adjusted to be in agreement with other peers.
|
||||
chain, err := blockdag.New(&blockdag.Config{
|
||||
DB: db,
|
||||
DAGParams: &dagconfig.MainNetParams,
|
||||
TimeSource: blockdag.NewMedianTime(),
|
||||
DB: db,
|
||||
DAGParams: &dagconfig.MainNetParams,
|
||||
TimeSource: blockdag.NewMedianTime(),
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create chain instance: %v\n", err)
|
||||
|
@ -5,15 +5,9 @@
|
||||
package indexers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"bytes"
|
||||
|
||||
"github.com/daglabs/btcd/blockdag"
|
||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||
"github.com/daglabs/btcd/database"
|
||||
"github.com/daglabs/btcd/util"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -169,44 +163,6 @@ func (m *Manager) Init(db database.DB, blockDAG *blockdag.BlockDAG, interrupt <-
|
||||
return nil
|
||||
}
|
||||
|
||||
// indexNeedsInputs returns whether or not the index needs access to the txouts
|
||||
// referenced by the transaction inputs being indexed.
|
||||
func indexNeedsInputs(index Indexer) bool {
|
||||
if idx, ok := index.(NeedsInputser); ok {
|
||||
return idx.NeedsInputs()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// dbFetchTx looks up the passed transaction hash in the transaction index and
|
||||
// loads it from the database.
|
||||
func dbFetchTx(dbTx database.Tx, hash *daghash.Hash) (*wire.MsgTx, error) {
|
||||
// Look up the location of the transaction.
|
||||
blockRegion, err := dbFetchFirstTxRegion(dbTx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockRegion == nil {
|
||||
return nil, fmt.Errorf("transaction %v not found", hash)
|
||||
}
|
||||
|
||||
// Load the raw transaction bytes from the database.
|
||||
txBytes, err := dbTx.FetchBlockRegion(blockRegion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Deserialize the transaction.
|
||||
var msgTx wire.MsgTx
|
||||
err = msgTx.Deserialize(bytes.NewReader(txBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &msgTx, nil
|
||||
}
|
||||
|
||||
// ConnectBlock must be invoked when a block is extending the main chain. It
|
||||
// keeps track of the state of each index it is managing, performs some sanity
|
||||
// checks, and invokes each indexer.
|
||||
|
@ -38,6 +38,7 @@ func TestTxIndexConnectBlock(t *testing.T) {
|
||||
config := blockdag.Config{
|
||||
IndexManager: indexManager,
|
||||
DAGParams: ¶ms,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
}
|
||||
|
||||
dag, teardown, err := blockdag.DAGSetup("TestTxIndexConnectBlock", config)
|
||||
|
@ -5,6 +5,7 @@
|
||||
package blockdag
|
||||
|
||||
import (
|
||||
"github.com/daglabs/btcd/wire"
|
||||
"testing"
|
||||
|
||||
"github.com/daglabs/btcd/dagconfig"
|
||||
@ -19,7 +20,8 @@ func TestNotifications(t *testing.T) {
|
||||
|
||||
// Create a new database and dag instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("notifications", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup dag instance: %v", err)
|
||||
|
@ -164,7 +164,7 @@ func (dag *BlockDAG) ProcessBlock(block *util.Block, flags BehaviorFlags) (bool,
|
||||
}
|
||||
|
||||
// Perform preliminary sanity checks on the block and its transactions.
|
||||
err = checkBlockSanity(block, dag.dagParams.PowLimit, dag.timeSource, flags)
|
||||
err = checkBlockSanity(block, dag.dagParams.PowLimit, dag.timeSource, dag.subnetworkID, flags)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package blockdag
|
||||
|
||||
import (
|
||||
"github.com/daglabs/btcd/wire"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@ -12,7 +13,8 @@ func TestSubnetworkRegistry(t *testing.T) {
|
||||
params := dagconfig.SimNetParams
|
||||
params.K = 1
|
||||
dag, teardownFunc, err := DAGSetup("TestSubnetworkRegistry", Config{
|
||||
DAGParams: ¶ms,
|
||||
DAGParams: ¶ms,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||
|
@ -799,7 +799,8 @@ func TestDiffUTXOSet_addTx(t *testing.T) {
|
||||
func TestApplyUTXOChanges(t *testing.T) {
|
||||
// Create a new database and dag instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("TestApplyUTXOChanges", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup dag instance: %v", err)
|
||||
|
@ -174,7 +174,7 @@ func CalcBlockSubsidy(height int32, 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) error {
|
||||
func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID) error {
|
||||
// A transaction must have at least one input.
|
||||
msgTx := tx.MsgTx()
|
||||
if len(msgTx.TxIn) == 0 {
|
||||
@ -286,6 +286,17 @@ func CheckTransactionSanity(tx *util.Tx) error {
|
||||
"with length != 8 bytes")
|
||||
}
|
||||
|
||||
// If we are a partial node, only transactions on the Registry subnetwork
|
||||
// or our own subnetwork may have a payload
|
||||
isLocalNodeFull := subnetworkID.IsEqual(&wire.SubnetworkIDSupportsAll)
|
||||
shouldTxBeFull := msgTx.SubnetworkID.IsEqual(&wire.SubnetworkIDRegistry) ||
|
||||
msgTx.SubnetworkID.IsEqual(subnetworkID)
|
||||
if !isLocalNodeFull && !shouldTxBeFull && len(msgTx.Payload) > 0 {
|
||||
return ruleError(ErrInvalidPayload,
|
||||
"transaction that was expected to be partial has a payload "+
|
||||
"with length > 0")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -469,7 +480,8 @@ func checkBlockParentsOrder(header *wire.BlockHeader) error {
|
||||
//
|
||||
// The flags do not modify the behavior of this function directly, however they
|
||||
// are needed to pass along to checkBlockHeaderSanity.
|
||||
func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error {
|
||||
func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTimeSource,
|
||||
subnetworkID *subnetworkid.SubnetworkID, flags BehaviorFlags) error {
|
||||
msgBlock := block.MsgBlock()
|
||||
header := &msgBlock.Header
|
||||
err := checkBlockHeaderSanity(header, powLimit, timeSource, flags)
|
||||
@ -524,7 +536,7 @@ func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTim
|
||||
// Do some preliminary checks on each transaction to ensure they are
|
||||
// sane before continuing.
|
||||
for _, tx := range transactions {
|
||||
err := CheckTransactionSanity(tx)
|
||||
err := CheckTransactionSanity(tx, subnetworkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -580,8 +592,8 @@ func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTim
|
||||
|
||||
// CheckBlockSanity performs some preliminary checks on a block to ensure it is
|
||||
// sane before continuing with block processing. These checks are context free.
|
||||
func CheckBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTimeSource) error {
|
||||
return checkBlockSanity(block, powLimit, timeSource, BFNone)
|
||||
func CheckBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTimeSource, subnetworkID *subnetworkid.SubnetworkID) error {
|
||||
return checkBlockSanity(block, powLimit, timeSource, subnetworkID, BFNone)
|
||||
}
|
||||
|
||||
// ExtractCoinbaseHeight attempts to extract the height of the block from the
|
||||
@ -1109,7 +1121,7 @@ func (dag *BlockDAG) CheckConnectBlockTemplate(block *util.Block) error {
|
||||
return ruleError(ErrParentBlockNotCurrentTips, str)
|
||||
}
|
||||
|
||||
err := checkBlockSanity(block, dag.dagParams.PowLimit, dag.timeSource, flags)
|
||||
err := checkBlockSanity(block, dag.dagParams.PowLimit, dag.timeSource, dag.subnetworkID, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -69,7 +69,8 @@ func TestSequenceLocksActive(t *testing.T) {
|
||||
func TestCheckConnectBlockTemplate(t *testing.T) {
|
||||
// Create a new database and chain instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("checkconnectblocktemplate", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup chain instance: %v", err)
|
||||
@ -157,13 +158,13 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
if len(block.Transactions()) < 3 {
|
||||
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(block.Transactions()))
|
||||
}
|
||||
err := CheckBlockSanity(block, powLimit, timeSource)
|
||||
err := CheckBlockSanity(block, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
if err != nil {
|
||||
t.Errorf("CheckBlockSanity: %v", err)
|
||||
}
|
||||
// Test with block with wrong transactions sorting order
|
||||
blockWithWrongTxOrder := util.NewBlock(&BlockWithWrongTxOrder)
|
||||
err = CheckBlockSanity(blockWithWrongTxOrder, powLimit, timeSource)
|
||||
err = CheckBlockSanity(blockWithWrongTxOrder, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
if err == nil {
|
||||
t.Errorf("CheckBlockSanity: transactions disorder is not detected")
|
||||
}
|
||||
@ -178,7 +179,7 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
// second fails.
|
||||
timestamp := block.MsgBlock().Header.Timestamp
|
||||
block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond)
|
||||
err = CheckBlockSanity(block, powLimit, timeSource)
|
||||
err = CheckBlockSanity(block, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
if err == nil {
|
||||
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
|
||||
}
|
||||
@ -442,7 +443,7 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
}
|
||||
|
||||
btcutilInvalidBlock := util.NewBlock(&invalidParentsOrderBlock)
|
||||
err = CheckBlockSanity(btcutilInvalidBlock, powLimit, timeSource)
|
||||
err = CheckBlockSanity(btcutilInvalidBlock, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
if err == nil {
|
||||
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
|
||||
}
|
||||
@ -1152,41 +1153,65 @@ func TestCheckTransactionSanity(t *testing.T) {
|
||||
numInputs uint32
|
||||
numOutputs uint32
|
||||
outputValue uint64
|
||||
subnetworkData *txSubnetworkData
|
||||
nodeSubnetworkID subnetworkid.SubnetworkID
|
||||
txSubnetworkData *txSubnetworkData
|
||||
extraModificationsFunc func(*wire.MsgTx)
|
||||
expectedErr error
|
||||
}{
|
||||
{"good one", 1, 1, 1, nil, nil, nil},
|
||||
{"no inputs", 0, 1, 1, nil, nil, ruleError(ErrNoTxInputs, "")},
|
||||
{"no outputs", 1, 0, 1, nil, nil, ruleError(ErrNoTxOutputs, "")},
|
||||
{"too big", 100000, 1, 1, nil, nil, ruleError(ErrTxTooBig, "")},
|
||||
{"too much satoshi in one output", 1, 1, util.MaxSatoshi + 1, nil, nil, ruleError(ErrBadTxOutValue, "")},
|
||||
{"too much satoshi in total outputs", 1, 2, util.MaxSatoshi - 1, nil, nil, ruleError(ErrBadTxOutValue, "")},
|
||||
{"duplicate inputs", 2, 1, 1, nil,
|
||||
{"good one", 1, 1, 1, wire.SubnetworkIDNative, nil, nil, nil},
|
||||
{"no inputs", 0, 1, 1, wire.SubnetworkIDNative, nil, nil, ruleError(ErrNoTxInputs, "")},
|
||||
{"no outputs", 1, 0, 1, wire.SubnetworkIDNative, nil, nil, ruleError(ErrNoTxOutputs, "")},
|
||||
{"too big", 100000, 1, 1, wire.SubnetworkIDNative, nil, nil, ruleError(ErrTxTooBig, "")},
|
||||
{"too much satoshi in one output", 1, 1, util.MaxSatoshi + 1,
|
||||
wire.SubnetworkIDNative,
|
||||
nil,
|
||||
nil,
|
||||
ruleError(ErrBadTxOutValue, "")},
|
||||
{"too much satoshi in total outputs", 1, 2, util.MaxSatoshi - 1,
|
||||
wire.SubnetworkIDNative,
|
||||
nil,
|
||||
nil,
|
||||
ruleError(ErrBadTxOutValue, "")},
|
||||
{"duplicate inputs", 2, 1, 1,
|
||||
wire.SubnetworkIDNative,
|
||||
nil,
|
||||
func(tx *wire.MsgTx) { tx.TxIn[1].PreviousOutPoint.Index = 0 },
|
||||
ruleError(ErrDuplicateTxInputs, "")},
|
||||
{"non-zero gas in native subnetwork", 1, 1, 0,
|
||||
{"non-zero gas in DAGCoin", 1, 1, 0,
|
||||
wire.SubnetworkIDNative,
|
||||
&txSubnetworkData{wire.SubnetworkIDNative, 1, []byte{}},
|
||||
nil, ruleError(ErrInvalidGas, "")},
|
||||
nil,
|
||||
ruleError(ErrInvalidGas, "")},
|
||||
{"non-zero gas in subnetwork registry", 1, 1, 0,
|
||||
&txSubnetworkData{wire.SubnetworkIDRegistry, 1, []byte{}},
|
||||
nil, ruleError(ErrInvalidGas, "")},
|
||||
{"non-zero payload in native subnetwork", 1, 1, 0,
|
||||
wire.SubnetworkIDNative,
|
||||
&txSubnetworkData{wire.SubnetworkIDNative, 1, []byte{}},
|
||||
nil,
|
||||
ruleError(ErrInvalidGas, "")},
|
||||
{"non-zero payload in DAGCoin", 1, 1, 0,
|
||||
wire.SubnetworkIDNative,
|
||||
&txSubnetworkData{wire.SubnetworkIDNative, 0, []byte{1}},
|
||||
nil, ruleError(ErrInvalidPayload, "")},
|
||||
nil,
|
||||
ruleError(ErrInvalidPayload, "")},
|
||||
{"payload in subnetwork registry isn't 8 bytes", 1, 1, 0,
|
||||
&txSubnetworkData{wire.SubnetworkIDRegistry, 0, []byte{1, 2, 3, 4, 5, 6, 7}},
|
||||
nil, ruleError(ErrInvalidPayload, "")},
|
||||
wire.SubnetworkIDNative,
|
||||
&txSubnetworkData{wire.SubnetworkIDNative, 0, []byte{1, 2, 3, 4, 5, 6, 7}},
|
||||
nil,
|
||||
ruleError(ErrInvalidPayload, "")},
|
||||
{"payload in other subnetwork isn't 0 bytes", 1, 1, 0,
|
||||
subnetworkid.SubnetworkID{123},
|
||||
&txSubnetworkData{subnetworkid.SubnetworkID{234}, 0, []byte{1}},
|
||||
nil,
|
||||
ruleError(ErrInvalidPayload, "")},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tx := createTxForTest(test.numInputs, test.numOutputs, test.outputValue, test.subnetworkData)
|
||||
tx := createTxForTest(test.numInputs, test.numOutputs, test.outputValue, test.txSubnetworkData)
|
||||
|
||||
if test.extraModificationsFunc != nil {
|
||||
test.extraModificationsFunc(tx)
|
||||
}
|
||||
|
||||
err := CheckTransactionSanity(util.NewTx(tx))
|
||||
err := CheckTransactionSanity(util.NewTx(tx), &test.nodeSubnetworkID)
|
||||
if e := checkRuleError(err, test.expectedErr); e != nil {
|
||||
t.Errorf("TestCheckTransactionSanity: '%s': %v", test.name, e)
|
||||
continue
|
||||
|
@ -646,10 +646,17 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
|
||||
return nil, nil, txRuleError(wire.RejectDuplicate, str)
|
||||
}
|
||||
|
||||
// Don't accept the transaction if it's from an incompatible subnetwork.
|
||||
subnetworkID := mp.cfg.DAG.SubnetworkID()
|
||||
if !tx.MsgTx().IsSubnetworkCompatible(subnetworkID) {
|
||||
str := "tx %v belongs to an invalid subnetwork"
|
||||
return nil, nil, txRuleError(wire.RejectInvalid, str)
|
||||
}
|
||||
|
||||
// Perform preliminary sanity checks on the transaction. This makes
|
||||
// use of blockchain which contains the invariant rules for what
|
||||
// transactions are allowed into blocks.
|
||||
err := blockdag.CheckTransactionSanity(tx)
|
||||
err := blockdag.CheckTransactionSanity(tx, subnetworkID)
|
||||
if err != nil {
|
||||
if cerr, ok := err.(blockdag.RuleError); ok {
|
||||
return nil, nil, dagRuleError(cerr)
|
||||
|
@ -283,7 +283,8 @@ func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName strin
|
||||
|
||||
// Create a new database and chain instance to run tests against.
|
||||
dag, teardownFunc, err := blockdag.DAGSetup(dbName, blockdag.Config{
|
||||
DAGParams: &dagconfig.MainNetParams,
|
||||
DAGParams: &dagconfig.MainNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("Failed to setup DAG instance: %v", err)
|
||||
|
@ -613,6 +613,14 @@ func (p *Peer) UserAgent() string {
|
||||
return userAgent
|
||||
}
|
||||
|
||||
func (p *Peer) SubnetworkID() *subnetworkid.SubnetworkID {
|
||||
p.flagsMtx.Lock()
|
||||
subnetworkID := p.cfg.SubnetworkID
|
||||
p.flagsMtx.Unlock()
|
||||
|
||||
return subnetworkID
|
||||
}
|
||||
|
||||
// LastAnnouncedBlock returns the last announced block of the remote peer.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
|
@ -1574,6 +1574,12 @@ func (s *Server) handleRelayInvMsg(state *peerState, msg relayMsg) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Don't relay the transaction if the peer's subnetwork is
|
||||
// incompatible with it.
|
||||
if !txD.Tx.MsgTx().IsSubnetworkCompatible(sp.Peer.SubnetworkID()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Queue the inventory to be relayed with the next batch.
|
||||
|
@ -3411,8 +3411,8 @@ func verifyDAG(s *Server, level, depth int32) error {
|
||||
|
||||
// Level 1 does basic chain sanity checks.
|
||||
if level > 0 {
|
||||
err := blockdag.CheckBlockSanity(block,
|
||||
s.cfg.DAGParams.PowLimit, s.cfg.TimeSource)
|
||||
err := blockdag.CheckBlockSanity(block, s.cfg.DAGParams.PowLimit,
|
||||
s.cfg.TimeSource, config.MainConfig().SubnetworkID)
|
||||
if err != nil {
|
||||
log.Errorf("Verify is unable to validate "+
|
||||
"block at hash %v height %d: %v",
|
||||
|
@ -725,6 +725,16 @@ func (msg *MsgTx) PkScriptLocs() []int {
|
||||
return pkScriptLocs
|
||||
}
|
||||
|
||||
// IsSubnetworkCompatible return true iff subnetworkID is one or more of the following:
|
||||
// 1. The SupportsAll subnetwork (full node)
|
||||
// 2. The native subnetwork
|
||||
// 3. The transaction's subnetwork
|
||||
func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *subnetworkid.SubnetworkID) bool {
|
||||
return subnetworkID.IsEqual(&SubnetworkIDSupportsAll) ||
|
||||
subnetworkID.IsEqual(&SubnetworkIDNative) ||
|
||||
subnetworkID.IsEqual(&msg.SubnetworkID)
|
||||
}
|
||||
|
||||
// NewMsgTx returns a new bitcoin tx message that conforms to the Message
|
||||
// interface. The return instance has a default version of TxVersion and there
|
||||
// are no transaction inputs or outputs. Also, the lock time is set to zero
|
||||
|
@ -751,6 +751,44 @@ func TestTxSerializeSize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSubnetworkCompatible(t *testing.T) {
|
||||
testTx := MsgTx{SubnetworkID: subnetworkid.SubnetworkID{123}}
|
||||
tests := []struct {
|
||||
name string
|
||||
subnetworkID *subnetworkid.SubnetworkID
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "SupportsAll subnetwork",
|
||||
subnetworkID: &SubnetworkIDSupportsAll,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "Native subnetwork",
|
||||
subnetworkID: &SubnetworkIDNative,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "same subnetwork as test tx",
|
||||
subnetworkID: &subnetworkid.SubnetworkID{123},
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "other subnetwork",
|
||||
subnetworkID: &subnetworkid.SubnetworkID{234},
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := testTx.IsSubnetworkCompatible(test.subnetworkID)
|
||||
if result != test.expectedResult {
|
||||
t.Errorf("IsSubnetworkCompatible got unexpected result in test '%s': "+
|
||||
"expected: %t, want: %t", test.name, test.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScriptFreeList(t *testing.T) {
|
||||
var list scriptFreeList = make(chan []byte, freeListMaxItems)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user