diff --git a/config.go b/config.go index 1be694aa..b66e7fb1 100644 --- a/config.go +++ b/config.go @@ -91,6 +91,7 @@ type config struct { NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"` TestNet3 bool `long:"testnet" description:"Use the test network"` RegressionTest bool `long:"regtest" description:"Use the regression test network"` + SimNet bool `long:"simnet" description:"Use the simulation test network"` DisableCheckpoints bool `long:"nocheckpoints" description:"Disable built-in checkpoints. Don't do this unless you know what you're doing."` DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` @@ -348,7 +349,9 @@ func loadConfig() (*config, []string, error) { // Load additional config from file. var configFileError error parser := newConfigParser(&cfg, &serviceOpts, flags.Default) - if !preCfg.RegressionTest || preCfg.ConfigFile != defaultConfigFile { + if !(preCfg.RegressionTest || preCfg.SimNet) || preCfg.ConfigFile != + defaultConfigFile { + err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) if err != nil { if _, ok := err.(*os.PathError); !ok { @@ -374,22 +377,36 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } - // The two test networks can't be selected simultaneously. - if cfg.TestNet3 && cfg.RegressionTest { - str := "%s: The testnet and regtest params can't be used " + - "together -- choose one of the two" + // Multiple networks can't be selected simultaneously. + numNets := 0 + if cfg.TestNet3 { + numNets++ + } + if cfg.RegressionTest { + numNets++ + } + if cfg.SimNet { + numNets++ + } + if numNets > 1 { + str := "%s: The testnet, regtest, and simnet params can't be " + + "used together -- choose one of the three" err := fmt.Errorf(str, "loadConfig") fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } - // Choose the active network params based on the testnet and regression - // test net flags. - if cfg.TestNet3 { + // Choose the active network params based on the selected network. + switch { + case cfg.TestNet3: activeNetParams = &testNet3Params - } else if cfg.RegressionTest { + case cfg.RegressionTest: activeNetParams = ®ressionNetParams + case cfg.SimNet: + // Also disable dns seeding on the simulation test network. + activeNetParams = &simNetParams + cfg.DisableDNSSeed = true } // Append the network type to the data directory so it is "namespaced" diff --git a/params.go b/params.go index a81c9b6c..d760726d 100644 --- a/params.go +++ b/params.go @@ -62,6 +62,14 @@ var testNet3Params = params{ }, } +// simNetParams contains parameters specific to the simulation test network +// (btcwire.SimNet). +var simNetParams = params{ + Params: &btcnet.SimNetParams, + rpcPort: "18556", + dnsSeeds: []string{}, // NOTE: There must NOT be any seeds. +} + // netName returns the name used when referring to a bitcoin network. At the // time of writing, btcd currently places blocks for testnet version 3 in the // data and log directory "testnet", which does not match the Name field of the diff --git a/peer.go b/peer.go index 9ef1e4d5..4481353d 100644 --- a/peer.go +++ b/peer.go @@ -284,6 +284,47 @@ func (p *peer) pushVersionMsg() error { return nil } +// updateAddresses potentially adds addresses to the address manager and +// requests known addresses from the remote peer depending on whether the peer +// is an inbound or outbound peer and other factors such as address routability +// and the negotiated protocol version. +func (p *peer) updateAddresses(msg *btcwire.MsgVersion) { + // Outbound connections. + if !p.inbound { + // TODO(davec): Only do this if not doing the initial block + // download and the local address is routable. + if !cfg.DisableListen /* && isCurrent? */ { + // Get address that best matches. + lna := p.server.addrManager.getBestLocalAddress(p.na) + if Routable(lna) { + addresses := []*btcwire.NetAddress{lna} + p.pushAddrMsg(addresses) + } + } + + // Request known addresses if the server address manager needs + // more and the peer has a protocol version new enough to + // include a timestamp with addresses. + hasTimestamp := p.ProtocolVersion() >= + btcwire.NetAddressTimeVersion + if p.server.addrManager.NeedMoreAddresses() && hasTimestamp { + p.QueueMessage(btcwire.NewMsgGetAddr(), nil) + } + + // Mark the address as a known good address. + p.server.addrManager.Good(p.na) + } else { + // A peer might not be advertising the same address that it + // actually connected from. One example of why this can happen + // is with NAT. Only add the address to the address manager if + // the addresses agree. + if NetAddressKey(&msg.AddrMe) == NetAddressKey(p.na) { + p.server.addrManager.AddAddress(p.na, p.na) + p.server.addrManager.Good(p.na) + } + } +} + // handleVersionMsg is invoked when a peer receives a version bitcoin message // and is used to negotiate the protocol version details as well as kick start // the communications. @@ -347,39 +388,13 @@ func (p *peer) handleVersionMsg(msg *btcwire.MsgVersion) { // Send verack. p.QueueMessage(btcwire.NewMsgVerAck(), nil) - // Outbound connections. - if !p.inbound { - // TODO(davec): Only do this if not doing the initial block - // download and the local address is routable. - if !cfg.DisableListen /* && isCurrent? */ { - // get address that best matches. p.na - lna := p.server.addrManager.getBestLocalAddress(p.na) - if Routable(lna) { - addresses := []*btcwire.NetAddress{lna} - p.pushAddrMsg(addresses) - } - } - - // Request known addresses if the server address manager needs - // more and the peer has a protocol version new enough to - // include a timestamp with addresses. - hasTimestamp := p.ProtocolVersion() >= - btcwire.NetAddressTimeVersion - if p.server.addrManager.NeedMoreAddresses() && hasTimestamp { - p.QueueMessage(btcwire.NewMsgGetAddr(), nil) - } - - // Mark the address as a known good address. - p.server.addrManager.Good(p.na) - } else { - // A peer might not be advertising the same address that it - // actually connected from. One example of why this can happen - // is with NAT. Only add the address to the address manager if - // the addresses agree. - if NetAddressKey(&msg.AddrMe) == NetAddressKey(p.na) { - p.server.addrManager.AddAddress(p.na, p.na) - p.server.addrManager.Good(p.na) - } + // Update the address manager and request known addresses from the + // remote peer for outbound connections. This is skipped when running + // on the simulation test network since it is only intended to connect + // to specified peers and actively avoids advertising and connecting to + // discovered peers. + if !cfg.SimNet { + p.updateAddresses(msg) } // Signal the block manager this peer is a new sync candidate. @@ -861,6 +876,14 @@ func (p *peer) handleGetHeadersMsg(msg *btcwire.MsgGetHeaders) { // and is used to provide the peer with known addresses from the address // manager. func (p *peer) handleGetAddrMsg(msg *btcwire.MsgGetAddr) { + // Don't return any addresses when running on the simulation test + // network. This helps prevent the network from becoming another + // public test network since it will not be able to learn about other + // peers that have not specifically been provided. + if cfg.SimNet { + return + } + // Get the current known addresses from the address manager. addrCache := p.server.addrManager.AddressCache() @@ -917,6 +940,14 @@ func (p *peer) pushAddrMsg(addresses []*btcwire.NetAddress) error { // handleAddrMsg is invoked when a peer receives an addr bitcoin message and // is used to notify the server about advertised addresses. func (p *peer) handleAddrMsg(msg *btcwire.MsgAddr) { + // Ignore addresses when running on the simulation test network. This + // helps prevent the network from becoming another public test network + // since it will not be able to learn about other peers that have not + // specifically been provided. + if cfg.SimNet { + return + } + // Ignore old style addresses which don't include a timestamp. if p.ProtocolVersion() < btcwire.NetAddressTimeVersion { return diff --git a/rpcserver.go b/rpcserver.go index d87d3db2..ec9144c3 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1778,8 +1778,9 @@ func handleGetWork(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { // Return an error if there are no peers connected since there is no // way to relay a found block or receive transactions to work on. - // However, allow this state when running in regression test mode. - if !cfg.RegressionTest && s.server.ConnectedCount() == 0 { + // However, allow this state when running in the regression test or + // simulation test mode. + if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 { return nil, btcjson.ErrClientNotConnected } diff --git a/server.go b/server.go index 58880c3d..906193a1 100644 --- a/server.go +++ b/server.go @@ -571,7 +571,15 @@ out: break out } - // Only try connect to more peers if we actually need more + // Don't try to connect to more peers when running on the + // simulation test network. The simulation network is only + // intended to connect to specified peers and actively avoid + // advertising and connecting to discovered peers. + if cfg.SimNet { + continue + } + + // Only try connect to more peers if we actually need more. if !state.NeedMoreOutbound() || len(cfg.ConnectPeers) > 0 || atomic.LoadInt32(&s.shutdown) != 0 { continue