// writeMessage sends a bitcoin Message to the peer with logging. func (p *peer) writeMessage(msg btcwire.Message) { // Don't do anything if we're disconnecting. if atomic.LoadInt32(&p.disconnect) != 0 { return } log.Debugf("[PEER] Sending command [%v] to %s", msg.Command(), p.addr) // Use closures to log expensive operations so they are only run when the // logging level requires it. log.Tracef("%v", newLogClosure(func() string { return "[PEER] msg" + spew.Sdump(msg) })) log.Tracef("%v", newLogClosure(func() string { var buf bytes.Buffer err := btcwire.WriteMessage(&buf, msg, p.protocolVersion, p.btcnet) if err != nil { return err.Error() } return "[PEER] " + spew.Sdump(buf.Bytes()) })) // Write the message to the peer. err := btcwire.WriteMessage(p.conn, msg, p.protocolVersion, p.btcnet) if err != nil { p.Disconnect() p.logError("[PEER] Can't send message: %v", err) return } }
// TestWriteMessageWireErrors performs negative tests against wire encoding from // concrete messages to confirm error paths work correctly. func TestWriteMessageWireErrors(t *testing.T) { pver := btcwire.ProtocolVersion btcnet := btcwire.MainNet btcwireErr := &btcwire.MessageError{} // Fake message with a command that is too long. badCommandMsg := &fakeMessage{command: "somethingtoolong"} // Fake message with a problem during encoding encodeErrMsg := &fakeMessage{forceEncodeErr: true} // Fake message that has a payload which exceed max. exceedPayload := make([]byte, btcwire.MaxMessagePayload+1) exceedPayloadErrMsg := &fakeMessage{payload: exceedPayload} bogusPayload := []byte{0x01, 0x02, 0x03, 0x04} bogusMsg := &fakeMessage{command: "bogus", payload: bogusPayload} tests := []struct { msg btcwire.Message // Message to encode pver uint32 // Protocol version for wire encoding btcnet btcwire.BitcoinNet // Bitcoin network for wire encoding max int // Max size of fixed buffer to induce errors err error // Expected error }{ // Command too long. {badCommandMsg, pver, btcnet, 0, btcwireErr}, // Force error in payload encode. {encodeErrMsg, pver, btcnet, 0, btcwireErr}, // Force error due to exceeding max payload size. {exceedPayloadErrMsg, pver, btcnet, 0, btcwireErr}, // Force error in header write. {bogusMsg, pver, btcnet, 0, io.ErrShortWrite}, // Force error in payload write. {bogusMsg, pver, btcnet, 24, io.ErrShortWrite}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode wire format. w := newFixedWriter(test.max) err := btcwire.WriteMessage(w, test.msg, test.pver, test.btcnet) if reflect.TypeOf(err) != reflect.TypeOf(test.err) { t.Errorf("WriteMessage #%d wrong error got: %v <%T>, "+ "want: %T", i, err, err, test.err) continue } // For errors which are not of type btcwire.MessageError, check // them for equality. if _, ok := err.(*btcwire.MessageError); !ok { if err != test.err { t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+ "want: %v <%T>", i, err, err, test.err, test.err) continue } } } }
// Creates the reading and writing interfaces used to send and recieve messages // from a Bitcoin peer. inSetup indicates the verbosity of the failure mode. func composeConnOuts(params connParams) (func() btcwire.Message, func(btcwire.Message)) { conn := params.conn logger := params.logger failmsg := "The Bitcoin server has reached its max peers and does not want to talk" read := func() btcwire.Message { msg, _, err := btcwire.ReadMessage(conn, params.pver, params.btcnet) // a Bitcoin node will typically send a TCP RST if it doesn't want to talk if params.insetup && err != nil { logger.Printf(failmsg) } if err != nil { logger.Fatal(err) } return msg } write := func(msg btcwire.Message) { err := btcwire.WriteMessage(conn, msg, params.pver, params.btcnet) if params.insetup && err != nil { logger.Printf(failmsg) } if err != nil { logger.Fatal(err) } } return read, write }
// writeMessage sends a bitcoin Message to the peer with logging. func (p *peer) writeMessage(msg btcwire.Message) { // Don't do anything if we're disconnecting. if atomic.LoadInt32(&p.disconnect) != 0 { return } if !p.versionKnown { switch msg.(type) { case *btcwire.MsgVersion: // This is OK. default: // We drop all messages other than version if we // haven't done the handshake already. return } } // Use closures to log expensive operations so they are only run when // the logging level requires it. log.Debugf("%v", newLogClosure(func() string { // Debug summary of message. summary := messageSummary(msg) if len(summary) > 0 { summary = " (" + summary + ")" } return fmt.Sprintf("PEER: Sending %v%s to %s", msg.Command(), summary, p.addr) })) log.Tracef("%v", newLogClosure(func() string { return "PEER: " + spew.Sdump(msg) })) log.Tracef("%v", newLogClosure(func() string { var buf bytes.Buffer err := btcwire.WriteMessage(&buf, msg, p.protocolVersion, p.btcnet) if err != nil { return err.Error() } return "PEER: " + spew.Sdump(buf.Bytes()) })) // Write the message to the peer. err := btcwire.WriteMessage(p.conn, msg, p.protocolVersion, p.btcnet) if err != nil { p.Disconnect() p.logError("PEER: Can't send message: %v", err) return } }
func (peer *Peer) WriteMessage(msg btcwire.Message) { err := btcwire.WriteMessage(peer.conn, msg, peer.version, peer.btcnet) if err != nil { fmt.Println("Error writing message %v", err) return } }
func composeWrite(threadLog func(LogLevel, string), pver uint32) func(net.Conn, btc.Message) { // creates a write function with logging in a closure return func(conn net.Conn, msg btc.Message) { err := btc.WriteMessage(conn, msg, pver, btcnet) if err != nil { threadLog(Info, err.Error()) } } }
// 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 } } }