From eb5b1b72bda33798f0095986662aaefad423e915 Mon Sep 17 00:00:00 2001 From: Matheus Degiovani Date: Fri, 26 Apr 2024 09:40:35 -0300 Subject: [PATCH] multi: Expose disablerelaytx This setting allows the wallet to not sync mempool transactions when in SPV mode. This is useful to reduce cpu, memory and bandwidth consumption for wallets that see very few useful mempool transactions, such as wallets meant for only occasional use or mobile wallets. The setting is exposed as a CLI or config argument --spvdisablerelaytx. --- config.go | 5 ++-- dcrwallet.go | 1 + p2p/peering.go | 53 ++++++++++++++++++++++++++++++++++++++----- sample-dcrwallet.conf | 18 +++++++++++++++ 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/config.go b/config.go index 47057f710..45d968db8 100644 --- a/config.go +++ b/config.go @@ -148,8 +148,9 @@ type config struct { Offline bool `long:"offline" description:"Do not sync the wallet"` // SPV options - SPV bool `long:"spv" description:"Sync using simplified payment verification"` - SPVConnect []string `long:"spvconnect" description:"SPV sync only with specified peers; disables DNS seeding"` + SPV bool `long:"spv" description:"Sync using simplified payment verification"` + SPVConnect []string `long:"spvconnect" description:"SPV sync only with specified peers; disables DNS seeding"` + SPVDisableRelayTx bool `long:"spvdisablerelaytx" description:"Disable receiving mempool transactions when in SPV mode"` // RPC server options RPCCert *cfgutil.ExplicitString `long:"rpccert" description:"RPC server TLS certificate"` diff --git a/dcrwallet.go b/dcrwallet.go index 9cb021e43..e3cf64a7e 100644 --- a/dcrwallet.go +++ b/dcrwallet.go @@ -546,6 +546,7 @@ func spvLoop(ctx context.Context, w *wallet.Wallet) { amgr := addrmgr.New(amgrDir, cfg.lookup) lp := p2p.NewLocalPeer(w.ChainParams(), addr, amgr) lp.SetDialFunc(cfg.dial) + lp.SetDisableRelayTx(cfg.SPVDisableRelayTx) syncer := spv.NewSyncer(w, lp) if len(cfg.SPVConnect) > 0 { syncer.SetPersistentPeers(cfg.SPVConnect) diff --git a/p2p/peering.go b/p2p/peering.go index 6c34e5e4a..c21ccac12 100644 --- a/p2p/peering.go +++ b/p2p/peering.go @@ -133,9 +133,10 @@ type LocalPeer struct { announcedHeaders chan *inMsg receivedInitState chan *inMsg - extaddr net.Addr - amgr *addrmgr.AddrManager - chainParams *chaincfg.Params + extaddr net.Addr + amgr *addrmgr.AddrManager + chainParams *chaincfg.Params + disableRelayTx bool rpByID map[uint64]*RemotePeer rpMu sync.Mutex @@ -168,6 +169,13 @@ func (lp *LocalPeer) SetDialFunc(dial DialFunc) { lp.dial = dial } +// SetDisableRelayTx sets whether remote peers will be asked to relay +// transactions to the local peer. This must be called before the local peer +// runs. +func (lp *LocalPeer) SetDisableRelayTx(disableRelayTx bool) { + lp.disableRelayTx = disableRelayTx +} + func isCGNAT(ip net.IP) bool { if ip4 := ip.To4(); ip4 != nil { return ip4[0] == 100 && ip4[1]&0xc0 == 64 // 100.64.0.0/10 @@ -210,6 +218,7 @@ func (lp *LocalPeer) newMsgVersion(pver uint32, c net.Conn) (*wire.MsgVersion, e v := wire.NewMsgVersion(la, ra, nonce, 0) v.AddUserAgent(uaName, uaVersion) v.ProtocolVersion = int32(pver) + v.DisableRelayTx = lp.disableRelayTx return v, nil } @@ -738,9 +747,7 @@ func (rp *RemotePeer) readMessages(ctx context.Context) error { case *wire.MsgHeaders: rp.receivedHeaders(ctx, m) case *wire.MsgInv: - if rp.lp.messageIsMasked(MaskInv) { - rp.lp.receivedInv <- newInMsg(rp, msg) - } + rp.receivedInv(ctx, m) case *wire.MsgGetMiningState: rp.receivedGetMiningState(ctx) case *wire.MsgGetInitState: @@ -1847,3 +1854,37 @@ func (rp *RemotePeer) GetInitState(ctx context.Context, msg *wire.MsgGetInitStat } } } + +// invVecContainsTx returns true if at least one inv vector is of type +// transaction. +func invVecContainsTx(inv []*wire.InvVect) bool { + for i := range inv { + if inv[i].Type == wire.InvTypeTx { + return true + } + } + return false +} + +// receivedInv is called when an inv message is received from the remote peer. +func (rp *RemotePeer) receivedInv(ctx context.Context, inv *wire.MsgInv) { + const opf = "remotepeer(%v).receivedInv" + + // When tx relay is disabled, we don't expect transactions on invs. + if rp.lp.disableRelayTx && invVecContainsTx(inv.InvList) { + op := errors.Opf(opf, rp.raddr) + err := errors.E(op, errors.Protocol, "received tx in msginv when tx relaying is disabled") + rp.Disconnect(err) + return + } + + // Ignore if the user is not interested in invs. + if !rp.lp.messageIsMasked(MaskInv) { + return + } + + select { + case rp.lp.receivedInv <- newInMsg(rp, inv): + case <-ctx.Done(): + } +} diff --git a/sample-dcrwallet.conf b/sample-dcrwallet.conf index fe7ea4671..f40a563e9 100644 --- a/sample-dcrwallet.conf +++ b/sample-dcrwallet.conf @@ -198,6 +198,24 @@ ; dcrdpassword= +; ------------------------------------------------------------------------------ +; SPV settings +; ------------------------------------------------------------------------------ + +; Enable SPV mode by setting SPV to 1. +; spv=1 + +; spvconnect may be used to specify specific peers to connect to, when using +; SPV mode. Multiple peers may be specified. When spvconnect is set, the wallet +; will connect _only_ to the listed peers. +; spvconnect= + +; Set spvdisablerelaytx to 1 to disable receiving transactions from remote peers +; in SPV mode. This reduces bandwidth consumption but effectively disables the +; mempool. +; spvdisablerelaytx=1 + + ; ------------------------------------------------------------------------------ ; Debug ; ------------------------------------------------------------------------------