From c9ee3d9c5eaebc948d1431c96ae4f88cef37cd31 Mon Sep 17 00:00:00 2001 From: David Hill Date: Mon, 24 Aug 2015 11:48:59 -0400 Subject: [PATCH] wire: Implement SFNodeBloom (BIP0111). SFNodeBloom is a new service flag that a node is required to use to indicate that it supports bloom filtering. This includes a protocol version bump to 70011 and a wire version bump to 0.3.0. btcd: The SFNodeBloom flag is set by default. A new configuration option --nopeerbloomfilters has been added to to disable bloom filtering. Any node advertising a version greater than or equal to 70011 that attempts to use bloom filtering will be disconnected if bloom filtering is disabled. This mimics Bitcoin Core commit afb0ccaf9c9e4e8fac7db3564c4e19c9218c6b03 --- config.go | 1 + doc.go | 165 ++++++++++++++++++++++-------------------- peer.go | 37 +++++++++- sample-btcd.conf | 2 + server.go | 23 ++++-- wire/doc.go | 9 ++- wire/msgversion.go | 2 +- wire/protocol.go | 12 ++- wire/protocol_test.go | 3 +- 9 files changed, 156 insertions(+), 98 deletions(-) diff --git a/config.go b/config.go index 48e168bf..5bff8899 100644 --- a/config.go +++ b/config.go @@ -116,6 +116,7 @@ type config struct { GetWorkKeys []string `long:"getworkkey" description:"DEPRECATED -- Use the --miningaddr option instead"` AddrIndex bool `long:"addrindex" description:"Build and maintain a full address index. Currently only supported by leveldb."` DropAddrIndex bool `long:"dropaddrindex" description:"Deletes the address-based transaction index from the database on start up, and the exits."` + NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support."` onionlookup func(string) ([]net.IP, error) lookup func(string) ([]net.IP, error) oniondial func(string, string) (net.Conn, error) diff --git a/doc.go b/doc.go index 660b158d..c3386235 100644 --- a/doc.go +++ b/doc.go @@ -21,86 +21,91 @@ Usage: btcd [OPTIONS] Application Options: - -V, --version Display version information and exit - -C, --configfile= Path to configuration file - -b, --datadir= Directory to store data - -a, --addpeer= Add a peer to connect with at startup - --connect= Connect only to the specified peers at startup - --nolisten Disable listening for incoming connections -- NOTE: - Listening is automatically disabled if the --connect - or --proxy options are used without also specifying - listen interfaces via --listen - --listen= Add an interface/port to listen for connections - (default all interfaces port: 8333, testnet: 18333) - --maxpeers= Max number of inbound and outbound peers (125) - --banduration= How long to ban misbehaving peers. Valid time units - are {s, m, h}. Minimum 1 second (24h0m0s) - -u, --rpcuser= Username for RPC connections - -P, --rpcpass= Password for RPC connections - --rpclimituser= Username for limited RPC connections - --rpclimitpass= Password for limited RPC connections - --rpclisten= Add an interface/port to listen for RPC connections - (default port: 8334, testnet: 18334) - --rpccert= File containing the certificate file - --rpckey= File containing the certificate key - --rpcmaxclients= Max number of RPC clients for standard connections - (10) - --rpcmaxwebsockets= Max number of RPC clients for standard connections - (25) - --norpc Disable built-in RPC server -- NOTE: The RPC server - is disabled by default if no rpcuser/rpcpass is - specified - --notls Disable TLS for the RPC server -- NOTE: This is only - allowed if the RPC server is bound to localhost - --nodnsseed Disable DNS seeding for peers - --externalip: Add an ip to the list of local addresses we claim to - listen on to peers - --proxy= Connect via SOCKS5 proxy (eg. 127.0.0.1:9050) - --proxyuser= Username for proxy server - --proxypass= Password for proxy server - --onion= Connect to tor hidden services via SOCKS5 proxy (eg. - 127.0.0.1:9050) - --onionuser= Username for onion proxy server - --onionpass= Password for onion proxy server - --noonion= Disable connecting to tor hidden services - --torisolation Enable Tor stream isolation by randomizing user - credentials for each connection. - --testnet= Use the test network - --regtest= Use the regression test network - --nocheckpoints= Disable built-in checkpoints. Don't do this unless - you know what you're doing. - --dbtype= Database backend to use for the Block Chain (leveldb) - --profile= Enable HTTP profiling on given port -- NOTE port must - be between 1024 and 65536 (6060) - --cpuprofile= Write CPU profile to the specified file - -d, --debuglevel: Logging level for all subsystems {trace, debug, info, - warn, error, critical} -- You may also specify - =,=,... to set - the log level for individual subsystems -- Use show - to list available subsystems (info) - --upnp Use UPnP to map our listening port outside of NAT - --limitfreerelay= Limit relay of transactions with no transaction fee - to the given amount in thousands of bytes per minute - (15) - --norelaypriority Do not require free or low-fee transactions to have - high priority for relaying - --maxorphantx= Max number of orphan transactions to keep in memory - (1000) - --generate= Generate (mine) bitcoins using the CPU - --miningaddr= Add the specified payment address to the list of - addresses to use for generated blocks -- At least - one address is required if the generate option is set - --blockminsize= Mininum block size in bytes to be used when creating - a block - --blockmaxsize= Maximum block size in bytes to be used when creating - a block (750000) - --blockprioritysize= Size in bytes for high-priority/low-fee transactions - when creating a block (50000) - --getworkkey= DEPRECATED -- Use the --miningaddr option instead - --addrindex= Build and maintain a full address index. Currently - only supported by leveldb. - --dropaddrindex= Deletes the address-based transaction index from the - database on start up, and the exits. + -V, --version Display version information and exit + -C, --configfile= Path to configuration file + -b, --datadir= Directory to store data + --logdir= Directory to log output. + -a, --addpeer= Add a peer to connect with at startup + --connect= Connect only to the specified peers at startup + --nolisten Disable listening for incoming connections -- NOTE: + Listening is automatically disabled if the --connect + or --proxy options are used without also specifying + listen interfaces via --listen + --listen= Add an interface/port to listen for connections + (default all interfaces port: 8333, testnet: 18333) + --maxpeers= Max number of inbound and outbound peers (125) + --banduration= How long to ban misbehaving peers. Valid time units + are {s, m, h}. Minimum 1 second (24h0m0s) + -u, --rpcuser= Username for RPC connections + -P, --rpcpass= Password for RPC connections + --rpclimituser= Username for limited RPC connections + --rpclimitpass= Password for limited RPC connections + --rpclisten= Add an interface/port to listen for RPC connections + (default port: 8334, testnet: 18334) + --rpccert= File containing the certificate file + --rpckey= File containing the certificate key + --rpcmaxclients= Max number of RPC clients for standard connections + (10) + --rpcmaxwebsockets= Max number of RPC websocket connections (25) + --norpc Disable built-in RPC server -- NOTE: The RPC server + is disabled by default if no rpcuser/rpcpass or + rpclimituser/rpclimitpass is specified + --notls Disable TLS for the RPC server -- NOTE: This is only + allowed if the RPC server is bound to localhost + --nodnsseed Disable DNS seeding for peers + --externalip= Add an ip to the list of local addresses we claim to + listen on to peers + --proxy= Connect via SOCKS5 proxy (eg. 127.0.0.1:9050) + --proxyuser= Username for proxy server + --proxypass= Password for proxy server + --onion= Connect to tor hidden services via SOCKS5 proxy + (eg. 127.0.0.1:9050) + --onionuser= Username for onion proxy server + --onionpass= Password for onion proxy server + --noonion Disable connecting to tor hidden services + --torisolation Enable Tor stream isolation by randomizing user + credentials for each connection. + --testnet Use the test network + --regtest Use the regression test network + --simnet Use the simulation test network + --nocheckpoints Disable built-in checkpoints. Don't do this unless + you know what you're doing. + --dbtype= Database backend to use for the Block Chain + (leveldb) + --profile= Enable HTTP profiling on given port -- NOTE port + must be between 1024 and 65536 + --cpuprofile= Write CPU profile to the specified file + -d, --debuglevel= Logging level for all subsystems {trace, debug, + info, warn, error, critical} -- You may also specify + =,=,... to set + the log level for individual subsystems -- Use show + to list available subsystems (info) + --upnp Use UPnP to map our listening port outside of NAT + --limitfreerelay= Limit relay of transactions with no transaction fee + to the given amount in thousands of bytes per + minute (15) + --norelaypriority Do not require free or low-fee transactions to have + high priority for relaying + --maxorphantx= Max number of orphan transactions to keep in memory + (1000) + --generate Generate (mine) bitcoins using the CPU + --miningaddr= Add the specified payment address to the list of + addresses to use for generated blocks -- At least + one address is required if the generate option is + set + --blockminsize= Mininum block size in bytes to be used when creating + a block + --blockmaxsize= Maximum block size in bytes to be used when creating + a block (750000) + --blockprioritysize= Size in bytes for high-priority/low-fee transactions + when creating a block (50000) + --getworkkey= DEPRECATED -- Use the --miningaddr option instead + --addrindex Build and maintain a full address index. Currently + only supported by leveldb. + --dropaddrindex Deletes the address-based transaction index from the + database on start up, and the exits. + --nopeerbloomfilters Disable bloom filtering support. + Help Options: -h, --help Show this help message diff --git a/peer.go b/peer.go index e35ca028..6e846223 100644 --- a/peer.go +++ b/peer.go @@ -28,7 +28,7 @@ import ( const ( // maxProtocolVersion is the max protocol version the peer supports. - maxProtocolVersion = 70002 + maxProtocolVersion = 70011 // outputBufferSize is the number of elements the output channels use. outputBufferSize = 50 @@ -335,8 +335,8 @@ func (p *peer) pushVersionMsg() error { // by the remote peer in its version message msg.AddrYou.Services = wire.SFNodeNetwork - // Advertise that we're a full node. - msg.Services = wire.SFNodeNetwork + // Advertise our supported services. + msg.Services = p.server.services // Advertise our max supported protocol version. msg.ProtocolVersion = maxProtocolVersion @@ -1088,11 +1088,33 @@ func (p *peer) handleGetHeadersMsg(msg *wire.MsgGetHeaders) { p.QueueMessage(headersMsg, nil) } +// isValidBIP0111 is a helper function for the bloom filter commands to check +// BIP0111 compliance. +func (p *peer) isValidBIP0111(cmd string) bool { + if p.server.services&wire.SFNodeBloom != wire.SFNodeBloom { + if p.ProtocolVersion() >= wire.BIP0111Version { + peerLog.Debugf("%s sent an unsupported %s "+ + "request -- disconnecting", p, cmd) + p.Disconnect() + } else { + peerLog.Debugf("Ignoring %s request from %s -- bloom "+ + "support is disabled", cmd, p) + } + return false + } + + return true +} + // handleFilterAddMsg is invoked when a peer receives a filteradd bitcoin // message and is used by remote peers to add data to an already loaded bloom // filter. The peer will be disconnected if a filter is not loaded when this // message is received. func (p *peer) handleFilterAddMsg(msg *wire.MsgFilterAdd) { + if !p.isValidBIP0111(msg.Command()) { + return + } + if !p.filter.IsLoaded() { peerLog.Debugf("%s sent a filteradd request with no filter "+ "loaded -- disconnecting", p) @@ -1108,12 +1130,17 @@ func (p *peer) handleFilterAddMsg(msg *wire.MsgFilterAdd) { // The peer will be disconnected if a filter is not loaded when this message is // received. func (p *peer) handleFilterClearMsg(msg *wire.MsgFilterClear) { + if !p.isValidBIP0111(msg.Command()) { + return + } + if !p.filter.IsLoaded() { peerLog.Debugf("%s sent a filterclear request with no "+ "filter loaded -- disconnecting", p) p.Disconnect() return } + p.filter.Unload() } @@ -1121,6 +1148,10 @@ func (p *peer) handleFilterClearMsg(msg *wire.MsgFilterClear) { // message and it used to load a bloom filter that should be used for delivering // merkle blocks and associated transactions that match the filter. func (p *peer) handleFilterLoadMsg(msg *wire.MsgFilterLoad) { + if !p.isValidBIP0111(msg.Command()) { + return + } + // Transaction relay is no longer disabled once a filterload message is // received regardless of its original state. p.relayMtx.Lock() diff --git a/sample-btcd.conf b/sample-btcd.conf index ee873b5e..f09e7d05 100644 --- a/sample-btcd.conf +++ b/sample-btcd.conf @@ -144,6 +144,8 @@ ; Disable listening for incoming connections. This will override all listeners. ; nolisten=1 +; Disable peer bloom filtering. See BIP0111. +; nopeerbloomfilters=1 ; ------------------------------------------------------------------------------ ; RPC server options - The following options control the built-in RPC server diff --git a/server.go b/server.go index 1d8aaa59..a842c5dd 100644 --- a/server.go +++ b/server.go @@ -28,16 +28,16 @@ import ( ) const ( - // These constants are used by the DNS seed code to pick a random last seen - // time. + // These constants are used by the DNS seed code to pick a random last + // seen time. secondsIn3Days int32 = 24 * 60 * 60 * 3 secondsIn4Days int32 = 24 * 60 * 60 * 4 ) const ( - // supportedServices describes which services are supported by the - // server. - supportedServices = wire.SFNodeNetwork + // defaultServices describes the default services that are supported by + // the server. + defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom // defaultMaxOutbound is the default number of max outbound peers. defaultMaxOutbound = 8 @@ -109,6 +109,7 @@ type server struct { nat NAT db database.Db timeSource blockchain.MedianTimeSource + services wire.ServiceFlag } type peerState struct { @@ -1216,7 +1217,7 @@ out: continue out } na := wire.NewNetAddressIPPort(externalip, uint16(listenPort), - wire.SFNodeNetwork) + s.services) err = s.addrManager.AddLocalAddress(na, addrmgr.UpnpPrio) if err != nil { // XXX DeletePortMapping? @@ -1250,6 +1251,11 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param return nil, err } + services := defaultServices + if cfg.NoPeerBloomFilters { + services &^= wire.SFNodeBloom + } + amgr := addrmgr.New(cfg.DataDir, btcdLookup) var listeners []net.Listener @@ -1287,7 +1293,7 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param eport = uint16(port) } na, err := amgr.HostToNetAddress(host, eport, - wire.SFNodeNetwork) + services) if err != nil { srvrLog.Warnf("Not adding %s as "+ "externalip: %v", sip, err) @@ -1323,7 +1329,7 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param continue } na := wire.NewNetAddressIPPort(ip, - uint16(port), wire.SFNodeNetwork) + uint16(port), services) if discover { err = amgr.AddLocalAddress(na, addrmgr.InterfacePrio) if err != nil { @@ -1394,6 +1400,7 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param nat: nat, db: db, timeSource: blockchain.NewMedianTime(), + services: services, } bm, err := newBlockManager(&s) if err != nil { diff --git a/wire/doc.go b/wire/doc.go index 8ae4e361..1d83a263 100644 --- a/wire/doc.go +++ b/wire/doc.go @@ -151,9 +151,10 @@ Bitcoin Improvement Proposals This package includes spec changes outlined by the following BIPs: - BIP0014 (https://en.bitcoin.it/wiki/BIP_0014) - BIP0031 (https://en.bitcoin.it/wiki/BIP_0031) - BIP0035 (https://en.bitcoin.it/wiki/BIP_0035) - BIP0037 (https://en.bitcoin.it/wiki/BIP_0037) + BIP0014 (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki) + BIP0031 (https://github.com/bitcoin/bips/blob/master/bip-0031.mediawiki) + BIP0035 (https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki) + BIP0037 (https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki) + BIP0111 (https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki) */ package wire diff --git a/wire/msgversion.go b/wire/msgversion.go index cdcbc302..9905b410 100644 --- a/wire/msgversion.go +++ b/wire/msgversion.go @@ -18,7 +18,7 @@ import ( const MaxUserAgentLen = 2000 // DefaultUserAgent for wire in the stack -const DefaultUserAgent = "/btcwire:0.2.1/" +const DefaultUserAgent = "/btcwire:0.3.0/" // MsgVersion implements the Message interface and represents a bitcoin version // message. It is used for a peer to advertise itself as soon as an outbound diff --git a/wire/protocol.go b/wire/protocol.go index 7ab769eb..13826083 100644 --- a/wire/protocol.go +++ b/wire/protocol.go @@ -12,7 +12,7 @@ import ( const ( // ProtocolVersion is the latest protocol version this package supports. - ProtocolVersion uint32 = 70002 + ProtocolVersion uint32 = 70011 // MultipleAddressVersion is the protocol version which added multiple // addresses per message (pver >= MultipleAddressVersion). @@ -35,6 +35,10 @@ const ( // with a relay flag (pver >= BIP0037Version). BIP0037Version uint32 = 70001 + // BIP0111Version is the protocol version which added the SFNodeBloom + // service flag. + BIP0111Version uint32 = 70011 + // RejectVersion is the protocol version which added a new reject // message. RejectVersion uint32 = 70002 @@ -50,12 +54,17 @@ const ( // SFNodeGetUTXO is a flag used to indicate a peer supports the // getutxos and utxos commands (BIP0064). SFNodeGetUTXO + + // SFNodeBloom is a flag used to indiciate a peer supports bloom + // filtering. + SFNodeBloom ) // Map of service flags back to their constant names for pretty printing. var sfStrings = map[ServiceFlag]string{ SFNodeNetwork: "SFNodeNetwork", SFNodeGetUTXO: "SFNodeGetUTXO", + SFNodeBloom: "SFNodeBloom", } // orderedSFStrings is an ordered list of service flags from highest to @@ -63,6 +72,7 @@ var sfStrings = map[ServiceFlag]string{ var orderedSFStrings = []ServiceFlag{ SFNodeNetwork, SFNodeGetUTXO, + SFNodeBloom, } // String returns the ServiceFlag in human-readable form. diff --git a/wire/protocol_test.go b/wire/protocol_test.go index 02365835..fee87968 100644 --- a/wire/protocol_test.go +++ b/wire/protocol_test.go @@ -19,7 +19,8 @@ func TestServiceFlagStringer(t *testing.T) { {0, "0x0"}, {wire.SFNodeNetwork, "SFNodeNetwork"}, {wire.SFNodeGetUTXO, "SFNodeGetUTXO"}, - {0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|0xfffffffc"}, + {wire.SFNodeBloom, "SFNodeBloom"}, + {0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|SFNodeBloom|0xfffffff8"}, } t.Logf("Running %d tests", len(tests))