From d72c039a1af5b65d6be1fc1ce9d43ee72c976ffd Mon Sep 17 00:00:00 2001 From: jj1980a <40311124+jj1980a@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:47:54 +0400 Subject: [PATCH] param: txs announced only (#1467) --- docs/cli/example_config.toml | 21 ++-- docs/cli/server.md | 2 + eth/backend.go | 1 + eth/handler.go | 5 +- eth/handler_eth_test.go | 96 +++++++++++++++++++ internal/cli/server/config.go | 33 ++++--- internal/cli/server/flags.go | 7 ++ internal/cli/server/testdata/default.toml | 1 + p2p/server.go | 3 + .../templates/mainnet-v1/archive/config.toml | 1 + .../mainnet-v1/sentry/sentry/bor/config.toml | 1 + .../sentry/sentry/bor/pbss_config.toml | 1 + .../sentry/validator/bor/config.toml | 1 + .../sentry/validator/bor/pbss_config.toml | 1 + .../mainnet-v1/without-sentry/bor/config.toml | 1 + .../without-sentry/bor/pbss_config.toml | 1 + .../testnet-amoy/archive/config.toml | 1 + .../sentry/sentry/bor/config.toml | 1 + .../sentry/sentry/bor/pbss_config.toml | 1 + .../sentry/validator/bor/config.toml | 1 + .../sentry/validator/bor/pbss_config.toml | 1 + .../without-sentry/bor/config.toml | 1 + .../without-sentry/bor/pbss_config.toml | 1 + scripts/getconfig.go | 1 + 24 files changed, 159 insertions(+), 25 deletions(-) diff --git a/docs/cli/example_config.toml b/docs/cli/example_config.toml index bea6ec2492..da4fc1455d 100644 --- a/docs/cli/example_config.toml +++ b/docs/cli/example_config.toml @@ -32,16 +32,17 @@ devfakeauthor = false # Run miner without validator set authorization enable-block-tracking = false # Enables additional logging of information collected while tracking block lifecycle [p2p] - maxpeers = 50 # Maximum number of network peers (network disabled if set to 0) - maxpendpeers = 50 # Maximum number of pending connection attempts - bind = "0.0.0.0" # Network binding address - port = 30303 # Network listening port - nodiscover = false # Disables the peer discovery mechanism (manual peer addition) - nat = "any" # NAT port mapping mechanism (any|none|upnp|pmp|extip:) - netrestrict = "" # Restricts network communication to the given IP networks (CIDR masks) - nodekey = "" # P2P node key file - nodekeyhex = "" # P2P node key as hex - txarrivalwait = "500ms" # Maximum duration to wait before requesting an announced transaction + maxpeers = 50 # Maximum number of network peers (network disabled if set to 0) + maxpendpeers = 50 # Maximum number of pending connection attempts + bind = "0.0.0.0" # Network binding address + port = 30303 # Network listening port + nodiscover = false # Disables the peer discovery mechanism (manual peer addition) + nat = "any" # NAT port mapping mechanism (any|none|upnp|pmp|extip:) + netrestrict = "" # Restricts network communication to the given IP networks (CIDR masks) + nodekey = "" # P2P node key file + nodekeyhex = "" # P2P node key as hex + txarrivalwait = "500ms" # Maximum duration to wait before requesting an announced transaction + txannouncementonly = false # Whether to only announce transactions to peers [p2p.discovery] v4disc = true # Enables the V4 discovery mechanism v5disc = false # Enables the experimental RLPx V5 (Topic Discovery) mechanism diff --git a/docs/cli/server.md b/docs/cli/server.md index 8fd5c2163e..9163d67d47 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -242,6 +242,8 @@ The ```bor server``` command runs the Bor client. - ```txarrivalwait```: Maximum duration to wait for a transaction before explicitly requesting it (default: 500ms) +- ```txannouncementonly```: Whether to only announce transactions to peers (default: false) + - ```v4disc```: Enables the V4 discovery mechanism (default: true) - ```v5disc```: Enables the experimental RLPx V5 (Topic Discovery) mechanism (default: false) diff --git a/eth/backend.go b/eth/backend.go index 8853d7fcb4..6867ec637c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -306,6 +306,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { EthAPI: blockChainAPI, checker: checker, enableBlockTracking: eth.config.EnableBlockTracking, + txAnnouncementOnly: eth.p2pServer.TxAnnouncementOnly, }); err != nil { return nil, err } diff --git a/eth/handler.go b/eth/handler.go index 9707ea4c55..e91d2f03ac 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -99,6 +99,7 @@ type handlerConfig struct { RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges EthAPI *ethapi.BlockChainAPI // EthAPI to interact enableBlockTracking bool // Whether to log information collected while tracking block lifecycle + txAnnouncementOnly bool // Whether to only announce txs to peers } type handler struct { @@ -129,6 +130,7 @@ type handler struct { requiredBlocks map[uint64]common.Hash enableBlockTracking bool + txAnnouncementOnly bool // channels for fetcher, syncer, txsyncLoop quitSync chan struct{} @@ -159,6 +161,7 @@ func newHandler(config *handlerConfig) (*handler, error) { ethAPI: config.EthAPI, requiredBlocks: config.RequiredBlocks, enableBlockTracking: config.enableBlockTracking, + txAnnouncementOnly: config.txAnnouncementOnly, quitSync: make(chan struct{}), handlerDoneCh: make(chan struct{}), handlerStartCh: make(chan struct{}), @@ -633,7 +636,7 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) { case tx.Size() > txMaxBroadcastSize: largeTxs++ default: - maybeDirect = true + maybeDirect = !h.txAnnouncementOnly } // Send the transaction (if it's small enough) directly to a subset of // the peers that have not received it yet, ensuring that the flow of diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index a5affaa310..9fe3e532cc 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -454,6 +454,102 @@ func testTransactionPropagation(t *testing.T, protocol uint) { } } +// This test checks that transactions are only announced when txannouncementonly is enabled +func TestSendTransactionAnnouncementsOnly67(t *testing.T) { + testSendTransactionAnnouncementsOnly(t, eth.ETH67) +} +func TestSendTransactionAnnouncementsOnly68(t *testing.T) { + testSendTransactionAnnouncementsOnly(t, eth.ETH68) +} + +func testSendTransactionAnnouncementsOnly(t *testing.T, protocol uint) { + t.Parallel() + + // Create a source handler that has txannouncementonly enabled + source := newTestHandler() + source.handler.txAnnouncementOnly = true + defer source.close() + + sink := newTestHandler() + defer sink.close() + + sourcePipe, sinkPipe := p2p.MsgPipe() + defer sourcePipe.Close() + defer sinkPipe.Close() + + sourcePeer := eth.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, sourcePipe), sourcePipe, source.txpool) + sinkPeer := eth.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, sinkPipe), sinkPipe, sink.txpool) + defer sourcePeer.Close() + defer sinkPeer.Close() + + go source.handler.runEthPeer(sourcePeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(source.handler), peer) + }) + + // Run the handshake locally + var ( + genesis = source.chain.Genesis() + head = source.chain.CurrentBlock() + td = source.chain.GetTd(head.Hash(), head.Number.Uint64()) + ) + if err := sinkPeer.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(source.chain), forkid.NewFilter(source.chain)); err != nil { + t.Fatalf("failed to run protocol handshake: %v", err) + } + + // Subscribe to all inbound network events on the sink peer + backend := new(testEthHandler) + + anns := make(chan []common.Hash) + annSub := backend.txAnnounces.Subscribe(anns) + defer annSub.Unsubscribe() + + bcasts := make(chan []*types.Transaction) + bcastSub := backend.txBroadcasts.Subscribe(bcasts) + defer bcastSub.Unsubscribe() + + go eth.Handle(backend, sinkPeer) + + // Fill the source pool with transactions and wait for them at the sink + txs := make([]*types.Transaction, 1024) + for nonce := range txs { + tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + txs[nonce] = tx + } + source.txpool.Add(txs, false, false) + + // Make sure we get all the transactions as announcements + seen := make(map[common.Hash]struct{}) + timeout := false + for len(seen) < len(txs) && !timeout { + switch protocol { + case 67, 68: + select { + case hashes := <-anns: + for _, hash := range hashes { + if _, ok := seen[hash]; ok { + t.Errorf("duplicate transaction announced: %x", hash) + } + seen[hash] = struct{}{} + } + case <-bcasts: + t.Errorf("received tx broadcast when txannouncementonly is true") + case <-time.After(5 * time.Second): + t.Errorf("transaction propagation timed out") + timeout = true + } + + default: + panic("unsupported protocol, please extend test") + } + } + for _, tx := range txs { + if _, ok := seen[tx.Hash()]; !ok { + t.Errorf("missing transaction: %x", tx.Hash()) + } + } +} + // Tests that blocks are broadcast to a sqrt number of peers only. func TestBroadcastBlock1Peer(t *testing.T) { testBroadcastBlock(t, 1, 1) } func TestBroadcastBlock2Peers(t *testing.T) { testBroadcastBlock(t, 2, 1) } diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 9d781ed368..676af0db56 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -222,6 +222,9 @@ type P2PConfig struct { // an announced transaction to arrive before explicitly requesting it TxArrivalWait time.Duration `hcl:"-,optional" toml:"-"` TxArrivalWaitRaw string `hcl:"txarrivalwait,optional" toml:"txarrivalwait,optional"` + + // TxAnnouncementOnly is used to only announce transactions to peers + TxAnnouncementOnly bool `hcl:"txannouncementonly,optional" toml:"txannouncementonly,optional"` } type P2PDiscovery struct { @@ -625,14 +628,15 @@ func DefaultConfig() *Config { RPCBatchLimit: 100, RPCReturnDataLimit: 100000, P2P: &P2PConfig{ - MaxPeers: 50, - MaxPendPeers: 50, - Bind: "0.0.0.0", - Port: 30303, - NoDiscover: false, - NAT: "any", - NetRestrict: "", - TxArrivalWait: 500 * time.Millisecond, + MaxPeers: 50, + MaxPendPeers: 50, + Bind: "0.0.0.0", + Port: 30303, + NoDiscover: false, + NAT: "any", + NetRestrict: "", + TxArrivalWait: 500 * time.Millisecond, + TxAnnouncementOnly: false, Discovery: &P2PDiscovery{ DiscoveryV4: true, V5Enabled: false, @@ -1343,12 +1347,13 @@ func (c *Config) buildNode() (*node.Config, error) { AllowUnprotectedTxs: c.JsonRPC.AllowUnprotectedTxs, EnablePersonal: c.JsonRPC.EnablePersonal, P2P: p2p.Config{ - MaxPeers: int(c.P2P.MaxPeers), - MaxPendingPeers: int(c.P2P.MaxPendPeers), - ListenAddr: c.P2P.Bind + ":" + strconv.Itoa(int(c.P2P.Port)), - DiscoveryV4: c.P2P.Discovery.DiscoveryV4, - DiscoveryV5: c.P2P.Discovery.V5Enabled, - TxArrivalWait: c.P2P.TxArrivalWait, + MaxPeers: int(c.P2P.MaxPeers), + MaxPendingPeers: int(c.P2P.MaxPendPeers), + ListenAddr: c.P2P.Bind + ":" + strconv.Itoa(int(c.P2P.Port)), + DiscoveryV4: c.P2P.Discovery.DiscoveryV4, + DiscoveryV5: c.P2P.Discovery.V5Enabled, + TxArrivalWait: c.P2P.TxArrivalWait, + TxAnnouncementOnly: c.P2P.TxAnnouncementOnly, }, HTTPModules: c.JsonRPC.Http.API, HTTPCors: c.JsonRPC.Http.Cors, diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index 4087d2e84c..47d2448243 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -812,6 +812,13 @@ func (c *Command) Flags(config *Config) *flagset.Flagset { Default: c.cliConfig.P2P.TxArrivalWait, Group: "P2P", }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "txannouncementonly", + Usage: "Whether to only announce transactions to peers", + Value: &c.cliConfig.P2P.TxAnnouncementOnly, + Default: c.cliConfig.P2P.TxAnnouncementOnly, + Group: "P2P", + }) f.SliceStringFlag(&flagset.SliceStringFlag{ Name: "discovery.dns", Usage: "Comma separated list of enrtree:// URLs which will be queried for nodes to connect to", diff --git a/internal/cli/server/testdata/default.toml b/internal/cli/server/testdata/default.toml index 5f95769546..7ab9490c8b 100644 --- a/internal/cli/server/testdata/default.toml +++ b/internal/cli/server/testdata/default.toml @@ -36,6 +36,7 @@ devfakeauthor = false nodekey = "" nodekeyhex = "" txarrivalwait = "500ms" + txannouncementonly = false [p2p.discovery] v4disc = true v5disc = false diff --git a/p2p/server.go b/p2p/server.go index 7b442358c7..d56e200261 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -172,6 +172,9 @@ type Config struct { // TxArrivalWait is the duration (ms) that the node will wait after seeing // an announced transaction before explicitly requesting it TxArrivalWait time.Duration + + // TxAnnouncementOnly is used to only announce transactions to peers + TxAnnouncementOnly bool } // Server manages all peer connections. diff --git a/packaging/templates/mainnet-v1/archive/config.toml b/packaging/templates/mainnet-v1/archive/config.toml index 463d2b8705..086534cd25 100644 --- a/packaging/templates/mainnet-v1/archive/config.toml +++ b/packaging/templates/mainnet-v1/archive/config.toml @@ -35,6 +35,7 @@ gcmode = "archive" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false # [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml index 2d143cb93e..c32bb06522 100644 --- a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml +++ b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml @@ -34,6 +34,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/mainnet-v1/sentry/sentry/bor/pbss_config.toml b/packaging/templates/mainnet-v1/sentry/sentry/bor/pbss_config.toml index 977919a7e7..b771c34a50 100644 --- a/packaging/templates/mainnet-v1/sentry/sentry/bor/pbss_config.toml +++ b/packaging/templates/mainnet-v1/sentry/sentry/bor/pbss_config.toml @@ -36,6 +36,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml b/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml index 31f50d7010..e9cd776ed9 100644 --- a/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml +++ b/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml @@ -36,6 +36,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/mainnet-v1/sentry/validator/bor/pbss_config.toml b/packaging/templates/mainnet-v1/sentry/validator/bor/pbss_config.toml index d6860a18fa..6dfe0d7ccb 100644 --- a/packaging/templates/mainnet-v1/sentry/validator/bor/pbss_config.toml +++ b/packaging/templates/mainnet-v1/sentry/validator/bor/pbss_config.toml @@ -38,6 +38,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/mainnet-v1/without-sentry/bor/config.toml b/packaging/templates/mainnet-v1/without-sentry/bor/config.toml index b8aaec7681..a7b04732e5 100644 --- a/packaging/templates/mainnet-v1/without-sentry/bor/config.toml +++ b/packaging/templates/mainnet-v1/without-sentry/bor/config.toml @@ -36,6 +36,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/mainnet-v1/without-sentry/bor/pbss_config.toml b/packaging/templates/mainnet-v1/without-sentry/bor/pbss_config.toml index b07d50762e..8f8020808a 100644 --- a/packaging/templates/mainnet-v1/without-sentry/bor/pbss_config.toml +++ b/packaging/templates/mainnet-v1/without-sentry/bor/pbss_config.toml @@ -37,6 +37,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v4disc = true # v5disc = false diff --git a/packaging/templates/testnet-amoy/archive/config.toml b/packaging/templates/testnet-amoy/archive/config.toml index 1742cb990c..22761bbd7b 100644 --- a/packaging/templates/testnet-amoy/archive/config.toml +++ b/packaging/templates/testnet-amoy/archive/config.toml @@ -34,6 +34,7 @@ gcmode = "archive" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv4 = [] diff --git a/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml b/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml index 15d6eb4cd2..6a52180ca4 100644 --- a/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml +++ b/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml @@ -33,6 +33,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv4 = [] diff --git a/packaging/templates/testnet-amoy/sentry/sentry/bor/pbss_config.toml b/packaging/templates/testnet-amoy/sentry/sentry/bor/pbss_config.toml index 8262d6d10c..dba4a6b075 100644 --- a/packaging/templates/testnet-amoy/sentry/sentry/bor/pbss_config.toml +++ b/packaging/templates/testnet-amoy/sentry/sentry/bor/pbss_config.toml @@ -34,6 +34,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv4 = [] diff --git a/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml b/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml index f94768168c..11f0999b4f 100644 --- a/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml +++ b/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml @@ -35,6 +35,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv4 = [] diff --git a/packaging/templates/testnet-amoy/sentry/validator/bor/pbss_config.toml b/packaging/templates/testnet-amoy/sentry/validator/bor/pbss_config.toml index e45761d2de..f8711db138 100644 --- a/packaging/templates/testnet-amoy/sentry/validator/bor/pbss_config.toml +++ b/packaging/templates/testnet-amoy/sentry/validator/bor/pbss_config.toml @@ -36,6 +36,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv4 = [] diff --git a/packaging/templates/testnet-amoy/without-sentry/bor/config.toml b/packaging/templates/testnet-amoy/without-sentry/bor/config.toml index 73f5a31742..bde03bdbcc 100644 --- a/packaging/templates/testnet-amoy/without-sentry/bor/config.toml +++ b/packaging/templates/testnet-amoy/without-sentry/bor/config.toml @@ -35,6 +35,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv4 = [] diff --git a/packaging/templates/testnet-amoy/without-sentry/bor/pbss_config.toml b/packaging/templates/testnet-amoy/without-sentry/bor/pbss_config.toml index 1820524938..19435285b0 100644 --- a/packaging/templates/testnet-amoy/without-sentry/bor/pbss_config.toml +++ b/packaging/templates/testnet-amoy/without-sentry/bor/pbss_config.toml @@ -36,6 +36,7 @@ syncmode = "full" # nodekey = "" # nodekeyhex = "" # txarrivalwait = "500ms" + # txannouncementonly = false [p2p.discovery] # v5disc = false # bootnodesv5 = [] diff --git a/scripts/getconfig.go b/scripts/getconfig.go index 0614065896..7e19845cb7 100644 --- a/scripts/getconfig.go +++ b/scripts/getconfig.go @@ -172,6 +172,7 @@ var nameTagMap = map[string]string{ "maxpeers": "maxpeers", "maxpendpeers": "maxpendpeers", "txarrivalwait": "txarrivalwait", + "txannouncementonly": "txannouncementonly", "nat": "nat", "nodiscover": "nodiscover", "v5disc": "v5disc",