Add a simulation test network via --simnet param.

This commit, along with recent commits to btcnet and btcwire, expose a new
network that is intended to provide a private network useful for
simulation testing.  To that end, it has the special property that it has
no DNS seeds and will actively ignore all addr and getaddr messages.  It
will also not try to connect to any nodes other than those specified via
--connect.  This allows the network to remain private to the specific
nodes involved in the testing and not simply become another public
testnet.

The network difficulty is also set extremely low like the regression test
network so blocks can be created extremely quickly without requiring a lot
of hashing power.
This commit is contained in:
Dave Collins 2014-05-28 15:19:38 -05:00
parent 252c022644
commit 605eb7f4b4
5 changed files with 110 additions and 45 deletions

View file

@ -91,6 +91,7 @@ type config struct {
NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"` NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"`
TestNet3 bool `long:"testnet" description:"Use the test network"` TestNet3 bool `long:"testnet" description:"Use the test network"`
RegressionTest bool `long:"regtest" description:"Use the regression 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."` 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"` 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"` 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. // Load additional config from file.
var configFileError error var configFileError error
parser := newConfigParser(&cfg, &serviceOpts, flags.Default) 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) err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
if err != nil { if err != nil {
if _, ok := err.(*os.PathError); !ok { if _, ok := err.(*os.PathError); !ok {
@ -374,22 +377,36 @@ func loadConfig() (*config, []string, error) {
return nil, nil, err return nil, nil, err
} }
// The two test networks can't be selected simultaneously. // Multiple networks can't be selected simultaneously.
if cfg.TestNet3 && cfg.RegressionTest { numNets := 0
str := "%s: The testnet and regtest params can't be used " + if cfg.TestNet3 {
"together -- choose one of the two" 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") err := fmt.Errorf(str, "loadConfig")
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr) parser.WriteHelp(os.Stderr)
return nil, nil, err return nil, nil, err
} }
// Choose the active network params based on the testnet and regression // Choose the active network params based on the selected network.
// test net flags. switch {
if cfg.TestNet3 { case cfg.TestNet3:
activeNetParams = &testNet3Params activeNetParams = &testNet3Params
} else if cfg.RegressionTest { case cfg.RegressionTest:
activeNetParams = &regressionNetParams activeNetParams = &regressionNetParams
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" // Append the network type to the data directory so it is "namespaced"

View file

@ -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 // 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 // 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 // data and log directory "testnet", which does not match the Name field of the

97
peer.go
View file

@ -284,6 +284,47 @@ func (p *peer) pushVersionMsg() error {
return nil 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 // 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 // and is used to negotiate the protocol version details as well as kick start
// the communications. // the communications.
@ -347,39 +388,13 @@ func (p *peer) handleVersionMsg(msg *btcwire.MsgVersion) {
// Send verack. // Send verack.
p.QueueMessage(btcwire.NewMsgVerAck(), nil) p.QueueMessage(btcwire.NewMsgVerAck(), nil)
// Outbound connections. // Update the address manager and request known addresses from the
if !p.inbound { // remote peer for outbound connections. This is skipped when running
// TODO(davec): Only do this if not doing the initial block // on the simulation test network since it is only intended to connect
// download and the local address is routable. // to specified peers and actively avoids advertising and connecting to
if !cfg.DisableListen /* && isCurrent? */ { // discovered peers.
// get address that best matches. p.na if !cfg.SimNet {
lna := p.server.addrManager.getBestLocalAddress(p.na) p.updateAddresses(msg)
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)
}
} }
// Signal the block manager this peer is a new sync candidate. // 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 // and is used to provide the peer with known addresses from the address
// manager. // manager.
func (p *peer) handleGetAddrMsg(msg *btcwire.MsgGetAddr) { 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. // Get the current known addresses from the address manager.
addrCache := p.server.addrManager.AddressCache() 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 // handleAddrMsg is invoked when a peer receives an addr bitcoin message and
// is used to notify the server about advertised addresses. // is used to notify the server about advertised addresses.
func (p *peer) handleAddrMsg(msg *btcwire.MsgAddr) { 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. // Ignore old style addresses which don't include a timestamp.
if p.ProtocolVersion() < btcwire.NetAddressTimeVersion { if p.ProtocolVersion() < btcwire.NetAddressTimeVersion {
return return

View file

@ -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 // 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. // way to relay a found block or receive transactions to work on.
// However, allow this state when running in regression test mode. // However, allow this state when running in the regression test or
if !cfg.RegressionTest && s.server.ConnectedCount() == 0 { // simulation test mode.
if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 {
return nil, btcjson.ErrClientNotConnected return nil, btcjson.ErrClientNotConnected
} }

View file

@ -571,7 +571,15 @@ out:
break 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 || if !state.NeedMoreOutbound() || len(cfg.ConnectPeers) > 0 ||
atomic.LoadInt32(&s.shutdown) != 0 { atomic.LoadInt32(&s.shutdown) != 0 {
continue continue