// TestPing tests the MsgPing API against the latest protocol version. func TestPing(t *testing.T) { pver := wire.ProtocolVersion // Ensure we get the same nonce back out. nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := wire.NewMsgPing(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Ensure the command is expected value. wantCmd := "ping" if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgPing: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value for latest protocol version. wantPayload := uint32(8) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } return }
// TestRandomUint64 exercises the randomness of the random number generator on // the system by ensuring the probability of the generated numbers. If the RNG // is evenly distributed as a proper cryptographic RNG should be, there really // should only be 1 number < 2^56 in 2^8 tries for a 64-bit number. However, // use a higher number of 5 to really ensure the test doesn't fail unless the // RNG is just horrendous. func TestRandomUint64(t *testing.T) { tries := 1 << 8 // 2^8 watermark := uint64(1 << 56) // 2^56 maxHits := 5 badRNG := "The random number generator on this system is clearly " + "terrible since we got %d values less than %d in %d runs " + "when only %d was expected" numHits := 0 for i := 0; i < tries; i++ { nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64 iteration %d failed - err %v", i, err) return } if nonce < watermark { numHits++ } if numHits > maxHits { str := fmt.Sprintf(badRNG, numHits, watermark, tries, maxHits) t.Errorf("Random Uint64 iteration %d failed - %v %v", i, str, numHits) return } } }
// TestPongLatest tests the MsgPong API against the latest protocol version. func TestPongLatest(t *testing.T) { pver := wire.ProtocolVersion nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } msg := wire.NewMsgPong(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPong: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Ensure the command is expected value. wantCmd := "pong" if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgPong: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value for latest protocol version. wantPayload := uint32(8) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Test encode with latest protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, pver) if err != nil { t.Errorf("encode of MsgPong failed %v err <%v>", msg, err) } // Test decode with latest protocol version. readmsg := wire.NewMsgPong(0) err = readmsg.BtcDecode(&buf, pver) if err != nil { t.Errorf("decode of MsgPong failed [%v] err <%v>", buf, err) } // Ensure nonce is the same. if msg.Nonce != readmsg.Nonce { t.Errorf("Should get same nonce for protocol version %d", pver) } return }
// TestBlockHeader tests the BlockHeader API. func TestBlockHeader(t *testing.T) { nonce64, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } nonce := uint32(nonce64) hash := mainNetGenesisHash merkleHash := mainNetGenesisMerkleRoot votebits := uint16(0x0000) bits := uint32(0x1d00ffff) finalState := [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} voters := uint16(0x0000) freshstake := uint8(0x00) revocations := uint8(0x00) poolsize := uint32(0) sbits := int64(0x0000000000000000) blockHeight := uint32(0) blockSize := uint32(0) extraData := [36]byte{} bh := wire.NewBlockHeader( 1, // verision &hash, &merkleHash, &merkleHash, // stakeRoot votebits, finalState, voters, freshstake, revocations, poolsize, bits, sbits, blockHeight, blockSize, nonce, extraData, ) // Ensure we get the same data back out. if !bh.PrevBlock.IsEqual(&hash) { t.Errorf("NewBlockHeader: wrong prev hash - got %v, want %v", spew.Sprint(bh.PrevBlock), spew.Sprint(hash)) } if !bh.MerkleRoot.IsEqual(&merkleHash) { t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v", spew.Sprint(bh.MerkleRoot), spew.Sprint(merkleHash)) } if !bh.StakeRoot.IsEqual(&merkleHash) { t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v", spew.Sprint(bh.MerkleRoot), spew.Sprint(merkleHash)) } if bh.VoteBits != votebits { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.FinalState != finalState { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.Voters != voters { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.FreshStake != freshstake { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.Revocations != revocations { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.PoolSize != poolsize { t.Errorf("NewBlockHeader: wrong PoolSize - got %v, want %v", bh.PoolSize, poolsize) } if bh.Bits != bits { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.SBits != sbits { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.Nonce != nonce { t.Errorf("NewBlockHeader: wrong nonce - got %v, want %v", bh.Nonce, nonce) } }
// solveBlock attempts to find some combination of a nonce, extra nonce, and // current timestamp which makes the passed block hash to a value less than the // target difficulty. The timestamp is updated periodically and the passed // block is modified with all tweaks during this process. This means that // when the function returns true, the block is ready for submission. // // This function will return early with false when conditions that trigger a // stale block such as a new block showing up or periodically when there are // new transactions and enough time has elapsed without finding a solution. func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit chan struct{}) bool { blockHeight := int64(msgBlock.Header.Height) // Choose a random extra nonce offset for this block template and // worker. enOffset, err := wire.RandomUint64() if err != nil { minrLog.Errorf("Unexpected error while generating random "+ "extra nonce offset: %v", err) enOffset = 0 } // Create a couple of convenience variables. header := &msgBlock.Header targetDifficulty := blockchain.CompactToBig(header.Bits) // Initial state. lastGenerated := time.Now() lastTxUpdate := m.server.txMemPool.LastUpdated() hashesCompleted := uint64(0) // Note that the entire extra nonce range is iterated and the offset is // added relying on the fact that overflow will wrap around 0 as // provided by the Go spec. for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { // Get the old nonce values. ens := getCoinbaseExtranonces(msgBlock) ens[2] = extraNonce + enOffset // Update the extra nonce in the block template with the // new value by regenerating the coinbase script and // setting the merkle root to the new value. The UpdateExtraNonce(msgBlock, blockHeight, ens) // Search through the entire nonce range for a solution while // periodically checking for early quit and stale block // conditions along with updates to the speed monitor. for i := uint32(0); i <= maxNonce; i++ { select { case <-quit: return false case <-ticker.C: m.updateHashes <- hashesCompleted hashesCompleted = 0 // The current block is stale if the memory pool // has been updated since the block template was // generated and it has been at least 3 seconds, // or if it's been one minute. if (lastTxUpdate != m.server.txMemPool.LastUpdated() && time.Now().After(lastGenerated.Add(3*time.Second))) || time.Now().After(lastGenerated.Add(60*time.Second)) { return false } UpdateBlockTime(msgBlock, m.server.blockManager) default: // Non-blocking select to fall through } // Update the nonce and hash the block header. header.Nonce = i hash := header.BlockSha() hashesCompleted += 1 // The block is solved when the new block hash is less // than the target difficulty. Yay! if blockchain.ShaHashToBig(&hash).Cmp(targetDifficulty) <= 0 { m.updateHashes <- hashesCompleted return true } } } return false }
// TestVersion tests the MsgVersion API. func TestVersion(t *testing.T) { pver := wire.ProtocolVersion // Create version message data. lastBlock := int32(234234) tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} me, err := wire.NewNetAddress(tcpAddrMe, wire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} you, err := wire.NewNetAddress(tcpAddrYou, wire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } // Ensure we get the correct data back out. msg := wire.NewMsgVersion(me, you, nonce, lastBlock) if msg.ProtocolVersion != int32(pver) { t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v", msg.ProtocolVersion, pver) } if !reflect.DeepEqual(&msg.AddrMe, me) { t.Errorf("NewMsgVersion: wrong me address - got %v, want %v", spew.Sdump(&msg.AddrMe), spew.Sdump(me)) } if !reflect.DeepEqual(&msg.AddrYou, you) { t.Errorf("NewMsgVersion: wrong you address - got %v, want %v", spew.Sdump(&msg.AddrYou), spew.Sdump(you)) } if msg.Nonce != nonce { t.Errorf("NewMsgVersion: wrong nonce - got %v, want %v", msg.Nonce, nonce) } if msg.UserAgent != wire.DefaultUserAgent { t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", msg.UserAgent, wire.DefaultUserAgent) } if msg.LastBlock != lastBlock { t.Errorf("NewMsgVersion: wrong last block - got %v, want %v", msg.LastBlock, lastBlock) } if msg.DisableRelayTx != false { t.Errorf("NewMsgVersion: disable relay tx is not false by "+ "default - got %v, want %v", msg.DisableRelayTx, false) } msg.AddUserAgent("myclient", "1.2.3", "optional", "comments") customUserAgent := wire.DefaultUserAgent + "myclient:1.2.3(optional; comments)/" if msg.UserAgent != customUserAgent { t.Errorf("AddUserAgent: wrong user agent - got %s, want %s", msg.UserAgent, customUserAgent) } msg.AddUserAgent("mygui", "3.4.5") customUserAgent += "mygui:3.4.5/" if msg.UserAgent != customUserAgent { t.Errorf("AddUserAgent: wrong user agent - got %s, want %s", msg.UserAgent, customUserAgent) } // accounting for ":", "/" err = msg.AddUserAgent(strings.Repeat("t", wire.MaxUserAgentLen-len(customUserAgent)-2+1), "") if _, ok := err.(*wire.MessageError); !ok { t.Errorf("AddUserAgent: expected error not received "+ "- got %v, want %T", err, wire.MessageError{}) } // Version message should not have any services set by default. if msg.Services != 0 { t.Errorf("NewMsgVersion: wrong default services - got %v, want %v", msg.Services, 0) } if msg.HasService(wire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service is set") } // Ensure the command is expected value. wantCmd := "version" if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgVersion: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value. // Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes + // remote and local net addresses + nonce 8 bytes + length of user agent // (varInt) + max allowed user agent length + last block 4 bytes + // relay transactions flag 1 byte. wantPayload := uint32(2102) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Ensure adding the full service node flag works. msg.AddService(wire.SFNodeNetwork) if msg.Services != wire.SFNodeNetwork { t.Errorf("AddService: wrong services - got %v, want %v", msg.Services, wire.SFNodeNetwork) } if !msg.HasService(wire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service not set") } // Use a fake connection. conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou} msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != nil { t.Errorf("NewMsgVersionFromConn: %v", err) } // Ensure we get the correct connection data back out. if !msg.AddrMe.IP.Equal(tcpAddrMe.IP) { t.Errorf("NewMsgVersionFromConn: wrong me ip - got %v, want %v", msg.AddrMe.IP, tcpAddrMe.IP) } if !msg.AddrYou.IP.Equal(tcpAddrYou.IP) { t.Errorf("NewMsgVersionFromConn: wrong you ip - got %v, want %v", msg.AddrYou.IP, tcpAddrYou.IP) } // Use a fake connection with local UDP addresses to force a failure. conn = &fakeConn{ localAddr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}, remoteAddr: tcpAddrYou, } msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != wire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, wire.ErrInvalidNetAddr) } // Use a fake connection with remote UDP addresses to force a failure. conn = &fakeConn{ localAddr: tcpAddrMe, remoteAddr: &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}, } msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != wire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, wire.ErrInvalidNetAddr) } return }
// newServer returns a new dcrd server configured to listen on addr for the // decred network type specified by chainParams. Use start to begin accepting // connections from peers. func newServer(listenAddrs []string, database database.Db, tmdb *stake.TicketDB, chainParams *chaincfg.Params) (*server, error) { nonce, err := wire.RandomUint64() if err != nil { return nil, err } amgr := addrmgr.New(cfg.DataDir, dcrdLookup) var listeners []net.Listener var nat NAT if !cfg.DisableListen { ipv4Addrs, ipv6Addrs, wildcard, err := parseListeners(listenAddrs) if err != nil { return nil, err } listeners = make([]net.Listener, 0, len(ipv4Addrs)+len(ipv6Addrs)) discover := true if len(cfg.ExternalIPs) != 0 { discover = false // if this fails we have real issues. port, _ := strconv.ParseUint( activeNetParams.DefaultPort, 10, 16) for _, sip := range cfg.ExternalIPs { eport := uint16(port) host, portstr, err := net.SplitHostPort(sip) if err != nil { // no port, use default. host = sip } else { port, err := strconv.ParseUint( portstr, 10, 16) if err != nil { srvrLog.Warnf("Can not parse "+ "port from %s for "+ "externalip: %v", sip, err) continue } eport = uint16(port) } na, err := amgr.HostToNetAddress(host, eport, wire.SFNodeNetwork) if err != nil { srvrLog.Warnf("Not adding %s as "+ "externalip: %v", sip, err) continue } err = amgr.AddLocalAddress(na, addrmgr.ManualPrio) if err != nil { amgrLog.Warnf("Skipping specified external IP: %v", err) } } } else if discover && cfg.Upnp { nat, err = Discover() if err != nil { srvrLog.Warnf("Can't discover upnp: %v", err) } // nil nat here is fine, just means no upnp on network. } // TODO(oga) nonstandard port... if wildcard { port, err := strconv.ParseUint(activeNetParams.DefaultPort, 10, 16) if err != nil { // I can't think of a cleaner way to do this... goto nowc } addrs, err := net.InterfaceAddrs() for _, a := range addrs { ip, _, err := net.ParseCIDR(a.String()) if err != nil { continue } na := wire.NewNetAddressIPPort(ip, uint16(port), wire.SFNodeNetwork) if discover { err = amgr.AddLocalAddress(na, addrmgr.InterfacePrio) if err != nil { amgrLog.Debugf("Skipping local address: %v", err) } } } } nowc: for _, addr := range ipv4Addrs { listener, err := net.Listen("tcp4", addr) if err != nil { srvrLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) if discover { if na, err := amgr.DeserializeNetAddress(addr); err == nil { err = amgr.AddLocalAddress(na, addrmgr.BoundPrio) if err != nil { amgrLog.Warnf("Skipping bound address: %v", err) } } } } for _, addr := range ipv6Addrs { listener, err := net.Listen("tcp6", addr) if err != nil { srvrLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) if discover { if na, err := amgr.DeserializeNetAddress(addr); err == nil { err = amgr.AddLocalAddress(na, addrmgr.BoundPrio) if err != nil { amgrLog.Debugf("Skipping bound address: %v", err) } } } } if len(listeners) == 0 { return nil, errors.New("no valid listen address") } } s := server{ nonce: nonce, listeners: listeners, chainParams: chainParams, addrManager: amgr, newPeers: make(chan *peer, cfg.MaxPeers), donePeers: make(chan *peer, cfg.MaxPeers), banPeers: make(chan *peer, cfg.MaxPeers), wakeup: make(chan struct{}), query: make(chan interface{}), relayInv: make(chan relayMsg, cfg.MaxPeers), broadcast: make(chan broadcastMsg, cfg.MaxPeers), quit: make(chan struct{}), modifyRebroadcastInv: make(chan interface{}), peerHeightsUpdate: make(chan updatePeerHeightsMsg), nat: nat, db: database, tmdb: tmdb, timeSource: blockchain.NewMedianTime(), } bm, err := newBlockManager(&s) if err != nil { return nil, err } s.blockManager = bm s.txMemPool = newTxMemPool(&s) s.cpuMiner = newCPUMiner(&s) if !cfg.NoAddrIndex { ai, err := newAddrIndexer(&s) if err != nil { return nil, err } s.addrIndexer = ai } if !cfg.DisableRPC { s.rpcServer, err = newRPCServer(cfg.RPCListeners, &s) if err != nil { return nil, err } } return &s, nil }