// 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 := btcwire.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 } } }
// TestPongCrossProtocol tests the MsgPong API when encoding with the latest // protocol version and decoded with BIP0031Version. func TestPongCrossProtocol(t *testing.T) { nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("Error generating nonce: %v", err) } msg := btcwire.NewMsgPong(nonce) if msg.Nonce != nonce { t.Errorf("Should get same nonce back out.") } // Encode with latest protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, btcwire.ProtocolVersion) if err != nil { t.Errorf("encode of MsgPong failed %v err <%v>", msg, err) } // Decode with old protocol version. readmsg := btcwire.NewMsgPong(0) err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) if err == nil { t.Errorf("encode of MsgPong succeeded when it shouldn't have %v", msg) } // Since one of the protocol versions doesn't support the pong message, // make sure the nonce didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Error("Should not get same nonce for cross protocol") } }
// TestPingCrossProtocol tests the MsgPing API when encoding with the latest // protocol version and decoding with BIP0031Version. func TestPingCrossProtocol(t *testing.T) { nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := btcwire.NewMsgPing(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Encode with latest protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, btcwire.ProtocolVersion) if err != nil { t.Errorf("encode of MsgPing failed %v err <%v>", msg, err) } // Decode with old protocol version. readmsg := btcwire.NewMsgPing(0) err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) if err != nil { t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err) } // Since one of the protocol versions doesn't support the nonce, make // sure it didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Error("Should not get same nonce for cross protocol") } }
// TestBlockHeader tests the BlockHeader API. func TestBlockHeader(t *testing.T) { nonce64, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } nonce := uint32(nonce64) hash := btcwire.GenesisHash merkleHash := btcwire.GenesisMerkleRoot bits := uint32(0x1d00ffff) bh := btcwire.NewBlockHeader(&hash, &merkleHash, bits, nonce) // 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.Bits != bits { 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) } }
// TestPing tests the MsgPing API against the latest protocol version. func TestPing(t *testing.T) { pver := btcwire.ProtocolVersion // Ensure we get the same nonce back out. nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := btcwire.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 }
// handlePing implements the ping command. func handlePing(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { // Ask server to ping \o_ nonce, err := btcwire.RandomUint64() if err != nil { return nil, fmt.Errorf("Not sending ping - can not generate "+ "nonce: %v", err) } s.server.BroadcastMessage(btcwire.NewMsgPing(nonce)) return nil, nil }
// newServer returns a new btcd server configured to listen on addr for the // bitcoin network type specified in btcnet. Use start to begin accepting // connections from peers. func newServer(addr string, db btcdb.Db, btcnet btcwire.BitcoinNet) (*server, error) { nonce, err := btcwire.RandomUint64() if err != nil { return nil, err } var listeners []net.Listener if !cfg.DisableListen { // IPv4 listener. listener4, err := net.Listen("tcp4", addr) if err != nil { return nil, err } listeners = append(listeners, listener4) // IPv6 listener. listener6, err := net.Listen("tcp6", addr) if err != nil { return nil, err } listeners = append(listeners, listener6) } s := server{ nonce: nonce, listeners: listeners, btcnet: btcnet, addrManager: NewAddrManager(), newPeers: make(chan *peer, cfg.MaxPeers), donePeers: make(chan *peer, cfg.MaxPeers), banPeers: make(chan *peer, cfg.MaxPeers), wakeup: make(chan bool), query: make(chan interface{}), relayInv: make(chan *btcwire.InvVect, cfg.MaxPeers), broadcast: make(chan broadcastMsg, cfg.MaxPeers), quit: make(chan bool), db: db, } bm, err := newBlockManager(&s) if err != nil { return nil, err } s.blockManager = bm s.txMemPool = newTxMemPool(&s) if !cfg.DisableRPC { s.rpcServer, err = newRPCServer(&s) if err != nil { return nil, err } } return &s, nil }
// TestPongLatest tests the MsgPong API against the latest protocol version. func TestPongLatest(t *testing.T) { pver := btcwire.ProtocolVersion nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } msg := btcwire.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 := btcwire.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 }
// TestPingBIP0031 tests the MsgPing API against the protocol version // BIP0031Version. func TestPingBIP0031(t *testing.T) { // Use the protocol version just prior to BIP0031Version changes. pver := btcwire.BIP0031Version nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := btcwire.NewMsgPing(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Ensure max payload is expected value for old protocol version. wantPayload := uint32(0) 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 old protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, pver) if err != nil { t.Errorf("encode of MsgPing failed %v err <%v>", msg, err) } // Test decode with old protocol version. readmsg := btcwire.NewMsgPing(0) err = readmsg.BtcDecode(&buf, pver) if err != nil { t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err) } // Since this protocol version doesn't support the nonce, make sure // it didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Errorf("Should not get same nonce for protocol version %d", pver) } return }
// TestPongBIP0031 tests the MsgPong API against the protocol version // BIP0031Version. func TestPongBIP0031(t *testing.T) { // Use the protocol version just prior to BIP0031Version changes. pver := btcwire.BIP0031Version nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("Error generating nonce: %v", err) } msg := btcwire.NewMsgPong(nonce) if msg.Nonce != nonce { t.Errorf("Should get same nonce back out.") } // Ensure max payload is expected value for old protocol version. size := msg.MaxPayloadLength(pver) if size != 0 { t.Errorf("Max length should be 0 for pong protocol version %d.", pver) } // Test encode with old protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, pver) if err == nil { t.Errorf("encode of MsgPong succeeded when it shouldn't have %v", msg) } // Test decode with old protocol version. readmsg := btcwire.NewMsgPong(0) err = readmsg.BtcDecode(&buf, pver) if err == nil { t.Errorf("decode of MsgPong succeeded when it shouldn't have", spew.Sdump(buf)) } // Since this protocol version doesn't support pong, make sure the // nonce didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Errorf("Should not get same nonce for protocol version %d", pver) } return }
// outHandler handles all outgoing messages for the peer. It must be run as a // goroutine. It uses a buffered channel to serialize output messages while // allowing the sender to continue running asynchronously. func (p *peer) outHandler() { pingTimer := time.AfterFunc(pingTimeoutMinutes*time.Minute, func() { nonce, err := btcwire.RandomUint64() if err != nil { peerLog.Errorf("Not sending ping on timeout to %s: %v", p, err) return } p.QueueMessage(btcwire.NewMsgPing(nonce), nil) }) out: for { select { case msg := <-p.sendQueue: // If the message is one we should get a reply for // then reset the timer, we only want to send pings // when otherwise we would not recieve a reply from // the peer. We specifically do not count block or inv // messages here since they are not sure of a reply if // the inv is of no interest explicitly solicited invs // should elicit a reply but we don't track them // specially. peerLog.Tracef("%s: recieved from queuehandler", p) reset := true switch msg.msg.(type) { case *btcwire.MsgVersion: // should get an ack case *btcwire.MsgGetAddr: // should get addresses case *btcwire.MsgPing: // expects pong case *btcwire.MsgMemPool: // Should return an inv. case *btcwire.MsgGetData: // Should get us block, tx, or not found. case *btcwire.MsgGetHeaders: // Should get us headers back. default: // Not one of the above, no sure reply. // We want to ping if nothing else // interesting happens. reset = false } if reset { pingTimer.Reset(pingTimeoutMinutes * time.Minute) } p.writeMessage(msg.msg) p.lastSend = time.Now() if msg.doneChan != nil { msg.doneChan <- true } peerLog.Tracef("%s: acking queuehandler", p) p.sendDoneQueue <- true peerLog.Tracef("%s: acked queuehandler", p) case <-p.quit: break out } } pingTimer.Stop() p.queueWg.Wait() // Drain any wait channels before we go away so we don't leave something // waiting for us. We have waited on queueWg and thus we can be sure // that we will not miss anything sent on sendQueue. cleanup: for { select { case msg := <-p.sendQueue: if msg.doneChan != nil { msg.doneChan <- false } // no need to send on sendDoneQueue since queueHandler // has been waited on and already exited. default: break cleanup } } peerLog.Tracef("Peer output handler done for %s", p.addr) }
// 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 *btcwire.MsgBlock, blockHeight int64, ticker *time.Ticker, quit chan struct{}) bool { // Choose a random extra nonce offset for this block template and // worker. enOffset, err := btcwire.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 := btcchain.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++ { // 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, extraNonce+enOffset) // 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 best block // has changed. bestHash, _ := m.server.blockManager.chainState.Best() if !header.PrevBlock.IsEqual(bestHash) { return false } // The current block is stale if the memory pool // has been updated since the block template was // generated and it has been at least one // minute. if lastTxUpdate != m.server.txMemPool.LastUpdated() && time.Now().After(lastGenerated.Add(time.Minute)) { return false } UpdateBlockTime(msgBlock, m.server.blockManager) default: // Non-blocking select to fall through } // Update the nonce and hash the block header. Each // hash is actually a double sha256 (two hashes), so // increment the number of hashes completed for each // attempt accordingly. header.Nonce = i hash, _ := header.BlockSha() hashesCompleted += 2 // The block is solved when the new block hash is less // than the target difficulty. Yay! if btcchain.ShaHashToBig(&hash).Cmp(targetDifficulty) <= 0 { m.updateHashes <- hashesCompleted return true } } } return false }
// outHandler handles all outgoing messages for the peer. It must be run as a // goroutine. It uses a buffered channel to serialize output messages while // allowing the sender to continue running asynchronously. func (p *peer) outHandler() { trickleTicker := time.NewTicker(time.Second * 10) pingTimer := time.AfterFunc(pingTimeoutMinutes*time.Minute, func() { nonce, err := btcwire.RandomUint64() if err != nil { log.Errorf("Not sending ping on timeout to %s: %v", p, err) return } p.QueueMessage(btcwire.NewMsgPing(nonce), nil) }) out: for { select { case msg := <-p.outputQueue: // If the message is one we should get a reply for // then reset the timer, we only want to send pings // when otherwise we would not recieve a reply from // the peer. We specifically do not count block or inv // messages here since they are not sure of a reply if // the inv is of no interest explicitly solicited invs // should elicit a reply but we don't track them // specially. reset := true switch msg.msg.(type) { case *btcwire.MsgVersion: // should get an ack case *btcwire.MsgGetAddr: // should get addresses case *btcwire.MsgPing: // expects pong case *btcwire.MsgMemPool: // Should return an inv. case *btcwire.MsgGetData: // Should get us block, tx, or not found. case *btcwire.MsgGetHeaders: // Should get us headers back. default: // Not one of the above, no sure reply. // We want to ping if nothing else // interesting happens. reset = false } if reset { pingTimer.Reset(pingTimeoutMinutes * time.Minute) } p.writeMessage(msg.msg) p.lastSend = time.Now() if msg.doneChan != nil { msg.doneChan <- true } case iv := <-p.outputInvChan: // No handshake? They'll find out soon enough. if p.versionKnown { p.invSendQueue.PushBack(iv) } case <-trickleTicker.C: // Don't send anything if we're disconnecting or there // is no queued inventory. if atomic.LoadInt32(&p.disconnect) != 0 || p.invSendQueue.Len() == 0 || !p.versionKnown { continue } // Create and send as many inv messages as needed to // drain the inventory send queue. invMsg := btcwire.NewMsgInv() for e := p.invSendQueue.Front(); e != nil; e = p.invSendQueue.Front() { iv := p.invSendQueue.Remove(e).(*btcwire.InvVect) // Don't send inventory that became known after // the initial check. if p.isKnownInventory(iv) { continue } invMsg.AddInvVect(iv) if len(invMsg.InvList) >= maxInvTrickleSize { p.writeMessage(invMsg) invMsg = btcwire.NewMsgInv() } // Add the inventory that is being relayed to // the known inventory for the peer. p.addKnownInventory(iv) } if len(invMsg.InvList) > 0 { p.writeMessage(invMsg) } case <-p.quit: break out } } pingTimer.Stop() // Drain any wait channels before we go away so we don't leave something // waiting for us. cleanup: for { select { case msg := <-p.outputQueue: if msg.doneChan != nil { msg.doneChan <- false } default: break cleanup } } log.Tracef("PEER: Peer output handler done for %s", p.addr) }
// newServer returns a new btcd server configured to listen on addr for the // bitcoin network type specified in btcnet. Use start to begin accepting // connections from peers. func newServer(listenAddrs []string, db btcdb.Db, btcnet btcwire.BitcoinNet) (*server, error) { nonce, err := btcwire.RandomUint64() if err != nil { return nil, err } amgr := NewAddrManager() 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.listenPort, 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 := hostToNetAddress(host, eport, btcwire.SFNodeNetwork) if err != nil { srvrLog.Warnf("Not adding %s as "+ "externalip: %v", sip, err) continue } amgr.addLocalAddress(na, ManualPrio) } } 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.listenPort, 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 := btcwire.NewNetAddressIPPort(ip, uint16(port), btcwire.SFNodeNetwork) if discover { amgr.addLocalAddress(na, InterfacePrio) } } } 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 := deserialiseNetAddress(addr); err == nil { amgr.addLocalAddress(na, BoundPrio) } } } 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 := deserialiseNetAddress(addr); err == nil { amgr.addLocalAddress(na, BoundPrio) } } } if len(listeners) == 0 { return nil, errors.New("No valid listen address") } } s := server{ nonce: nonce, listeners: listeners, btcnet: btcnet, addrManager: amgr, newPeers: make(chan *peer, cfg.MaxPeers), donePeers: make(chan *peer, cfg.MaxPeers), banPeers: make(chan *peer, cfg.MaxPeers), wakeup: make(chan bool), query: make(chan interface{}), relayInv: make(chan *btcwire.InvVect, cfg.MaxPeers), broadcast: make(chan broadcastMsg, cfg.MaxPeers), quit: make(chan bool), nat: nat, db: db, } bm, err := newBlockManager(&s) if err != nil { return nil, err } s.blockManager = bm s.txMemPool = newTxMemPool(&s) if !cfg.DisableRPC { s.rpcServer, err = newRPCServer(cfg.RPCListeners, &s) if err != nil { return nil, err } } return &s, nil }
// TestVersion tests the MsgVersion API. func TestVersion(t *testing.T) { pver := btcwire.ProtocolVersion // Create version message data. userAgent := "/btcdtest:0.0.1/" lastBlock := int32(234234) tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} me, err := btcwire.NewNetAddress(tcpAddrMe, btcwire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} you, err := btcwire.NewNetAddress(tcpAddrYou, btcwire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } // Ensure we get the correct data back out. msg := btcwire.NewMsgVersion(me, you, nonce, userAgent, 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 != userAgent { t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", msg.UserAgent, userAgent) } if msg.LastBlock != lastBlock { t.Errorf("NewMsgVersion: wrong last block - got %v, want %v", msg.LastBlock, lastBlock) } // 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(btcwire.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. wantPayload := uint32(2101) 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(btcwire.SFNodeNetwork) if msg.Services != btcwire.SFNodeNetwork { t.Errorf("AddService: wrong services - got %v, want %v", msg.Services, btcwire.SFNodeNetwork) } if !msg.HasService(btcwire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service not set") } // Use a fake connection. conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou} msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, 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 = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock) if err != btcwire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, btcwire.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 = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock) if err != btcwire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, btcwire.ErrInvalidNetAddr) } return }
func genNonce() uint64 { n, _ := btc.RandomUint64() return n }
// TestVersion tests the MsgVersion API. func TestVersion(t *testing.T) { pver := btcwire.ProtocolVersion // Create version message data. lastBlock := int32(234234) tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} me, err := btcwire.NewNetAddress(tcpAddrMe, btcwire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} you, err := btcwire.NewNetAddress(tcpAddrYou, btcwire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } nonce, err := btcwire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } // Ensure we get the correct data back out. msg := btcwire.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 != btcwire.DefaultUserAgent { t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", msg.UserAgent, btcwire.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 := btcwire.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", btcwire.MaxUserAgentLen-len(customUserAgent)-2+1), "") if _, ok := err.(*btcwire.MessageError); !ok { t.Errorf("AddUserAgent: expected error not received "+ "- got %v, want %T", err, btcwire.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(btcwire.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(btcwire.SFNodeNetwork) if msg.Services != btcwire.SFNodeNetwork { t.Errorf("AddService: wrong services - got %v, want %v", msg.Services, btcwire.SFNodeNetwork) } if !msg.HasService(btcwire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service not set") } // Use a fake connection. conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou} msg, err = btcwire.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 = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != btcwire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, btcwire.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 = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != btcwire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, btcwire.ErrInvalidNetAddr) } return }