[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:
stasatdaglabs 2019-01-22 13:56:40 +02:00 committed by Svarog
parent 7e739a6430
commit 4be23bff07
20 changed files with 191 additions and 94 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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: &params,
DAGParams: &params,
SubnetworkID: &wire.SubnetworkIDSupportsAll,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)

View File

@ -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)

View File

@ -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.

View File

@ -38,6 +38,7 @@ func TestTxIndexConnectBlock(t *testing.T) {
config := blockdag.Config{
IndexManager: indexManager,
DAGParams: &params,
SubnetworkID: &wire.SubnetworkIDSupportsAll,
}
dag, teardown, err := blockdag.DAGSetup("TestTxIndexConnectBlock", config)

View File

@ -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)

View File

@ -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
}

View File

@ -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: &params,
DAGParams: &params,
SubnetworkID: &wire.SubnetworkIDSupportsAll,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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",

View File

@ -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

View File

@ -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)