Example #1
0
// 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")
	}
}
Example #2
0
// 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
}
Example #3
0
// 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
}
Example #4
0
// 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
}
Example #5
0
// 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)
}
Example #6
0
// 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
		}
	}
}
Example #7
0
// 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)
}