// 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") } }
// 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 }
// handlePingMsg is invoked when a peer receives a ping bitcoin message. For // recent clients (protocol version > BIP0031Version), it replies with a pong // message. For older clients, it does nothing and anything other than failure // is considered a successful ping. func (p *peer) handlePingMsg(msg *btcwire.MsgPing) { // Only Reply with pong is message comes from a new enough client. if p.protocolVersion > btcwire.BIP0031Version { // Include nonce from ping so pong can be identified. p.QueueMessage(btcwire.NewMsgPong(msg.Nonce), nil) } }
// 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 }
func connHandler(id int, outAddrs chan<- []*btc.NetAddress, outNode chan<- Node, inAddr <-chan *btc.NetAddress) { // A worker that deals with the connection to a single bitcoin node. // It writes the list of nodes reported by node into out. // It also writes a valid node into outNode. // It reads from inAddr everytime it closes a connection for { addr := <-inAddr strA := addressFmt(*addr) threadLog := func(reported LogLevel, msg string) { if reported <= Level { logger.Printf("[%d] %s: %s\n", id, strA, msg) } } connProtoVer := btc.ProtocolVersion write := composeWrite(threadLog, connProtoVer) conn, err := net.DialTimeout("tcp", strA, time.Millisecond*500) if err != nil { threadLog(Log, err.Error()) continue } threadLog(Info, "Connected") ver_m, _ := btc.NewMsgVersionFromConn(conn, genNonce(), 0) ver_m.AddUserAgent("btcmonitor", "0.0.1") write(conn, ver_m) // We are looking for successful addr messages wins := 0 // After 10 seconds we just close the conn and handle errors time.AfterFunc(time.Second*10, func() { conn.Close() }) MessageLoop: for { var resp btc.Message resp, _, err := btc.ReadMessage(conn, connProtoVer, btcnet) if err != nil { threadLog(Log, err.Error()) break MessageLoop } threadLog(Info, resp.Command()) switch resp := resp.(type) { case *btc.MsgVersion: nodePVer := uint32(resp.ProtocolVersion) if nodePVer < connProtoVer { connProtoVer = nodePVer write = composeWrite(threadLog, connProtoVer) } node := convNode(*addr, *resp) outNode <- node verack := btc.NewMsgVerAck() write(conn, verack) getAddr := btc.NewMsgGetAddr() write(conn, getAddr) case *btc.MsgAddr: wins += 1 addrs := resp.AddrList outAddrs <- addrs if wins == 3 { break MessageLoop } case *btc.MsgPing: nonce := resp.Nonce pong := btc.NewMsgPong(nonce) write(conn, pong) } } } }
// TestMessage tests the Read/WriteMessage API. func TestMessage(t *testing.T) { pver := btcwire.ProtocolVersion // Create the various types of messages to test. // MsgVersion. addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} you, err := btcwire.NewNetAddress(addrYou, btcwire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } you.Timestamp = time.Time{} // Version message has zero value timestamp. addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} me, err := btcwire.NewNetAddress(addrMe, btcwire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } me.Timestamp = time.Time{} // Version message has zero value timestamp. msgVersion := btcwire.NewMsgVersion(me, you, 123123, "/test:0.0.1/", 0) msgVerack := btcwire.NewMsgVerAck() msgGetAddr := btcwire.NewMsgGetAddr() msgAddr := btcwire.NewMsgAddr() msgGetBlocks := btcwire.NewMsgGetBlocks(&btcwire.ShaHash{}) msgBlock := &blockOne msgInv := btcwire.NewMsgInv() msgGetData := btcwire.NewMsgGetData() msgNotFound := btcwire.NewMsgNotFound() msgTx := btcwire.NewMsgTx() msgPing := btcwire.NewMsgPing(123123) msgPong := btcwire.NewMsgPong(123123) msgGetHeaders := btcwire.NewMsgGetHeaders() msgHeaders := btcwire.NewMsgHeaders() msgAlert := btcwire.NewMsgAlert("payload", "signature") msgMemPool := btcwire.NewMsgMemPool() tests := []struct { in btcwire.Message // Value to encode out btcwire.Message // Expected decoded value pver uint32 // Protocol version for wire encoding btcnet btcwire.BitcoinNet // Network to use for wire encoding }{ {msgVersion, msgVersion, pver, btcwire.MainNet}, {msgVerack, msgVerack, pver, btcwire.MainNet}, {msgGetAddr, msgGetAddr, pver, btcwire.MainNet}, {msgAddr, msgAddr, pver, btcwire.MainNet}, {msgGetBlocks, msgGetBlocks, pver, btcwire.MainNet}, {msgBlock, msgBlock, pver, btcwire.MainNet}, {msgInv, msgInv, pver, btcwire.MainNet}, {msgGetData, msgGetData, pver, btcwire.MainNet}, {msgNotFound, msgNotFound, pver, btcwire.MainNet}, {msgTx, msgTx, pver, btcwire.MainNet}, {msgPing, msgPing, pver, btcwire.MainNet}, {msgPong, msgPong, pver, btcwire.MainNet}, {msgGetHeaders, msgGetHeaders, pver, btcwire.MainNet}, {msgHeaders, msgHeaders, pver, btcwire.MainNet}, {msgAlert, msgAlert, pver, btcwire.MainNet}, {msgMemPool, msgMemPool, pver, btcwire.MainNet}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. var buf bytes.Buffer err := btcwire.WriteMessage(&buf, test.in, test.pver, test.btcnet) if err != nil { t.Errorf("WriteMessage #%d error %v", i, err) continue } // Decode from wire format. rbuf := bytes.NewBuffer(buf.Bytes()) msg, _, err := btcwire.ReadMessage(rbuf, test.pver, test.btcnet) if err != nil { t.Errorf("ReadMessage #%d error %v, msg %v", i, err, spew.Sdump(msg)) continue } if !reflect.DeepEqual(msg, test.out) { t.Errorf("ReadMessage #%d\n got: %v want: %v", i, spew.Sdump(msg), spew.Sdump(test.out)) continue } } }
// TestPongWireErrors performs negative tests against wire encode and decode // of MsgPong to confirm error paths work correctly. func TestPongWireErrors(t *testing.T) { pver := btcwire.ProtocolVersion pverNoPong := btcwire.BIP0031Version btcwireErr := &btcwire.MessageError{} basePong := btcwire.NewMsgPong(123123) // 0x1e0f3 basePongEncoded := []byte{ 0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, } tests := []struct { in *btcwire.MsgPong // Value to encode buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Latest protocol version with intentional read/write errors. // Force error in nonce. {basePong, basePongEncoded, pver, 0, io.ErrShortWrite, io.EOF}, // Force error due to unsupported protocol version. {basePong, basePongEncoded, pverNoPong, 4, btcwireErr, btcwireErr}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. w := newFixedWriter(test.max) err := test.in.BtcEncode(w, test.pver) if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // For errors which are not of type btcwire.MessageError, check // them for equality. if _, ok := err.(*btcwire.MessageError); !ok { if err != test.writeErr { t.Errorf("BtcEncode #%d wrong error got: %v, "+ "want: %v", i, err, test.writeErr) continue } } // Decode from wire format. var msg btcwire.MsgPong r := newFixedReader(test.max, test.buf) err = msg.BtcDecode(r, test.pver) if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } // For errors which are not of type btcwire.MessageError, check // them for equality. if _, ok := err.(*btcwire.MessageError); !ok { if err != test.readErr { t.Errorf("BtcDecode #%d wrong error got: %v, "+ "want: %v", i, err, test.readErr) continue } } } }
func nodeHandler(cfg TowerCfg, txStream chan<- *TxMeta, blockStream chan<- *btcwire.MsgBlock) { conn := setupConn(cfg.Addr, cfg.Logger) connparams := connParams{ conn: conn, pver: btcwire.ProtocolVersion, btcnet: cfg.Net, logger: cfg.Logger, insetup: true, } read, write := composeConnOuts(connparams) // Initial handshake ver_m, _ := btcwire.NewMsgVersionFromConn(conn, genNonce(), int32(cfg.StartHeight)) ver_m.AddUserAgent("Watchtower", "0.0.0") write(ver_m) // Wait for responses acked, responded := false, false for { var msg btcwire.Message msg = read() cfg.Logger.Println(msg.Command()) switch msg := msg.(type) { case *btcwire.MsgVersion: responded = true ack := btcwire.NewMsgVerAck() nodeVer := uint32(msg.ProtocolVersion) if nodeVer < connparams.pver { connparams.pver = nodeVer read, write = composeConnOuts(connparams) } write(ack) case *btcwire.MsgVerAck: acked = true } if responded && acked { break } } // We are through the initial handshake, assume functional channel from here // on out. If there any errors with the pipe logger.Fatal gets called. connparams.insetup = false read, write = composeConnOuts(connparams) cfg.Logger.Println("Conn Negotiated") for { // If there are messages to send to peers send them. Otherwise, listen! select { case msg := <-cfg.MsgChan: write(msg) default: // listen for txs + blocks then push them into the appropriatestreams msg := read() switch msg := msg.(type) { case *btcwire.MsgInv: want := btcwire.NewMsgGetData() invVec := msg.InvList for i := range invVec { chunk := invVec[i] want.AddInvVect(chunk) } write(want) case *btcwire.MsgTx: var empt []byte // evaluates to nil meta := TxMeta{MsgTx: msg, BlockSha: empt, Time: time.Now()} txStream <- &meta case *btcwire.MsgBlock: blockStream <- msg case *btcwire.MsgPing: pong := btcwire.NewMsgPong(msg.Nonce) // More fun than anything... write(pong) } } } }