Add vendor dir (#16) (#4)

* Add vendor dir so builds dont require dep

* Pin specific version go-eth version
This commit is contained in:
Matt K
2018-01-29 13:44:18 -06:00
committed by GitHub
parent 82119b3c4b
commit 293dd2e848
4319 changed files with 1448696 additions and 26 deletions
+112
View File
@@ -0,0 +1,112 @@
wire
====
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/wire)
Package wire implements the bitcoin wire protocol. A comprehensive suite of
tests with 100% test coverage is provided to ensure proper functionality.
There is an associated blog post about the release of this package
[here](https://blog.conformal.com/btcwire-the-bitcoin-wire-protocol-package-from-btcd/).
This package has intentionally been designed so it can be used as a standalone
package for any projects needing to interface with bitcoin peers at the wire
protocol level.
## Installation and Updating
```bash
$ go get -u github.com/btcsuite/btcd/wire
```
## Bitcoin Message Overview
The bitcoin protocol consists of exchanging messages between peers. Each message
is preceded by a header which identifies information about it such as which
bitcoin network it is a part of, its type, how big it is, and a checksum to
verify validity. All encoding and decoding of message headers is handled by this
package.
To accomplish this, there is a generic interface for bitcoin messages named
`Message` which allows messages of any type to be read, written, or passed
around through channels, functions, etc. In addition, concrete implementations
of most of the currently supported bitcoin messages are provided. For these
supported messages, all of the details of marshalling and unmarshalling to and
from the wire using bitcoin encoding are handled so the caller doesn't have to
concern themselves with the specifics.
## Reading Messages Example
In order to unmarshal bitcoin messages from the wire, use the `ReadMessage`
function. It accepts any `io.Reader`, but typically this will be a `net.Conn`
to a remote node running a bitcoin peer. Example syntax is:
```Go
// Use the most recent protocol version supported by the package and the
// main bitcoin network.
pver := wire.ProtocolVersion
btcnet := wire.MainNet
// Reads and validates the next bitcoin message from conn using the
// protocol version pver and the bitcoin network btcnet. The returns
// are a wire.Message, a []byte which contains the unmarshalled
// raw payload, and a possible error.
msg, rawPayload, err := wire.ReadMessage(conn, pver, btcnet)
if err != nil {
// Log and handle the error
}
```
See the package documentation for details on determining the message type.
## Writing Messages Example
In order to marshal bitcoin messages to the wire, use the `WriteMessage`
function. It accepts any `io.Writer`, but typically this will be a `net.Conn`
to a remote node running a bitcoin peer. Example syntax to request addresses
from a remote peer is:
```Go
// Use the most recent protocol version supported by the package and the
// main bitcoin network.
pver := wire.ProtocolVersion
btcnet := wire.MainNet
// Create a new getaddr bitcoin message.
msg := wire.NewMsgGetAddr()
// Writes a bitcoin message msg to conn using the protocol version
// pver, and the bitcoin network btcnet. The return is a possible
// error.
err := wire.WriteMessage(conn, msg, pver, btcnet)
if err != nil {
// Log and handle the error
}
```
## GPG Verification Key
All official release tags are signed by Conformal so users can ensure the code
has not been tampered with and is coming from the btcsuite developers. To
verify the signature perform the following:
- Download the public key from the Conformal website at
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
- Import the public key into your GPG keyring:
```bash
gpg --import GIT-GPG-KEY-conformal.txt
```
- Verify the release tag with the following command where `TAG_NAME` is a
placeholder for the specific tag:
```bash
git tag -v TAG_NAME
```
## License
Package wire is licensed under the [copyfree](http://copyfree.org) ISC
License.
+634
View File
@@ -0,0 +1,634 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"compress/bzip2"
"fmt"
"io/ioutil"
"net"
"os"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
// the main network, regression test network, and test network (version 3).
var genesisCoinbaseTx = MsgTx{
Version: 1,
TxIn: []*TxIn{
{
PreviousOutPoint: OutPoint{
Hash: chainhash.Hash{},
Index: 0xffffffff,
},
SignatureScript: []byte{
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */
0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */
0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */
0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, /* |ancellor| */
0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, 0x6e, /* | on brin| */
0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x63, /* |k of sec|*/
0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6c, /* |ond bail| */
0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, /* |out for |*/
0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */
},
Sequence: 0xffffffff,
},
},
TxOut: []*TxOut{
{
Value: 0x12a05f200,
PkScript: []byte{
0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */
0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */
0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */
0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */
0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */
0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */
0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */
0x1d, 0x5f, 0xac, /* |._.| */
},
},
},
LockTime: 0,
}
// BenchmarkWriteVarInt1 performs a benchmark on how long it takes to write
// a single byte variable length integer.
func BenchmarkWriteVarInt1(b *testing.B) {
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 1)
}
}
// BenchmarkWriteVarInt3 performs a benchmark on how long it takes to write
// a three byte variable length integer.
func BenchmarkWriteVarInt3(b *testing.B) {
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 65535)
}
}
// BenchmarkWriteVarInt5 performs a benchmark on how long it takes to write
// a five byte variable length integer.
func BenchmarkWriteVarInt5(b *testing.B) {
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 4294967295)
}
}
// BenchmarkWriteVarInt9 performs a benchmark on how long it takes to write
// a nine byte variable length integer.
func BenchmarkWriteVarInt9(b *testing.B) {
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 18446744073709551615)
}
}
// BenchmarkReadVarInt1 performs a benchmark on how long it takes to read
// a single byte variable length integer.
func BenchmarkReadVarInt1(b *testing.B) {
buf := []byte{0x01}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarInt(r, 0)
}
}
// BenchmarkReadVarInt3 performs a benchmark on how long it takes to read
// a three byte variable length integer.
func BenchmarkReadVarInt3(b *testing.B) {
buf := []byte{0x0fd, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarInt(r, 0)
}
}
// BenchmarkReadVarInt5 performs a benchmark on how long it takes to read
// a five byte variable length integer.
func BenchmarkReadVarInt5(b *testing.B) {
buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarInt(r, 0)
}
}
// BenchmarkReadVarInt9 performs a benchmark on how long it takes to read
// a nine byte variable length integer.
func BenchmarkReadVarInt9(b *testing.B) {
buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarInt(r, 0)
}
}
// BenchmarkReadVarStr4 performs a benchmark on how long it takes to read a
// four byte variable length string.
func BenchmarkReadVarStr4(b *testing.B) {
buf := []byte{0x04, 't', 'e', 's', 't'}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarString(r, 0)
}
}
// BenchmarkReadVarStr10 performs a benchmark on how long it takes to read a
// ten byte variable length string.
func BenchmarkReadVarStr10(b *testing.B) {
buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarString(r, 0)
}
}
// BenchmarkWriteVarStr4 performs a benchmark on how long it takes to write a
// four byte variable length string.
func BenchmarkWriteVarStr4(b *testing.B) {
for i := 0; i < b.N; i++ {
WriteVarString(ioutil.Discard, 0, "test")
}
}
// BenchmarkWriteVarStr10 performs a benchmark on how long it takes to write a
// ten byte variable length string.
func BenchmarkWriteVarStr10(b *testing.B) {
for i := 0; i < b.N; i++ {
WriteVarString(ioutil.Discard, 0, "test012345")
}
}
// BenchmarkReadOutPoint performs a benchmark on how long it takes to read a
// transaction output point.
func BenchmarkReadOutPoint(b *testing.B) {
buf := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
0xff, 0xff, 0xff, 0xff, // Previous output index
}
r := bytes.NewReader(buf)
var op OutPoint
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readOutPoint(r, 0, 0, &op)
}
}
// BenchmarkWriteOutPoint performs a benchmark on how long it takes to write a
// transaction output point.
func BenchmarkWriteOutPoint(b *testing.B) {
op := &OutPoint{
Hash: chainhash.Hash{},
Index: 0,
}
for i := 0; i < b.N; i++ {
writeOutPoint(ioutil.Discard, 0, 0, op)
}
}
// BenchmarkReadTxOut performs a benchmark on how long it takes to read a
// transaction output.
func BenchmarkReadTxOut(b *testing.B) {
buf := []byte{
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
0xee, // 65-byte signature
0xac, // OP_CHECKSIG
}
r := bytes.NewReader(buf)
var txOut TxOut
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readTxOut(r, 0, 0, &txOut)
scriptPool.Return(txOut.PkScript)
}
}
// BenchmarkWriteTxOut performs a benchmark on how long it takes to write
// a transaction output.
func BenchmarkWriteTxOut(b *testing.B) {
txOut := blockOne.Transactions[0].TxOut[0]
for i := 0; i < b.N; i++ {
WriteTxOut(ioutil.Discard, 0, 0, txOut)
}
}
// BenchmarkReadTxIn performs a benchmark on how long it takes to read a
// transaction input.
func BenchmarkReadTxIn(b *testing.B) {
buf := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
0xff, 0xff, 0xff, 0xff, // Previous output index
0x07, // Varint for length of signature script
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script
0xff, 0xff, 0xff, 0xff, // Sequence
}
r := bytes.NewReader(buf)
var txIn TxIn
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readTxIn(r, 0, 0, &txIn)
scriptPool.Return(txIn.SignatureScript)
}
}
// BenchmarkWriteTxIn performs a benchmark on how long it takes to write
// a transaction input.
func BenchmarkWriteTxIn(b *testing.B) {
txIn := blockOne.Transactions[0].TxIn[0]
for i := 0; i < b.N; i++ {
writeTxIn(ioutil.Discard, 0, 0, txIn)
}
}
// BenchmarkDeserializeTx performs a benchmark on how long it takes to
// deserialize a small transaction.
func BenchmarkDeserializeTxSmall(b *testing.B) {
buf := []byte{
0x01, 0x00, 0x00, 0x00, // Version
0x01, // Varint for number of input transactions
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // // Previous output hash
0xff, 0xff, 0xff, 0xff, // Prevous output index
0x07, // Varint for length of signature script
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script
0xff, 0xff, 0xff, 0xff, // Sequence
0x01, // Varint for number of output transactions
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
0xee, // 65-byte signature
0xac, // OP_CHECKSIG
0x00, 0x00, 0x00, 0x00, // Lock time
}
r := bytes.NewReader(buf)
var tx MsgTx
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
tx.Deserialize(r)
}
}
// BenchmarkDeserializeTxLarge performs a benchmark on how long it takes to
// deserialize a very large transaction.
func BenchmarkDeserializeTxLarge(b *testing.B) {
// tx bb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e08
// from the main block chain.
fi, err := os.Open("testdata/megatx.bin.bz2")
if err != nil {
b.Fatalf("Failed to read transaction data: %v", err)
}
defer fi.Close()
buf, err := ioutil.ReadAll(bzip2.NewReader(fi))
if err != nil {
b.Fatalf("Failed to read transaction data: %v", err)
}
r := bytes.NewReader(buf)
var tx MsgTx
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
tx.Deserialize(r)
}
}
// BenchmarkSerializeTx performs a benchmark on how long it takes to serialize
// a transaction.
func BenchmarkSerializeTx(b *testing.B) {
tx := blockOne.Transactions[0]
for i := 0; i < b.N; i++ {
tx.Serialize(ioutil.Discard)
}
}
// BenchmarkReadBlockHeader performs a benchmark on how long it takes to
// deserialize a block header.
func BenchmarkReadBlockHeader(b *testing.B) {
buf := []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot
0x29, 0xab, 0x5f, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0xf3, 0xe0, 0x01, 0x00, // Nonce
0x00, // TxnCount Varint
}
r := bytes.NewReader(buf)
var header BlockHeader
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readBlockHeader(r, 0, &header)
}
}
// BenchmarkWriteBlockHeader performs a benchmark on how long it takes to
// serialize a block header.
func BenchmarkWriteBlockHeader(b *testing.B) {
header := blockOne.Header
for i := 0; i < b.N; i++ {
writeBlockHeader(ioutil.Discard, 0, &header)
}
}
// BenchmarkDecodeGetHeaders performs a benchmark on how long it takes to
// decode a getheaders message with the maximum number of block locator hashes.
func BenchmarkDecodeGetHeaders(b *testing.B) {
// Create a message with the maximum number of block locators.
pver := ProtocolVersion
var m MsgGetHeaders
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.AddBlockLocatorHash(hash)
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgGetHeaders.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgGetHeaders
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkDecodeHeaders performs a benchmark on how long it takes to
// decode a headers message with the maximum number of headers.
func BenchmarkDecodeHeaders(b *testing.B) {
// Create a message with the maximum number of headers.
pver := ProtocolVersion
var m MsgHeaders
for i := 0; i < MaxBlockHeadersPerMsg; i++ {
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.AddBlockHeader(NewBlockHeader(1, hash, hash, 0, uint32(i)))
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgHeaders.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgHeaders
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkDecodeGetBlocks performs a benchmark on how long it takes to
// decode a getblocks message with the maximum number of block locator hashes.
func BenchmarkDecodeGetBlocks(b *testing.B) {
// Create a message with the maximum number of block locators.
pver := ProtocolVersion
var m MsgGetBlocks
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.AddBlockLocatorHash(hash)
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgGetBlocks.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgGetBlocks
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkDecodeAddr performs a benchmark on how long it takes to decode an
// addr message with the maximum number of addresses.
func BenchmarkDecodeAddr(b *testing.B) {
// Create a message with the maximum number of addresses.
pver := ProtocolVersion
ip := net.ParseIP("127.0.0.1")
ma := NewMsgAddr()
for port := uint16(0); port < MaxAddrPerMsg; port++ {
ma.AddAddress(NewNetAddressIPPort(ip, port, SFNodeNetwork))
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := ma.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgAddr.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgAddr
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkDecodeInv performs a benchmark on how long it takes to decode an inv
// message with the maximum number of entries.
func BenchmarkDecodeInv(b *testing.B) {
// Create a message with the maximum number of entries.
pver := ProtocolVersion
var m MsgInv
for i := 0; i < MaxInvPerMsg; i++ {
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.AddInvVect(NewInvVect(InvTypeBlock, hash))
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgInv.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgInv
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkDecodeNotFound performs a benchmark on how long it takes to decode
// a notfound message with the maximum number of entries.
func BenchmarkDecodeNotFound(b *testing.B) {
// Create a message with the maximum number of entries.
pver := ProtocolVersion
var m MsgNotFound
for i := 0; i < MaxInvPerMsg; i++ {
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.AddInvVect(NewInvVect(InvTypeBlock, hash))
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgNotFound.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgNotFound
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkDecodeMerkleBlock performs a benchmark on how long it takes to
// decode a reasonably sized merkleblock message.
func BenchmarkDecodeMerkleBlock(b *testing.B) {
// Create a message with random data.
pver := ProtocolVersion
var m MsgMerkleBlock
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", 10000))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.Header = *NewBlockHeader(1, hash, hash, 0, uint32(10000))
for i := 0; i < 105; i++ {
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
if err != nil {
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
}
m.AddTxHash(hash)
if i%8 == 0 {
m.Flags = append(m.Flags, uint8(i))
}
}
// Serialize it so the bytes are available to test the decode below.
var bb bytes.Buffer
if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil {
b.Fatalf("MsgMerkleBlock.BtcEncode: unexpected error: %v", err)
}
buf := bb.Bytes()
r := bytes.NewReader(buf)
var msg MsgMerkleBlock
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
msg.BtcDecode(r, pver, LatestEncoding)
}
}
// BenchmarkTxHash performs a benchmark on how long it takes to hash a
// transaction.
func BenchmarkTxHash(b *testing.B) {
for i := 0; i < b.N; i++ {
genesisCoinbaseTx.TxHash()
}
}
// BenchmarkDoubleHashB performs a benchmark on how long it takes to perform a
// double hash returning a byte slice.
func BenchmarkDoubleHashB(b *testing.B) {
var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err)
return
}
txBytes := buf.Bytes()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = chainhash.DoubleHashB(txBytes)
}
}
// BenchmarkDoubleHashH performs a benchmark on how long it takes to perform
// a double hash returning a chainhash.Hash.
func BenchmarkDoubleHashH(b *testing.B) {
var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err)
return
}
txBytes := buf.Bytes()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = chainhash.DoubleHashH(txBytes)
}
}
+128
View File
@@ -0,0 +1,128 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// MaxBlockHeaderPayload is the maximum number of bytes a block header can be.
// Version 4 bytes + Timestamp 4 bytes + Bits 4 bytes + Nonce 4 bytes +
// PrevBlock and MerkleRoot hashes.
const MaxBlockHeaderPayload = 16 + (chainhash.HashSize * 2)
// BlockHeader defines information about a block and is used in the bitcoin
// block (MsgBlock) and headers (MsgHeaders) messages.
type BlockHeader struct {
// Version of the block. This is not the same as the protocol version.
Version int32
// Hash of the previous block in the block chain.
PrevBlock chainhash.Hash
// Merkle tree reference to hash of all transactions for the block.
MerkleRoot chainhash.Hash
// Time the block was created. This is, unfortunately, encoded as a
// uint32 on the wire and therefore is limited to 2106.
Timestamp time.Time
// Difficulty target for the block.
Bits uint32
// Nonce used to generate the block.
Nonce uint32
}
// blockHeaderLen is a constant that represents the number of bytes for a block
// header.
const blockHeaderLen = 80
// BlockHash computes the block identifier hash for the given block header.
func (h *BlockHeader) BlockHash() chainhash.Hash {
// Encode the header and double sha256 everything prior to the number of
// transactions. Ignore the error returns since there is no way the
// encode could fail except being out of memory which would cause a
// run-time panic.
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
_ = writeBlockHeader(buf, 0, h)
return chainhash.DoubleHashH(buf.Bytes())
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
// See Deserialize for decoding block headers stored to disk, such as in a
// database, as opposed to decoding block headers from the wire.
func (h *BlockHeader) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
return readBlockHeader(r, pver, h)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
// See Serialize for encoding block headers to be stored to disk, such as in a
// database, as opposed to encoding block headers for the wire.
func (h *BlockHeader) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
return writeBlockHeader(w, pver, h)
}
// Deserialize decodes a block header from r into the receiver using a format
// that is suitable for long-term storage such as a database while respecting
// the Version field.
func (h *BlockHeader) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of readBlockHeader.
return readBlockHeader(r, 0, h)
}
// Serialize encodes a block header from r into the receiver using a format
// that is suitable for long-term storage such as a database while respecting
// the Version field.
func (h *BlockHeader) Serialize(w io.Writer) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of writeBlockHeader.
return writeBlockHeader(w, 0, h)
}
// NewBlockHeader returns a new BlockHeader using the provided version, previous
// block hash, merkle root hash, difficulty bits, and nonce used to generate the
// block with defaults for the remaining fields.
func NewBlockHeader(version int32, prevHash, merkleRootHash *chainhash.Hash,
bits uint32, nonce uint32) *BlockHeader {
// Limit the timestamp to one second precision since the protocol
// doesn't support better.
return &BlockHeader{
Version: version,
PrevBlock: *prevHash,
MerkleRoot: *merkleRootHash,
Timestamp: time.Unix(time.Now().Unix(), 0),
Bits: bits,
Nonce: nonce,
}
}
// readBlockHeader reads a bitcoin block header from r. See Deserialize for
// decoding block headers stored to disk, such as in a database, as opposed to
// decoding from the wire.
func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
return readElements(r, &bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
(*uint32Time)(&bh.Timestamp), &bh.Bits, &bh.Nonce)
}
// writeBlockHeader writes a bitcoin block header to w. See Serialize for
// encoding block headers to be stored to disk, such as in a database, as
// opposed to encoding for the wire.
func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
sec := uint32(bh.Timestamp.Unix())
return writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
sec, bh.Bits, bh.Nonce)
}
+261
View File
@@ -0,0 +1,261 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
)
// TestBlockHeader tests the BlockHeader API.
func TestBlockHeader(t *testing.T) {
nonce64, err := RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
nonce := uint32(nonce64)
hash := mainNetGenesisHash
merkleHash := mainNetGenesisMerkleRoot
bits := uint32(0x1d00ffff)
bh := NewBlockHeader(1, &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)
}
}
// TestBlockHeaderWire tests the BlockHeader wire encode and decode for various
// protocol versions.
func TestBlockHeaderWire(t *testing.T) {
nonce := uint32(123123) // 0x1e0f3
pver := uint32(70001)
// baseBlockHdr is used in the various tests as a baseline BlockHeader.
bits := uint32(0x1d00ffff)
baseBlockHdr := &BlockHeader{
Version: 1,
PrevBlock: mainNetGenesisHash,
MerkleRoot: mainNetGenesisMerkleRoot,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Bits: bits,
Nonce: nonce,
}
// baseBlockHdrEncoded is the wire encoded bytes of baseBlockHdr.
baseBlockHdrEncoded := []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot
0x29, 0xab, 0x5f, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0xf3, 0xe0, 0x01, 0x00, // Nonce
}
tests := []struct {
in *BlockHeader // Data to encode
out *BlockHeader // Expected decoded data
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding variant to use
}{
// Latest protocol version.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := writeBlockHeader(&buf, test.pver, test.in)
if err != nil {
t.Errorf("writeBlockHeader #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("writeBlockHeader #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
buf.Reset()
err = test.in.BtcEncode(&buf, pver, 0)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the block header from wire format.
var bh BlockHeader
rbuf := bytes.NewReader(test.buf)
err = readBlockHeader(rbuf, test.pver, &bh)
if err != nil {
t.Errorf("readBlockHeader #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&bh, test.out) {
t.Errorf("readBlockHeader #%d\n got: %s want: %s", i,
spew.Sdump(&bh), spew.Sdump(test.out))
continue
}
rbuf = bytes.NewReader(test.buf)
err = bh.BtcDecode(rbuf, pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&bh, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(&bh), spew.Sdump(test.out))
continue
}
}
}
// TestBlockHeaderSerialize tests BlockHeader serialize and deserialize.
func TestBlockHeaderSerialize(t *testing.T) {
nonce := uint32(123123) // 0x1e0f3
// baseBlockHdr is used in the various tests as a baseline BlockHeader.
bits := uint32(0x1d00ffff)
baseBlockHdr := &BlockHeader{
Version: 1,
PrevBlock: mainNetGenesisHash,
MerkleRoot: mainNetGenesisMerkleRoot,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Bits: bits,
Nonce: nonce,
}
// baseBlockHdrEncoded is the wire encoded bytes of baseBlockHdr.
baseBlockHdrEncoded := []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot
0x29, 0xab, 0x5f, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0xf3, 0xe0, 0x01, 0x00, // Nonce
}
tests := []struct {
in *BlockHeader // Data to encode
out *BlockHeader // Expected decoded data
buf []byte // Serialized data
}{
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the block header.
var buf bytes.Buffer
err := test.in.Serialize(&buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("Serialize #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Deserialize the block header.
var bh BlockHeader
rbuf := bytes.NewReader(test.buf)
err = bh.Deserialize(rbuf)
if err != nil {
t.Errorf("Deserialize #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&bh, test.out) {
t.Errorf("Deserialize #%d\n got: %s want: %s", i,
spew.Sdump(&bh), spew.Sdump(test.out))
continue
}
}
}
+689
View File
@@ -0,0 +1,689 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"math"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
const (
// MaxVarIntPayload is the maximum payload size for a variable length integer.
MaxVarIntPayload = 9
// binaryFreeListMaxItems is the number of buffers to keep in the free
// list to use for binary serialization and deserialization.
binaryFreeListMaxItems = 1024
)
var (
// littleEndian is a convenience variable since binary.LittleEndian is
// quite long.
littleEndian = binary.LittleEndian
// bigEndian is a convenience variable since binary.BigEndian is quite
// long.
bigEndian = binary.BigEndian
)
// binaryFreeList defines a concurrent safe free list of byte slices (up to the
// maximum number defined by the binaryFreeListMaxItems constant) that have a
// cap of 8 (thus it supports up to a uint64). It is used to provide temporary
// buffers for serializing and deserializing primitive numbers to and from their
// binary encoding in order to greatly reduce the number of allocations
// required.
//
// For convenience, functions are provided for each of the primitive unsigned
// integers that automatically obtain a buffer from the free list, perform the
// necessary binary conversion, read from or write to the given io.Reader or
// io.Writer, and return the buffer to the free list.
type binaryFreeList chan []byte
// Borrow returns a byte slice from the free list with a length of 8. A new
// buffer is allocated if there are not any available on the free list.
func (l binaryFreeList) Borrow() []byte {
var buf []byte
select {
case buf = <-l:
default:
buf = make([]byte, 8)
}
return buf[:8]
}
// Return puts the provided byte slice back on the free list. The buffer MUST
// have been obtained via the Borrow function and therefore have a cap of 8.
func (l binaryFreeList) Return(buf []byte) {
select {
case l <- buf:
default:
// Let it go to the garbage collector.
}
}
// Uint8 reads a single byte from the provided reader using a buffer from the
// free list and returns it as a uint8.
func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
buf := l.Borrow()[:1]
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := buf[0]
l.Return(buf)
return rv, nil
}
// Uint16 reads two bytes from the provided reader using a buffer from the
// free list, converts it to a number using the provided byte order, and returns
// the resulting uint16.
func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) {
buf := l.Borrow()[:2]
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := byteOrder.Uint16(buf)
l.Return(buf)
return rv, nil
}
// Uint32 reads four bytes from the provided reader using a buffer from the
// free list, converts it to a number using the provided byte order, and returns
// the resulting uint32.
func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
buf := l.Borrow()[:4]
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := byteOrder.Uint32(buf)
l.Return(buf)
return rv, nil
}
// Uint64 reads eight bytes from the provided reader using a buffer from the
// free list, converts it to a number using the provided byte order, and returns
// the resulting uint64.
func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) {
buf := l.Borrow()[:8]
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := byteOrder.Uint64(buf)
l.Return(buf)
return rv, nil
}
// PutUint8 copies the provided uint8 into a buffer from the free list and
// writes the resulting byte to the given writer.
func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
buf := l.Borrow()[:1]
buf[0] = val
_, err := w.Write(buf)
l.Return(buf)
return err
}
// PutUint16 serializes the provided uint16 using the given byte order into a
// buffer from the free list and writes the resulting two bytes to the given
// writer.
func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error {
buf := l.Borrow()[:2]
byteOrder.PutUint16(buf, val)
_, err := w.Write(buf)
l.Return(buf)
return err
}
// PutUint32 serializes the provided uint32 using the given byte order into a
// buffer from the free list and writes the resulting four bytes to the given
// writer.
func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error {
buf := l.Borrow()[:4]
byteOrder.PutUint32(buf, val)
_, err := w.Write(buf)
l.Return(buf)
return err
}
// PutUint64 serializes the provided uint64 using the given byte order into a
// buffer from the free list and writes the resulting eight bytes to the given
// writer.
func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error {
buf := l.Borrow()[:8]
byteOrder.PutUint64(buf, val)
_, err := w.Write(buf)
l.Return(buf)
return err
}
// binarySerializer provides a free list of buffers to use for serializing and
// deserializing primitive integer values to and from io.Readers and io.Writers.
var binarySerializer binaryFreeList = make(chan []byte, binaryFreeListMaxItems)
// errNonCanonicalVarInt is the common format string used for non-canonically
// encoded variable length integer errors.
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
"encode a value greater than %x"
// uint32Time represents a unix timestamp encoded with a uint32. It is used as
// a way to signal the readElement function how to decode a timestamp into a Go
// time.Time since it is otherwise ambiguous.
type uint32Time time.Time
// int64Time represents a unix timestamp encoded with an int64. It is used as
// a way to signal the readElement function how to decode a timestamp into a Go
// time.Time since it is otherwise ambiguous.
type int64Time time.Time
// readElement reads the next sequence of bytes from r using little endian
// depending on the concrete type of element pointed to.
func readElement(r io.Reader, element interface{}) error {
// Attempt to read the element based on the concrete type via fast
// type assertions first.
switch e := element.(type) {
case *int32:
rv, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
*e = int32(rv)
return nil
case *uint32:
rv, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
*e = rv
return nil
case *int64:
rv, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
*e = int64(rv)
return nil
case *uint64:
rv, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
*e = rv
return nil
case *bool:
rv, err := binarySerializer.Uint8(r)
if err != nil {
return err
}
if rv == 0x00 {
*e = false
} else {
*e = true
}
return nil
// Unix timestamp encoded as a uint32.
case *uint32Time:
rv, err := binarySerializer.Uint32(r, binary.LittleEndian)
if err != nil {
return err
}
*e = uint32Time(time.Unix(int64(rv), 0))
return nil
// Unix timestamp encoded as an int64.
case *int64Time:
rv, err := binarySerializer.Uint64(r, binary.LittleEndian)
if err != nil {
return err
}
*e = int64Time(time.Unix(int64(rv), 0))
return nil
// Message header checksum.
case *[4]byte:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil
// Message header command.
case *[CommandSize]uint8:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil
// IP address.
case *[16]byte:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil
case *chainhash.Hash:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil
case *ServiceFlag:
rv, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
*e = ServiceFlag(rv)
return nil
case *InvType:
rv, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
*e = InvType(rv)
return nil
case *BitcoinNet:
rv, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
*e = BitcoinNet(rv)
return nil
case *BloomUpdateType:
rv, err := binarySerializer.Uint8(r)
if err != nil {
return err
}
*e = BloomUpdateType(rv)
return nil
case *RejectCode:
rv, err := binarySerializer.Uint8(r)
if err != nil {
return err
}
*e = RejectCode(rv)
return nil
}
// Fall back to the slower binary.Read if a fast path was not available
// above.
return binary.Read(r, littleEndian, element)
}
// readElements reads multiple items from r. It is equivalent to multiple
// calls to readElement.
func readElements(r io.Reader, elements ...interface{}) error {
for _, element := range elements {
err := readElement(r, element)
if err != nil {
return err
}
}
return nil
}
// writeElement writes the little endian representation of element to w.
func writeElement(w io.Writer, element interface{}) error {
// Attempt to write the element based on the concrete type via fast
// type assertions first.
switch e := element.(type) {
case int32:
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
if err != nil {
return err
}
return nil
case uint32:
err := binarySerializer.PutUint32(w, littleEndian, e)
if err != nil {
return err
}
return nil
case int64:
err := binarySerializer.PutUint64(w, littleEndian, uint64(e))
if err != nil {
return err
}
return nil
case uint64:
err := binarySerializer.PutUint64(w, littleEndian, e)
if err != nil {
return err
}
return nil
case bool:
var err error
if e {
err = binarySerializer.PutUint8(w, 0x01)
} else {
err = binarySerializer.PutUint8(w, 0x00)
}
if err != nil {
return err
}
return nil
// Message header checksum.
case [4]byte:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil
// Message header command.
case [CommandSize]uint8:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil
// IP address.
case [16]byte:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil
case *chainhash.Hash:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil
case ServiceFlag:
err := binarySerializer.PutUint64(w, littleEndian, uint64(e))
if err != nil {
return err
}
return nil
case InvType:
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
if err != nil {
return err
}
return nil
case BitcoinNet:
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
if err != nil {
return err
}
return nil
case BloomUpdateType:
err := binarySerializer.PutUint8(w, uint8(e))
if err != nil {
return err
}
return nil
case RejectCode:
err := binarySerializer.PutUint8(w, uint8(e))
if err != nil {
return err
}
return nil
}
// Fall back to the slower binary.Write if a fast path was not available
// above.
return binary.Write(w, littleEndian, element)
}
// writeElements writes multiple items to w. It is equivalent to multiple
// calls to writeElement.
func writeElements(w io.Writer, elements ...interface{}) error {
for _, element := range elements {
err := writeElement(w, element)
if err != nil {
return err
}
}
return nil
}
// ReadVarInt reads a variable length integer from r and returns it as a uint64.
func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
discriminant, err := binarySerializer.Uint8(r)
if err != nil {
return 0, err
}
var rv uint64
switch discriminant {
case 0xff:
sv, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return 0, err
}
rv = sv
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
min := uint64(0x100000000)
if rv < min {
return 0, messageError("ReadVarInt", fmt.Sprintf(
errNonCanonicalVarInt, rv, discriminant, min))
}
case 0xfe:
sv, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
return 0, err
}
rv = uint64(sv)
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
min := uint64(0x10000)
if rv < min {
return 0, messageError("ReadVarInt", fmt.Sprintf(
errNonCanonicalVarInt, rv, discriminant, min))
}
case 0xfd:
sv, err := binarySerializer.Uint16(r, littleEndian)
if err != nil {
return 0, err
}
rv = uint64(sv)
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
min := uint64(0xfd)
if rv < min {
return 0, messageError("ReadVarInt", fmt.Sprintf(
errNonCanonicalVarInt, rv, discriminant, min))
}
default:
rv = uint64(discriminant)
}
return rv, nil
}
// WriteVarInt serializes val to w using a variable number of bytes depending
// on its value.
func WriteVarInt(w io.Writer, pver uint32, val uint64) error {
if val < 0xfd {
return binarySerializer.PutUint8(w, uint8(val))
}
if val <= math.MaxUint16 {
err := binarySerializer.PutUint8(w, 0xfd)
if err != nil {
return err
}
return binarySerializer.PutUint16(w, littleEndian, uint16(val))
}
if val <= math.MaxUint32 {
err := binarySerializer.PutUint8(w, 0xfe)
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, uint32(val))
}
err := binarySerializer.PutUint8(w, 0xff)
if err != nil {
return err
}
return binarySerializer.PutUint64(w, littleEndian, val)
}
// VarIntSerializeSize returns the number of bytes it would take to serialize
// val as a variable length integer.
func VarIntSerializeSize(val uint64) int {
// The value is small enough to be represented by itself, so it's
// just 1 byte.
if val < 0xfd {
return 1
}
// Discriminant 1 byte plus 2 bytes for the uint16.
if val <= math.MaxUint16 {
return 3
}
// Discriminant 1 byte plus 4 bytes for the uint32.
if val <= math.MaxUint32 {
return 5
}
// Discriminant 1 byte plus 8 bytes for the uint64.
return 9
}
// ReadVarString reads a variable length string from r and returns it as a Go
// string. A variable length string is encoded as a variable length integer
// containing the length of the string followed by the bytes that represent the
// string itself. An error is returned if the length is greater than the
// maximum block payload size since it helps protect against memory exhaustion
// attacks and forced panics through malformed messages.
func ReadVarString(r io.Reader, pver uint32) (string, error) {
count, err := ReadVarInt(r, pver)
if err != nil {
return "", err
}
// Prevent variable length strings that are larger than the maximum
// message size. It would be possible to cause memory exhaustion and
// panics without a sane upper bound on this count.
if count > MaxMessagePayload {
str := fmt.Sprintf("variable length string is too long "+
"[count %d, max %d]", count, MaxMessagePayload)
return "", messageError("ReadVarString", str)
}
buf := make([]byte, count)
_, err = io.ReadFull(r, buf)
if err != nil {
return "", err
}
return string(buf), nil
}
// WriteVarString serializes str to w as a variable length integer containing
// the length of the string followed by the bytes that represent the string
// itself.
func WriteVarString(w io.Writer, pver uint32, str string) error {
err := WriteVarInt(w, pver, uint64(len(str)))
if err != nil {
return err
}
_, err = w.Write([]byte(str))
return err
}
// ReadVarBytes reads a variable length byte array. A byte array is encoded
// as a varInt containing the length of the array followed by the bytes
// themselves. An error is returned if the length is greater than the
// passed maxAllowed parameter which helps protect against memory exhuastion
// attacks and forced panics thorugh malformed messages. The fieldName
// parameter is only used for the error message so it provides more context in
// the error.
func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
fieldName string) ([]byte, error) {
count, err := ReadVarInt(r, pver)
if err != nil {
return nil, err
}
// Prevent byte array larger than the max message size. It would
// be possible to cause memory exhaustion and panics without a sane
// upper bound on this count.
if count > uint64(maxAllowed) {
str := fmt.Sprintf("%s is larger than the max allowed size "+
"[count %d, max %d]", fieldName, count, maxAllowed)
return nil, messageError("ReadVarBytes", str)
}
b := make([]byte, count)
_, err = io.ReadFull(r, b)
if err != nil {
return nil, err
}
return b, nil
}
// WriteVarBytes serializes a variable length byte array to w as a varInt
// containing the number of bytes, followed by the bytes themselves.
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
slen := uint64(len(bytes))
err := WriteVarInt(w, pver, slen)
if err != nil {
return err
}
_, err = w.Write(bytes)
return err
}
// randomUint64 returns a cryptographically random uint64 value. This
// unexported version takes a reader primarily to ensure the error paths
// can be properly tested by passing a fake reader in the tests.
func randomUint64(r io.Reader) (uint64, error) {
rv, err := binarySerializer.Uint64(r, bigEndian)
if err != nil {
return 0, err
}
return rv, nil
}
// RandomUint64 returns a cryptographically random uint64 value.
func RandomUint64() (uint64, error) {
return randomUint64(rand.Reader)
}
+759
View File
@@ -0,0 +1,759 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"fmt"
"io"
"reflect"
"strings"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// mainNetGenesisHash is the hash of the first block in the block chain for the
// main network (genesis block).
var mainNetGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
})
// mainNetGenesisMerkleRoot is the hash of the first transaction in the genesis
// block for the main network.
var mainNetGenesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a,
})
// fakeRandReader implements the io.Reader interface and is used to force
// errors in the RandomUint64 function.
type fakeRandReader struct {
n int
err error
}
// Read returns the fake reader error and the lesser of the fake reader value
// and the length of p.
func (r *fakeRandReader) Read(p []byte) (int, error) {
n := r.n
if n > len(p) {
n = len(p)
}
return n, r.err
}
// TestElementWire tests wire encode and decode for various element types. This
// is mainly to test the "fast" paths in readElement and writeElement which use
// type assertions to avoid reflection when possible.
func TestElementWire(t *testing.T) {
type writeElementReflect int32
tests := []struct {
in interface{} // Value to encode
buf []byte // Wire encoding
}{
{int32(1), []byte{0x01, 0x00, 0x00, 0x00}},
{uint32(256), []byte{0x00, 0x01, 0x00, 0x00}},
{
int64(65536),
[]byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
},
{
uint64(4294967296),
[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
},
{
true,
[]byte{0x01},
},
{
false,
[]byte{0x00},
},
{
[4]byte{0x01, 0x02, 0x03, 0x04},
[]byte{0x01, 0x02, 0x03, 0x04},
},
{
[CommandSize]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c,
},
[]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c,
},
},
{
[16]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
},
[]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
},
},
{
(*chainhash.Hash)(&[chainhash.HashSize]byte{ // Make go vet happy.
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
}),
[]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
},
},
{
ServiceFlag(SFNodeNetwork),
[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
{
InvType(InvTypeTx),
[]byte{0x01, 0x00, 0x00, 0x00},
},
{
BitcoinNet(MainNet),
[]byte{0xf9, 0xbe, 0xb4, 0xd9},
},
// Type not supported by the "fast" path and requires reflection.
{
writeElementReflect(1),
[]byte{0x01, 0x00, 0x00, 0x00},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Write to wire format.
var buf bytes.Buffer
err := writeElement(&buf, test.in)
if err != nil {
t.Errorf("writeElement #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("writeElement #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Read from wire format.
rbuf := bytes.NewReader(test.buf)
val := test.in
if reflect.ValueOf(test.in).Kind() != reflect.Ptr {
val = reflect.New(reflect.TypeOf(test.in)).Interface()
}
err = readElement(rbuf, val)
if err != nil {
t.Errorf("readElement #%d error %v", i, err)
continue
}
ival := val
if reflect.ValueOf(test.in).Kind() != reflect.Ptr {
ival = reflect.Indirect(reflect.ValueOf(val)).Interface()
}
if !reflect.DeepEqual(ival, test.in) {
t.Errorf("readElement #%d\n got: %s want: %s", i,
spew.Sdump(ival), spew.Sdump(test.in))
continue
}
}
}
// TestElementWireErrors performs negative tests against wire encode and decode
// of various element types to confirm error paths work correctly.
func TestElementWireErrors(t *testing.T) {
tests := []struct {
in interface{} // Value to encode
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
{int32(1), 0, io.ErrShortWrite, io.EOF},
{uint32(256), 0, io.ErrShortWrite, io.EOF},
{int64(65536), 0, io.ErrShortWrite, io.EOF},
{true, 0, io.ErrShortWrite, io.EOF},
{[4]byte{0x01, 0x02, 0x03, 0x04}, 0, io.ErrShortWrite, io.EOF},
{
[CommandSize]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c,
},
0, io.ErrShortWrite, io.EOF,
},
{
[16]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
},
0, io.ErrShortWrite, io.EOF,
},
{
(*chainhash.Hash)(&[chainhash.HashSize]byte{ // Make go vet happy.
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
}),
0, io.ErrShortWrite, io.EOF,
},
{ServiceFlag(SFNodeNetwork), 0, io.ErrShortWrite, io.EOF},
{InvType(InvTypeTx), 0, io.ErrShortWrite, io.EOF},
{BitcoinNet(MainNet), 0, io.ErrShortWrite, io.EOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
w := newFixedWriter(test.max)
err := writeElement(w, test.in)
if err != test.writeErr {
t.Errorf("writeElement #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// Decode from wire format.
r := newFixedReader(test.max, nil)
val := test.in
if reflect.ValueOf(test.in).Kind() != reflect.Ptr {
val = reflect.New(reflect.TypeOf(test.in)).Interface()
}
err = readElement(r, val)
if err != test.readErr {
t.Errorf("readElement #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestVarIntWire tests wire encode and decode for variable length integers.
func TestVarIntWire(t *testing.T) {
pver := ProtocolVersion
tests := []struct {
in uint64 // Value to encode
out uint64 // Expected decoded value
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
// Single byte
{0, 0, []byte{0x00}, pver},
// Max single byte
{0xfc, 0xfc, []byte{0xfc}, pver},
// Min 2-byte
{0xfd, 0xfd, []byte{0xfd, 0x0fd, 0x00}, pver},
// Max 2-byte
{0xffff, 0xffff, []byte{0xfd, 0xff, 0xff}, pver},
// Min 4-byte
{0x10000, 0x10000, []byte{0xfe, 0x00, 0x00, 0x01, 0x00}, pver},
// Max 4-byte
{0xffffffff, 0xffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff}, pver},
// Min 8-byte
{
0x100000000, 0x100000000,
[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
pver,
},
// Max 8-byte
{
0xffffffffffffffff, 0xffffffffffffffff,
[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
pver,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := WriteVarInt(&buf, test.pver, test.in)
if err != nil {
t.Errorf("WriteVarInt #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("WriteVarInt #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
val, err := ReadVarInt(rbuf, test.pver)
if err != nil {
t.Errorf("ReadVarInt #%d error %v", i, err)
continue
}
if val != test.out {
t.Errorf("ReadVarInt #%d\n got: %d want: %d", i,
val, test.out)
continue
}
}
}
// TestVarIntWireErrors performs negative tests against wire encode and decode
// of variable length integers to confirm error paths work correctly.
func TestVarIntWireErrors(t *testing.T) {
pver := ProtocolVersion
tests := []struct {
in uint64 // 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
}{
// Force errors on discriminant.
{0, []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF},
// Force errors on 2-byte read/write.
{0xfd, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force errors on 4-byte read/write.
{0x10000, []byte{0xfe}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force errors on 8-byte read/write.
{0x100000000, []byte{0xff}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
w := newFixedWriter(test.max)
err := WriteVarInt(w, test.pver, test.in)
if err != test.writeErr {
t.Errorf("WriteVarInt #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// Decode from wire format.
r := newFixedReader(test.max, test.buf)
_, err = ReadVarInt(r, test.pver)
if err != test.readErr {
t.Errorf("ReadVarInt #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestVarIntNonCanonical ensures variable length integers that are not encoded
// canonically return the expected error.
func TestVarIntNonCanonical(t *testing.T) {
pver := ProtocolVersion
tests := []struct {
name string // Test name for easier identification
in []byte // Value to decode
pver uint32 // Protocol version for wire encoding
}{
{
"0 encoded with 3 bytes", []byte{0xfd, 0x00, 0x00},
pver,
},
{
"max single-byte value encoded with 3 bytes",
[]byte{0xfd, 0xfc, 0x00}, pver,
},
{
"0 encoded with 5 bytes",
[]byte{0xfe, 0x00, 0x00, 0x00, 0x00}, pver,
},
{
"max three-byte value encoded with 5 bytes",
[]byte{0xfe, 0xff, 0xff, 0x00, 0x00}, pver,
},
{
"0 encoded with 9 bytes",
[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
pver,
},
{
"max five-byte value encoded with 9 bytes",
[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00},
pver,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
rbuf := bytes.NewReader(test.in)
val, err := ReadVarInt(rbuf, test.pver)
if _, ok := err.(*MessageError); !ok {
t.Errorf("ReadVarInt #%d (%s) unexpected error %v", i,
test.name, err)
continue
}
if val != 0 {
t.Errorf("ReadVarInt #%d (%s)\n got: %d want: 0", i,
test.name, val)
continue
}
}
}
// TestVarIntWire tests the serialize size for variable length integers.
func TestVarIntSerializeSize(t *testing.T) {
tests := []struct {
val uint64 // Value to get the serialized size for
size int // Expected serialized size
}{
// Single byte
{0, 1},
// Max single byte
{0xfc, 1},
// Min 2-byte
{0xfd, 3},
// Max 2-byte
{0xffff, 3},
// Min 4-byte
{0x10000, 5},
// Max 4-byte
{0xffffffff, 5},
// Min 8-byte
{0x100000000, 9},
// Max 8-byte
{0xffffffffffffffff, 9},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
serializedSize := VarIntSerializeSize(test.val)
if serializedSize != test.size {
t.Errorf("VarIntSerializeSize #%d got: %d, want: %d", i,
serializedSize, test.size)
continue
}
}
}
// TestVarStringWire tests wire encode and decode for variable length strings.
func TestVarStringWire(t *testing.T) {
pver := ProtocolVersion
// str256 is a string that takes a 2-byte varint to encode.
str256 := strings.Repeat("test", 64)
tests := []struct {
in string // String to encode
out string // String to decoded value
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
// Empty string
{"", "", []byte{0x00}, pver},
// Single byte varint + string
{"Test", "Test", append([]byte{0x04}, []byte("Test")...), pver},
// 2-byte varint + string
{str256, str256, append([]byte{0xfd, 0x00, 0x01}, []byte(str256)...), pver},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := WriteVarString(&buf, test.pver, test.in)
if err != nil {
t.Errorf("WriteVarString #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("WriteVarString #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
val, err := ReadVarString(rbuf, test.pver)
if err != nil {
t.Errorf("ReadVarString #%d error %v", i, err)
continue
}
if val != test.out {
t.Errorf("ReadVarString #%d\n got: %s want: %s", i,
val, test.out)
continue
}
}
}
// TestVarStringWireErrors performs negative tests against wire encode and
// decode of variable length strings to confirm error paths work correctly.
func TestVarStringWireErrors(t *testing.T) {
pver := ProtocolVersion
// str256 is a string that takes a 2-byte varint to encode.
str256 := strings.Repeat("test", 64)
tests := []struct {
in string // 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 errors on empty string.
{"", []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF},
// Force error on single byte varint + string.
{"Test", []byte{0x04}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force errors on 2-byte varint + string.
{str256, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
w := newFixedWriter(test.max)
err := WriteVarString(w, test.pver, test.in)
if err != test.writeErr {
t.Errorf("WriteVarString #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// Decode from wire format.
r := newFixedReader(test.max, test.buf)
_, err = ReadVarString(r, test.pver)
if err != test.readErr {
t.Errorf("ReadVarString #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestVarStringOverflowErrors performs tests to ensure deserializing variable
// length strings intentionally crafted to use large values for the string
// length are handled properly. This could otherwise potentially be used as an
// attack vector.
func TestVarStringOverflowErrors(t *testing.T) {
pver := ProtocolVersion
tests := []struct {
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
err error // Expected error
}{
{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
pver, &MessageError{}},
{[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
pver, &MessageError{}},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
_, err := ReadVarString(rbuf, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("ReadVarString #%d wrong error got: %v, "+
"want: %v", i, err, reflect.TypeOf(test.err))
continue
}
}
}
// TestVarBytesWire tests wire encode and decode for variable length byte array.
func TestVarBytesWire(t *testing.T) {
pver := ProtocolVersion
// bytes256 is a byte array that takes a 2-byte varint to encode.
bytes256 := bytes.Repeat([]byte{0x01}, 256)
tests := []struct {
in []byte // Byte Array to write
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
// Empty byte array
{[]byte{}, []byte{0x00}, pver},
// Single byte varint + byte array
{[]byte{0x01}, []byte{0x01, 0x01}, pver},
// 2-byte varint + byte array
{bytes256, append([]byte{0xfd, 0x00, 0x01}, bytes256...), pver},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := WriteVarBytes(&buf, test.pver, test.in)
if err != nil {
t.Errorf("WriteVarBytes #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("WriteVarBytes #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
val, err := ReadVarBytes(rbuf, test.pver, MaxMessagePayload,
"test payload")
if err != nil {
t.Errorf("ReadVarBytes #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("ReadVarBytes #%d\n got: %s want: %s", i,
val, test.buf)
continue
}
}
}
// TestVarBytesWireErrors performs negative tests against wire encode and
// decode of variable length byte arrays to confirm error paths work correctly.
func TestVarBytesWireErrors(t *testing.T) {
pver := ProtocolVersion
// bytes256 is a byte array that takes a 2-byte varint to encode.
bytes256 := bytes.Repeat([]byte{0x01}, 256)
tests := []struct {
in []byte // Byte Array to write
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 errors on empty byte array.
{[]byte{}, []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF},
// Force error on single byte varint + byte array.
{[]byte{0x01, 0x02, 0x03}, []byte{0x04}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force errors on 2-byte varint + byte array.
{bytes256, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
w := newFixedWriter(test.max)
err := WriteVarBytes(w, test.pver, test.in)
if err != test.writeErr {
t.Errorf("WriteVarBytes #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// Decode from wire format.
r := newFixedReader(test.max, test.buf)
_, err = ReadVarBytes(r, test.pver, MaxMessagePayload,
"test payload")
if err != test.readErr {
t.Errorf("ReadVarBytes #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestVarBytesOverflowErrors performs tests to ensure deserializing variable
// length byte arrays intentionally crafted to use large values for the array
// length are handled properly. This could otherwise potentially be used as an
// attack vector.
func TestVarBytesOverflowErrors(t *testing.T) {
pver := ProtocolVersion
tests := []struct {
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
err error // Expected error
}{
{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
pver, &MessageError{}},
{[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
pver, &MessageError{}},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
_, err := ReadVarBytes(rbuf, test.pver, MaxMessagePayload,
"test payload")
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("ReadVarBytes #%d wrong error got: %v, "+
"want: %v", i, err, reflect.TypeOf(test.err))
continue
}
}
}
// 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 := 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
}
}
}
// TestRandomUint64Errors uses a fake reader to force error paths to be executed
// and checks the results accordingly.
func TestRandomUint64Errors(t *testing.T) {
// Test short reads.
fr := &fakeRandReader{n: 2, err: io.EOF}
nonce, err := randomUint64(fr)
if err != io.ErrUnexpectedEOF {
t.Errorf("Error not expected value of %v [%v]",
io.ErrUnexpectedEOF, err)
}
if nonce != 0 {
t.Errorf("Nonce is not 0 [%v]", nonce)
}
}
+162
View File
@@ -0,0 +1,162 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
Package wire implements the bitcoin wire protocol.
For the complete details of the bitcoin protocol, see the official wiki entry
at https://en.bitcoin.it/wiki/Protocol_specification. The following only serves
as a quick overview to provide information on how to use the package.
At a high level, this package provides support for marshalling and unmarshalling
supported bitcoin messages to and from the wire. This package does not deal
with the specifics of message handling such as what to do when a message is
received. This provides the caller with a high level of flexibility.
Bitcoin Message Overview
The bitcoin protocol consists of exchanging messages between peers. Each
message is preceded by a header which identifies information about it such as
which bitcoin network it is a part of, its type, how big it is, and a checksum
to verify validity. All encoding and decoding of message headers is handled by
this package.
To accomplish this, there is a generic interface for bitcoin messages named
Message which allows messages of any type to be read, written, or passed around
through channels, functions, etc. In addition, concrete implementations of most
of the currently supported bitcoin messages are provided. For these supported
messages, all of the details of marshalling and unmarshalling to and from the
wire using bitcoin encoding are handled so the caller doesn't have to concern
themselves with the specifics.
Message Interaction
The following provides a quick summary of how the bitcoin messages are intended
to interact with one another. As stated above, these interactions are not
directly handled by this package. For more in-depth details about the
appropriate interactions, see the official bitcoin protocol wiki entry at
https://en.bitcoin.it/wiki/Protocol_specification.
The initial handshake consists of two peers sending each other a version message
(MsgVersion) followed by responding with a verack message (MsgVerAck). Both
peers use the information in the version message (MsgVersion) to negotiate
things such as protocol version and supported services with each other. Once
the initial handshake is complete, the following chart indicates message
interactions in no particular order.
Peer A Sends Peer B Responds
----------------------------------------------------------------------------
getaddr message (MsgGetAddr) addr message (MsgAddr)
getblocks message (MsgGetBlocks) inv message (MsgInv)
inv message (MsgInv) getdata message (MsgGetData)
getdata message (MsgGetData) block message (MsgBlock) -or-
tx message (MsgTx) -or-
notfound message (MsgNotFound)
getheaders message (MsgGetHeaders) headers message (MsgHeaders)
ping message (MsgPing) pong message (MsgHeaders)* -or-
(none -- Ability to send message is enough)
NOTES:
* The pong message was not added until later protocol versions as defined
in BIP0031. The BIP0031Version constant can be used to detect a recent
enough protocol version for this purpose (version > BIP0031Version).
Common Parameters
There are several common parameters that arise when using this package to read
and write bitcoin messages. The following sections provide a quick overview of
these parameters so the next sections can build on them.
Protocol Version
The protocol version should be negotiated with the remote peer at a higher
level than this package via the version (MsgVersion) message exchange, however,
this package provides the wire.ProtocolVersion constant which indicates the
latest protocol version this package supports and is typically the value to use
for all outbound connections before a potentially lower protocol version is
negotiated.
Bitcoin Network
The bitcoin network is a magic number which is used to identify the start of a
message and which bitcoin network the message applies to. This package provides
the following constants:
wire.MainNet
wire.TestNet (Regression test network)
wire.TestNet3 (Test network version 3)
wire.SimNet (Simulation test network)
Determining Message Type
As discussed in the bitcoin message overview section, this package reads
and writes bitcoin messages using a generic interface named Message. In
order to determine the actual concrete type of the message, use a type
switch or type assertion. An example of a type switch follows:
// Assumes msg is already a valid concrete message such as one created
// via NewMsgVersion or read via ReadMessage.
switch msg := msg.(type) {
case *wire.MsgVersion:
// The message is a pointer to a MsgVersion struct.
fmt.Printf("Protocol version: %v", msg.ProtocolVersion)
case *wire.MsgBlock:
// The message is a pointer to a MsgBlock struct.
fmt.Printf("Number of tx in block: %v", msg.Header.TxnCount)
}
Reading Messages
In order to unmarshall bitcoin messages from the wire, use the ReadMessage
function. It accepts any io.Reader, but typically this will be a net.Conn to
a remote node running a bitcoin peer. Example syntax is:
// Reads and validates the next bitcoin message from conn using the
// protocol version pver and the bitcoin network btcnet. The returns
// are a wire.Message, a []byte which contains the unmarshalled
// raw payload, and a possible error.
msg, rawPayload, err := wire.ReadMessage(conn, pver, btcnet)
if err != nil {
// Log and handle the error
}
Writing Messages
In order to marshall bitcoin messages to the wire, use the WriteMessage
function. It accepts any io.Writer, but typically this will be a net.Conn to
a remote node running a bitcoin peer. Example syntax to request addresses
from a remote peer is:
// Create a new getaddr bitcoin message.
msg := wire.NewMsgGetAddr()
// Writes a bitcoin message msg to conn using the protocol version
// pver, and the bitcoin network btcnet. The return is a possible
// error.
err := wire.WriteMessage(conn, msg, pver, btcnet)
if err != nil {
// Log and handle the error
}
Errors
Errors returned by this package are either the raw errors provided by underlying
calls to read/write from streams such as io.EOF, io.ErrUnexpectedEOF, and
io.ErrShortWrite, or of type wire.MessageError. This allows the caller to
differentiate between general IO errors and malformed messages through type
assertions.
Bitcoin Improvement Proposals
This package includes spec changes outlined by the following BIPs:
BIP0014 (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki)
BIP0031 (https://github.com/bitcoin/bips/blob/master/bip-0031.mediawiki)
BIP0035 (https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki)
BIP0037 (https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki)
BIP0111 (https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki)
BIP0130 (https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki)
BIP0133 (https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki)
*/
package wire
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
)
// MessageError describes an issue with a message.
// An example of some potential issues are messages from the wrong bitcoin
// network, invalid commands, mismatched checksums, and exceeding max payloads.
//
// This provides a mechanism for the caller to type assert the error to
// differentiate between general io errors such as io.EOF and issues that
// resulted from malformed messages.
type MessageError struct {
Func string // Function name
Description string // Human readable description of the issue
}
// Error satisfies the error interface and prints human-readable errors.
func (e *MessageError) Error() string {
if e.Func != "" {
return fmt.Sprintf("%v: %v", e.Func, e.Description)
}
return e.Description
}
// messageError creates an error for the given function and description.
func messageError(f string, desc string) *MessageError {
return &MessageError{Func: f, Description: desc}
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import "io"
// fakeMessage implements the Message interface and is used to force encode
// errors in messages.
type fakeMessage struct {
command string
payload []byte
forceEncodeErr bool
forceLenErr bool
}
// BtcDecode doesn't do anything. It just satisfies the wire.Message
// interface.
func (msg *fakeMessage) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
return nil
}
// BtcEncode writes the payload field of the fake message or forces an error
// if the forceEncodeErr flag of the fake message is set. It also satisfies the
// wire.Message interface.
func (msg *fakeMessage) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if msg.forceEncodeErr {
err := &MessageError{
Func: "fakeMessage.BtcEncode",
Description: "intentional error",
}
return err
}
_, err := w.Write(msg.payload)
return err
}
// Command returns the command field of the fake message and satisfies the
// Message interface.
func (msg *fakeMessage) Command() string {
return msg.command
}
// MaxPayloadLength returns the length of the payload field of fake message
// or a smaller value if the forceLenErr flag of the fake message is set. It
// satisfies the Message interface.
func (msg *fakeMessage) MaxPayloadLength(pver uint32) uint32 {
lenp := uint32(len(msg.payload))
if msg.forceLenErr {
return lenp - 1
}
return lenp
}
+77
View File
@@ -0,0 +1,77 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
)
// fixedWriter implements the io.Writer interface and intentially allows
// testing of error paths by forcing short writes.
type fixedWriter struct {
b []byte
pos int
}
// Write writes the contents of p to w. When the contents of p would cause
// the writer to exceed the maximum allowed size of the fixed writer,
// io.ErrShortWrite is returned and the writer is left unchanged.
//
// This satisfies the io.Writer interface.
func (w *fixedWriter) Write(p []byte) (n int, err error) {
lenp := len(p)
if w.pos+lenp > cap(w.b) {
return 0, io.ErrShortWrite
}
n = lenp
w.pos += copy(w.b[w.pos:], p)
return
}
// Bytes returns the bytes already written to the fixed writer.
func (w *fixedWriter) Bytes() []byte {
return w.b
}
// newFixedWriter returns a new io.Writer that will error once more bytes than
// the specified max have been written.
func newFixedWriter(max int) io.Writer {
b := make([]byte, max)
fw := fixedWriter{b, 0}
return &fw
}
// fixedReader implements the io.Reader interface and intentially allows
// testing of error paths by forcing short reads.
type fixedReader struct {
buf []byte
pos int
iobuf *bytes.Buffer
}
// Read reads the next len(p) bytes from the fixed reader. When the number of
// bytes read would exceed the maximum number of allowed bytes to be read from
// the fixed writer, an error is returned.
//
// This satisfies the io.Reader interface.
func (fr *fixedReader) Read(p []byte) (n int, err error) {
n, err = fr.iobuf.Read(p)
fr.pos += n
return
}
// newFixedReader returns a new io.Reader that will error once more bytes than
// the specified max have been read.
func newFixedReader(max int, buf []byte) io.Reader {
b := make([]byte, max)
if buf != nil {
copy(b[:], buf)
}
iobuf := bytes.NewBuffer(b)
fr := fixedReader{b, 0, iobuf}
return &fr
}
+86
View File
@@ -0,0 +1,86 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
const (
// MaxInvPerMsg is the maximum number of inventory vectors that can be in a
// single bitcoin inv message.
MaxInvPerMsg = 50000
// Maximum payload size for an inventory vector.
maxInvVectPayload = 4 + chainhash.HashSize
// InvWitnessFlag denotes that the inventory vector type is requesting,
// or sending a version which includes witness data.
InvWitnessFlag = 1 << 30
)
// InvType represents the allowed types of inventory vectors. See InvVect.
type InvType uint32
// These constants define the various supported inventory vector types.
const (
InvTypeError InvType = 0
InvTypeTx InvType = 1
InvTypeBlock InvType = 2
InvTypeFilteredBlock InvType = 3
InvTypeWitnessBlock InvType = InvTypeBlock | InvWitnessFlag
InvTypeWitnessTx InvType = InvTypeTx | InvWitnessFlag
InvTypeFilteredWitnessBlock InvType = InvTypeFilteredBlock | InvWitnessFlag
)
// Map of service flags back to their constant names for pretty printing.
var ivStrings = map[InvType]string{
InvTypeError: "ERROR",
InvTypeTx: "MSG_TX",
InvTypeBlock: "MSG_BLOCK",
InvTypeFilteredBlock: "MSG_FILTERED_BLOCK",
InvTypeWitnessBlock: "MSG_WITNESS_BLOCK",
InvTypeWitnessTx: "MSG_WITNESS_TX",
InvTypeFilteredWitnessBlock: "MSG_FILTERED_WITNESS_BLOCK",
}
// String returns the InvType in human-readable form.
func (invtype InvType) String() string {
if s, ok := ivStrings[invtype]; ok {
return s
}
return fmt.Sprintf("Unknown InvType (%d)", uint32(invtype))
}
// InvVect defines a bitcoin inventory vector which is used to describe data,
// as specified by the Type field, that a peer wants, has, or does not have to
// another peer.
type InvVect struct {
Type InvType // Type of data
Hash chainhash.Hash // Hash of the data
}
// NewInvVect returns a new InvVect using the provided type and hash.
func NewInvVect(typ InvType, hash *chainhash.Hash) *InvVect {
return &InvVect{
Type: typ,
Hash: *hash,
}
}
// readInvVect reads an encoded InvVect from r depending on the protocol
// version.
func readInvVect(r io.Reader, pver uint32, iv *InvVect) error {
return readElements(r, &iv.Type, &iv.Hash)
}
// writeInvVect serializes an InvVect to w depending on the protocol version.
func writeInvVect(w io.Writer, pver uint32, iv *InvVect) error {
return writeElements(w, iv.Type, &iv.Hash)
}
+269
View File
@@ -0,0 +1,269 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestInvVectStringer tests the stringized output for inventory vector types.
func TestInvTypeStringer(t *testing.T) {
tests := []struct {
in InvType
want string
}{
{InvTypeError, "ERROR"},
{InvTypeTx, "MSG_TX"},
{InvTypeBlock, "MSG_BLOCK"},
{0xffffffff, "Unknown InvType (4294967295)"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.String()
if result != test.want {
t.Errorf("String #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
// TestInvVect tests the InvVect API.
func TestInvVect(t *testing.T) {
ivType := InvTypeBlock
hash := chainhash.Hash{}
// Ensure we get the same payload and signature back out.
iv := NewInvVect(ivType, &hash)
if iv.Type != ivType {
t.Errorf("NewInvVect: wrong type - got %v, want %v",
iv.Type, ivType)
}
if !iv.Hash.IsEqual(&hash) {
t.Errorf("NewInvVect: wrong hash - got %v, want %v",
spew.Sdump(iv.Hash), spew.Sdump(hash))
}
}
// TestInvVectWire tests the InvVect wire encode and decode for various
// protocol versions and supported inventory vector types.
func TestInvVectWire(t *testing.T) {
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
baseHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// errInvVect is an inventory vector with an error.
errInvVect := InvVect{
Type: InvTypeError,
Hash: chainhash.Hash{},
}
// errInvVectEncoded is the wire encoded bytes of errInvVect.
errInvVectEncoded := []byte{
0x00, 0x00, 0x00, 0x00, // InvTypeError
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // No hash
}
// txInvVect is an inventory vector representing a transaction.
txInvVect := InvVect{
Type: InvTypeTx,
Hash: *baseHash,
}
// txInvVectEncoded is the wire encoded bytes of txInvVect.
txInvVectEncoded := []byte{
0x01, 0x00, 0x00, 0x00, // InvTypeTx
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
}
// blockInvVect is an inventory vector representing a block.
blockInvVect := InvVect{
Type: InvTypeBlock,
Hash: *baseHash,
}
// blockInvVectEncoded is the wire encoded bytes of blockInvVect.
blockInvVectEncoded := []byte{
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
}
tests := []struct {
in InvVect // NetAddress to encode
out InvVect // Expected decoded NetAddress
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
ProtocolVersion,
},
// Latest protocol version tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
ProtocolVersion,
},
// Latest protocol version block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
ProtocolVersion,
},
// Protocol version BIP0035Version error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
BIP0035Version,
},
// Protocol version BIP0035Version tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
BIP0035Version,
},
// Protocol version BIP0035Version block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
BIP0035Version,
},
// Protocol version BIP0031Version error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
BIP0031Version,
},
// Protocol version BIP0031Version tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
BIP0031Version,
},
// Protocol version BIP0031Version block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
BIP0031Version,
},
// Protocol version NetAddressTimeVersion error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
MultipleAddressVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := writeInvVect(&buf, test.pver, &test.in)
if err != nil {
t.Errorf("writeInvVect #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("writeInvVect #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var iv InvVect
rbuf := bytes.NewReader(test.buf)
err = readInvVect(rbuf, test.pver, &iv)
if err != nil {
t.Errorf("readInvVect #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(iv, test.out) {
t.Errorf("readInvVect #%d\n got: %s want: %s", i,
spew.Sdump(iv), spew.Sdump(test.out))
continue
}
}
}
+412
View File
@@ -0,0 +1,412 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"fmt"
"io"
"unicode/utf8"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// MessageHeaderSize is the number of bytes in a bitcoin message header.
// Bitcoin network (magic) 4 bytes + command 12 bytes + payload length 4 bytes +
// checksum 4 bytes.
const MessageHeaderSize = 24
// CommandSize is the fixed size of all commands in the common bitcoin message
// header. Shorter commands must be zero padded.
const CommandSize = 12
// MaxMessagePayload is the maximum bytes a message can be regardless of other
// individual limits imposed by messages themselves.
const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
// Commands used in bitcoin message headers which describe the type of message.
const (
CmdVersion = "version"
CmdVerAck = "verack"
CmdGetAddr = "getaddr"
CmdAddr = "addr"
CmdGetBlocks = "getblocks"
CmdInv = "inv"
CmdGetData = "getdata"
CmdNotFound = "notfound"
CmdBlock = "block"
CmdTx = "tx"
CmdGetHeaders = "getheaders"
CmdHeaders = "headers"
CmdPing = "ping"
CmdPong = "pong"
CmdAlert = "alert"
CmdMemPool = "mempool"
CmdFilterAdd = "filteradd"
CmdFilterClear = "filterclear"
CmdFilterLoad = "filterload"
CmdMerkleBlock = "merkleblock"
CmdReject = "reject"
CmdSendHeaders = "sendheaders"
CmdFeeFilter = "feefilter"
)
// MessageEncoding represents the wire message encoding format to be used.
type MessageEncoding uint32
const (
// BaseEncoding encodes all messages in the default format specified
// for the Bitcoin wire protocol.
BaseEncoding MessageEncoding = 1 << iota
// WitnessEncoding encodes all messages other than transaction messages
// using the default Bitcoin wire protocol specification. For transaction
// messages, the new encoding format detailed in BIP0144 will be used.
WitnessEncoding
)
// LatestEncoding is the most recently specified encoding for the Bitcoin wire
// protocol.
var LatestEncoding = WitnessEncoding
// Message is an interface that describes a bitcoin message. A type that
// implements Message has complete control over the representation of its data
// and may therefore contain additional or fewer fields than those which
// are used directly in the protocol encoded message.
type Message interface {
BtcDecode(io.Reader, uint32, MessageEncoding) error
BtcEncode(io.Writer, uint32, MessageEncoding) error
Command() string
MaxPayloadLength(uint32) uint32
}
// makeEmptyMessage creates a message of the appropriate concrete type based
// on the command.
func makeEmptyMessage(command string) (Message, error) {
var msg Message
switch command {
case CmdVersion:
msg = &MsgVersion{}
case CmdVerAck:
msg = &MsgVerAck{}
case CmdGetAddr:
msg = &MsgGetAddr{}
case CmdAddr:
msg = &MsgAddr{}
case CmdGetBlocks:
msg = &MsgGetBlocks{}
case CmdBlock:
msg = &MsgBlock{}
case CmdInv:
msg = &MsgInv{}
case CmdGetData:
msg = &MsgGetData{}
case CmdNotFound:
msg = &MsgNotFound{}
case CmdTx:
msg = &MsgTx{}
case CmdPing:
msg = &MsgPing{}
case CmdPong:
msg = &MsgPong{}
case CmdGetHeaders:
msg = &MsgGetHeaders{}
case CmdHeaders:
msg = &MsgHeaders{}
case CmdAlert:
msg = &MsgAlert{}
case CmdMemPool:
msg = &MsgMemPool{}
case CmdFilterAdd:
msg = &MsgFilterAdd{}
case CmdFilterClear:
msg = &MsgFilterClear{}
case CmdFilterLoad:
msg = &MsgFilterLoad{}
case CmdMerkleBlock:
msg = &MsgMerkleBlock{}
case CmdReject:
msg = &MsgReject{}
case CmdSendHeaders:
msg = &MsgSendHeaders{}
case CmdFeeFilter:
msg = &MsgFeeFilter{}
default:
return nil, fmt.Errorf("unhandled command [%s]", command)
}
return msg, nil
}
// messageHeader defines the header structure for all bitcoin protocol messages.
type messageHeader struct {
magic BitcoinNet // 4 bytes
command string // 12 bytes
length uint32 // 4 bytes
checksum [4]byte // 4 bytes
}
// readMessageHeader reads a bitcoin message header from r.
func readMessageHeader(r io.Reader) (int, *messageHeader, error) {
// Since readElements doesn't return the amount of bytes read, attempt
// to read the entire header into a buffer first in case there is a
// short read so the proper amount of read bytes are known. This works
// since the header is a fixed size.
var headerBytes [MessageHeaderSize]byte
n, err := io.ReadFull(r, headerBytes[:])
if err != nil {
return n, nil, err
}
hr := bytes.NewReader(headerBytes[:])
// Create and populate a messageHeader struct from the raw header bytes.
hdr := messageHeader{}
var command [CommandSize]byte
readElements(hr, &hdr.magic, &command, &hdr.length, &hdr.checksum)
// Strip trailing zeros from command string.
hdr.command = string(bytes.TrimRight(command[:], string(0)))
return n, &hdr, nil
}
// discardInput reads n bytes from reader r in chunks and discards the read
// bytes. This is used to skip payloads when various errors occur and helps
// prevent rogue nodes from causing massive memory allocation through forging
// header length.
func discardInput(r io.Reader, n uint32) {
maxSize := uint32(10 * 1024) // 10k at a time
numReads := n / maxSize
bytesRemaining := n % maxSize
if n > 0 {
buf := make([]byte, maxSize)
for i := uint32(0); i < numReads; i++ {
io.ReadFull(r, buf)
}
}
if bytesRemaining > 0 {
buf := make([]byte, bytesRemaining)
io.ReadFull(r, buf)
}
}
// WriteMessageN writes a bitcoin Message to w including the necessary header
// information and returns the number of bytes written. This function is the
// same as WriteMessage except it also returns the number of bytes written.
func WriteMessageN(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) (int, error) {
return WriteMessageWithEncodingN(w, msg, pver, btcnet, BaseEncoding)
}
// WriteMessage writes a bitcoin Message to w including the necessary header
// information. This function is the same as WriteMessageN except it doesn't
// doesn't return the number of bytes written. This function is mainly provided
// for backwards compatibility with the original API, but it's also useful for
// callers that don't care about byte counts.
func WriteMessage(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) error {
_, err := WriteMessageN(w, msg, pver, btcnet)
return err
}
// WriteMessageWithEncodingN writes a bitcoin Message to w including the
// necessary header information and returns the number of bytes written.
// This function is the same as WriteMessageN except it also allows the caller
// to specify the message encoding format to be used when serializing wire
// messages.
func WriteMessageWithEncodingN(w io.Writer, msg Message, pver uint32,
btcnet BitcoinNet, encoding MessageEncoding) (int, error) {
totalBytes := 0
// Enforce max command size.
var command [CommandSize]byte
cmd := msg.Command()
if len(cmd) > CommandSize {
str := fmt.Sprintf("command [%s] is too long [max %v]",
cmd, CommandSize)
return totalBytes, messageError("WriteMessage", str)
}
copy(command[:], []byte(cmd))
// Encode the message payload.
var bw bytes.Buffer
err := msg.BtcEncode(&bw, pver, encoding)
if err != nil {
return totalBytes, err
}
payload := bw.Bytes()
lenp := len(payload)
// Enforce maximum overall message payload.
if lenp > MaxMessagePayload {
str := fmt.Sprintf("message payload is too large - encoded "+
"%d bytes, but maximum message payload is %d bytes",
lenp, MaxMessagePayload)
return totalBytes, messageError("WriteMessage", str)
}
// Enforce maximum message payload based on the message type.
mpl := msg.MaxPayloadLength(pver)
if uint32(lenp) > mpl {
str := fmt.Sprintf("message payload is too large - encoded "+
"%d bytes, but maximum message payload size for "+
"messages of type [%s] is %d.", lenp, cmd, mpl)
return totalBytes, messageError("WriteMessage", str)
}
// Create header for the message.
hdr := messageHeader{}
hdr.magic = btcnet
hdr.command = cmd
hdr.length = uint32(lenp)
copy(hdr.checksum[:], chainhash.DoubleHashB(payload)[0:4])
// Encode the header for the message. This is done to a buffer
// rather than directly to the writer since writeElements doesn't
// return the number of bytes written.
hw := bytes.NewBuffer(make([]byte, 0, MessageHeaderSize))
writeElements(hw, hdr.magic, command, hdr.length, hdr.checksum)
// Write header.
n, err := w.Write(hw.Bytes())
totalBytes += n
if err != nil {
return totalBytes, err
}
// Write payload.
n, err = w.Write(payload)
totalBytes += n
return totalBytes, err
}
// ReadMessageWithEncodingN reads, validates, and parses the next bitcoin Message
// from r for the provided protocol version and bitcoin network. It returns the
// number of bytes read in addition to the parsed Message and raw bytes which
// comprise the message. This function is the same as ReadMessageN except it
// allows the caller to specify which message encoding is to to consult when
// decoding wire messages.
func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
enc MessageEncoding) (int, Message, []byte, error) {
totalBytes := 0
n, hdr, err := readMessageHeader(r)
totalBytes += n
if err != nil {
return totalBytes, nil, nil, err
}
// Enforce maximum message payload.
if hdr.length > MaxMessagePayload {
str := fmt.Sprintf("message payload is too large - header "+
"indicates %d bytes, but max message payload is %d "+
"bytes.", hdr.length, MaxMessagePayload)
return totalBytes, nil, nil, messageError("ReadMessage", str)
}
// Check for messages from the wrong bitcoin network.
if hdr.magic != btcnet {
discardInput(r, hdr.length)
str := fmt.Sprintf("message from other network [%v]", hdr.magic)
return totalBytes, nil, nil, messageError("ReadMessage", str)
}
// Check for malformed commands.
command := hdr.command
if !utf8.ValidString(command) {
discardInput(r, hdr.length)
str := fmt.Sprintf("invalid command %v", []byte(command))
return totalBytes, nil, nil, messageError("ReadMessage", str)
}
// Create struct of appropriate message type based on the command.
msg, err := makeEmptyMessage(command)
if err != nil {
discardInput(r, hdr.length)
return totalBytes, nil, nil, messageError("ReadMessage",
err.Error())
}
// Check for maximum length based on the message type as a malicious client
// could otherwise create a well-formed header and set the length to max
// numbers in order to exhaust the machine's memory.
mpl := msg.MaxPayloadLength(pver)
if hdr.length > mpl {
discardInput(r, hdr.length)
str := fmt.Sprintf("payload exceeds max length - header "+
"indicates %v bytes, but max payload size for "+
"messages of type [%v] is %v.", hdr.length, command, mpl)
return totalBytes, nil, nil, messageError("ReadMessage", str)
}
// Read payload.
payload := make([]byte, hdr.length)
n, err = io.ReadFull(r, payload)
totalBytes += n
if err != nil {
return totalBytes, nil, nil, err
}
// Test checksum.
checksum := chainhash.DoubleHashB(payload)[0:4]
if !bytes.Equal(checksum[:], hdr.checksum[:]) {
str := fmt.Sprintf("payload checksum failed - header "+
"indicates %v, but actual checksum is %v.",
hdr.checksum, checksum)
return totalBytes, nil, nil, messageError("ReadMessage", str)
}
// Unmarshal message. NOTE: This must be a *bytes.Buffer since the
// MsgVersion BtcDecode function requires it.
pr := bytes.NewBuffer(payload)
err = msg.BtcDecode(pr, pver, enc)
if err != nil {
return totalBytes, nil, nil, err
}
return totalBytes, msg, payload, nil
}
// ReadMessageN reads, validates, and parses the next bitcoin Message from r for
// the provided protocol version and bitcoin network. It returns the number of
// bytes read in addition to the parsed Message and raw bytes which comprise the
// message. This function is the same as ReadMessage except it also returns the
// number of bytes read.
func ReadMessageN(r io.Reader, pver uint32, btcnet BitcoinNet) (int, Message, []byte, error) {
return ReadMessageWithEncodingN(r, pver, btcnet, BaseEncoding)
}
// ReadMessage reads, validates, and parses the next bitcoin Message from r for
// the provided protocol version and bitcoin network. It returns the parsed
// Message and raw bytes which comprise the message. This function only differs
// from ReadMessageN in that it doesn't return the number of bytes read. This
// function is mainly provided for backwards compatibility with the original
// API, but it's also useful for callers that don't care about byte counts.
func ReadMessage(r io.Reader, pver uint32, btcnet BitcoinNet) (Message, []byte, error) {
_, msg, buf, err := ReadMessageN(r, pver, btcnet)
return msg, buf, err
}
+446
View File
@@ -0,0 +1,446 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"encoding/binary"
"io"
"net"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// makeHeader is a convenience function to make a message header in the form of
// a byte slice. It is used to force errors when reading messages.
func makeHeader(btcnet BitcoinNet, command string,
payloadLen uint32, checksum uint32) []byte {
// The length of a bitcoin message header is 24 bytes.
// 4 byte magic number of the bitcoin network + 12 byte command + 4 byte
// payload length + 4 byte checksum.
buf := make([]byte, 24)
binary.LittleEndian.PutUint32(buf, uint32(btcnet))
copy(buf[4:], []byte(command))
binary.LittleEndian.PutUint32(buf[16:], payloadLen)
binary.LittleEndian.PutUint32(buf[20:], checksum)
return buf
}
// TestMessage tests the Read/WriteMessage and Read/WriteMessageN API.
func TestMessage(t *testing.T) {
pver := ProtocolVersion
// Create the various types of messages to test.
// MsgVersion.
addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}
you := NewNetAddress(addrYou, SFNodeNetwork)
you.Timestamp = time.Time{} // Version message has zero value timestamp.
addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
me := NewNetAddress(addrMe, SFNodeNetwork)
me.Timestamp = time.Time{} // Version message has zero value timestamp.
msgVersion := NewMsgVersion(me, you, 123123, 0)
msgVerack := NewMsgVerAck()
msgGetAddr := NewMsgGetAddr()
msgAddr := NewMsgAddr()
msgGetBlocks := NewMsgGetBlocks(&chainhash.Hash{})
msgBlock := &blockOne
msgInv := NewMsgInv()
msgGetData := NewMsgGetData()
msgNotFound := NewMsgNotFound()
msgTx := NewMsgTx(1)
msgPing := NewMsgPing(123123)
msgPong := NewMsgPong(123123)
msgGetHeaders := NewMsgGetHeaders()
msgHeaders := NewMsgHeaders()
msgAlert := NewMsgAlert([]byte("payload"), []byte("signature"))
msgMemPool := NewMsgMemPool()
msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
msgFilterClear := NewMsgFilterClear()
msgFilterLoad := NewMsgFilterLoad([]byte{0x01}, 10, 0, BloomUpdateNone)
bh := NewBlockHeader(1, &chainhash.Hash{}, &chainhash.Hash{}, 0, 0)
msgMerkleBlock := NewMsgMerkleBlock(bh)
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
tests := []struct {
in Message // Value to encode
out Message // Expected decoded value
pver uint32 // Protocol version for wire encoding
btcnet BitcoinNet // Network to use for wire encoding
bytes int // Expected num bytes read/written
}{
{msgVersion, msgVersion, pver, MainNet, 125},
{msgVerack, msgVerack, pver, MainNet, 24},
{msgGetAddr, msgGetAddr, pver, MainNet, 24},
{msgAddr, msgAddr, pver, MainNet, 25},
{msgGetBlocks, msgGetBlocks, pver, MainNet, 61},
{msgBlock, msgBlock, pver, MainNet, 239},
{msgInv, msgInv, pver, MainNet, 25},
{msgGetData, msgGetData, pver, MainNet, 25},
{msgNotFound, msgNotFound, pver, MainNet, 25},
{msgTx, msgTx, pver, MainNet, 34},
{msgPing, msgPing, pver, MainNet, 32},
{msgPong, msgPong, pver, MainNet, 32},
{msgGetHeaders, msgGetHeaders, pver, MainNet, 61},
{msgHeaders, msgHeaders, pver, MainNet, 25},
{msgAlert, msgAlert, pver, MainNet, 42},
{msgMemPool, msgMemPool, pver, MainNet, 24},
{msgFilterAdd, msgFilterAdd, pver, MainNet, 26},
{msgFilterClear, msgFilterClear, pver, MainNet, 24},
{msgFilterLoad, msgFilterLoad, pver, MainNet, 35},
{msgMerkleBlock, msgMerkleBlock, pver, MainNet, 110},
{msgReject, msgReject, pver, MainNet, 79},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
nw, err := WriteMessageN(&buf, test.in, test.pver, test.btcnet)
if err != nil {
t.Errorf("WriteMessage #%d error %v", i, err)
continue
}
// Ensure the number of bytes written match the expected value.
if nw != test.bytes {
t.Errorf("WriteMessage #%d unexpected num bytes "+
"written - got %d, want %d", i, nw, test.bytes)
}
// Decode from wire format.
rbuf := bytes.NewReader(buf.Bytes())
nr, msg, _, err := ReadMessageN(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
}
// Ensure the number of bytes read match the expected value.
if nr != test.bytes {
t.Errorf("ReadMessage #%d unexpected num bytes read - "+
"got %d, want %d", i, nr, test.bytes)
}
}
// Do the same thing for Read/WriteMessage, but ignore the bytes since
// they don't return them.
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := 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.NewReader(buf.Bytes())
msg, _, err := 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
}
}
}
// TestReadMessageWireErrors performs negative tests against wire decoding into
// concrete messages to confirm error paths work correctly.
func TestReadMessageWireErrors(t *testing.T) {
pver := ProtocolVersion
btcnet := MainNet
// Ensure message errors are as expected with no function specified.
wantErr := "something bad happened"
testErr := MessageError{Description: wantErr}
if testErr.Error() != wantErr {
t.Errorf("MessageError: wrong error - got %v, want %v",
testErr.Error(), wantErr)
}
// Ensure message errors are as expected with a function specified.
wantFunc := "foo"
testErr = MessageError{Func: wantFunc, Description: wantErr}
if testErr.Error() != wantFunc+": "+wantErr {
t.Errorf("MessageError: wrong error - got %v, want %v",
testErr.Error(), wantErr)
}
// Wire encoded bytes for main and testnet3 networks magic identifiers.
testNet3Bytes := makeHeader(TestNet3, "", 0, 0)
// Wire encoded bytes for a message that exceeds max overall message
// length.
mpl := uint32(MaxMessagePayload)
exceedMaxPayloadBytes := makeHeader(btcnet, "getaddr", mpl+1, 0)
// Wire encoded bytes for a command which is invalid utf-8.
badCommandBytes := makeHeader(btcnet, "bogus", 0, 0)
badCommandBytes[4] = 0x81
// Wire encoded bytes for a command which is valid, but not supported.
unsupportedCommandBytes := makeHeader(btcnet, "bogus", 0, 0)
// Wire encoded bytes for a message which exceeds the max payload for
// a specific message type.
exceedTypePayloadBytes := makeHeader(btcnet, "getaddr", 1, 0)
// Wire encoded bytes for a message which does not deliver the full
// payload according to the header length.
shortPayloadBytes := makeHeader(btcnet, "version", 115, 0)
// Wire encoded bytes for a message with a bad checksum.
badChecksumBytes := makeHeader(btcnet, "version", 2, 0xbeef)
badChecksumBytes = append(badChecksumBytes, []byte{0x0, 0x0}...)
// Wire encoded bytes for a message which has a valid header, but is
// the wrong format. An addr starts with a varint of the number of
// contained in the message. Claim there is two, but don't provide
// them. At the same time, forge the header fields so the message is
// otherwise accurate.
badMessageBytes := makeHeader(btcnet, "addr", 1, 0xeaadc31c)
badMessageBytes = append(badMessageBytes, 0x2)
// Wire encoded bytes for a message which the header claims has 15k
// bytes of data to discard.
discardBytes := makeHeader(btcnet, "bogus", 15*1024, 0)
tests := []struct {
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
btcnet BitcoinNet // Bitcoin network for wire encoding
max int // Max size of fixed buffer to induce errors
readErr error // Expected read error
bytes int // Expected num bytes read
}{
// Latest protocol version with intentional read errors.
// Short header.
{
[]byte{},
pver,
btcnet,
0,
io.EOF,
0,
},
// Wrong network. Want MainNet, but giving TestNet3.
{
testNet3Bytes,
pver,
btcnet,
len(testNet3Bytes),
&MessageError{},
24,
},
// Exceed max overall message payload length.
{
exceedMaxPayloadBytes,
pver,
btcnet,
len(exceedMaxPayloadBytes),
&MessageError{},
24,
},
// Invalid UTF-8 command.
{
badCommandBytes,
pver,
btcnet,
len(badCommandBytes),
&MessageError{},
24,
},
// Valid, but unsupported command.
{
unsupportedCommandBytes,
pver,
btcnet,
len(unsupportedCommandBytes),
&MessageError{},
24,
},
// Exceed max allowed payload for a message of a specific type.
{
exceedTypePayloadBytes,
pver,
btcnet,
len(exceedTypePayloadBytes),
&MessageError{},
24,
},
// Message with a payload shorter than the header indicates.
{
shortPayloadBytes,
pver,
btcnet,
len(shortPayloadBytes),
io.EOF,
24,
},
// Message with a bad checksum.
{
badChecksumBytes,
pver,
btcnet,
len(badChecksumBytes),
&MessageError{},
26,
},
// Message with a valid header, but wrong format.
{
badMessageBytes,
pver,
btcnet,
len(badMessageBytes),
io.EOF,
25,
},
// 15k bytes of data to discard.
{
discardBytes,
pver,
btcnet,
len(discardBytes),
&MessageError{},
24,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
r := newFixedReader(test.max, test.buf)
nr, _, _, err := ReadMessageN(r, test.pver, test.btcnet)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
"want: %T", i, err, err, test.readErr)
continue
}
// Ensure the number of bytes written match the expected value.
if nr != test.bytes {
t.Errorf("ReadMessage #%d unexpected num bytes read - "+
"got %d, want %d", i, nr, test.bytes)
}
// For errors which are not of type MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
"want: %v <%T>", i, err, err,
test.readErr, test.readErr)
continue
}
}
}
}
// TestWriteMessageWireErrors performs negative tests against wire encoding from
// concrete messages to confirm error paths work correctly.
func TestWriteMessageWireErrors(t *testing.T) {
pver := ProtocolVersion
btcnet := MainNet
wireErr := &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 payload which exceeds max overall message size.
exceedOverallPayload := make([]byte, MaxMessagePayload+1)
exceedOverallPayloadErrMsg := &fakeMessage{payload: exceedOverallPayload}
// Fake message that has payload which exceeds max allowed per message.
exceedPayload := make([]byte, 1)
exceedPayloadErrMsg := &fakeMessage{payload: exceedPayload, forceLenErr: true}
// Fake message that is used to force errors in the header and payload
// writes.
bogusPayload := []byte{0x01, 0x02, 0x03, 0x04}
bogusMsg := &fakeMessage{command: "bogus", payload: bogusPayload}
tests := []struct {
msg Message // Message to encode
pver uint32 // Protocol version for wire encoding
btcnet BitcoinNet // Bitcoin network for wire encoding
max int // Max size of fixed buffer to induce errors
err error // Expected error
bytes int // Expected num bytes written
}{
// Command too long.
{badCommandMsg, pver, btcnet, 0, wireErr, 0},
// Force error in payload encode.
{encodeErrMsg, pver, btcnet, 0, wireErr, 0},
// Force error due to exceeding max overall message payload size.
{exceedOverallPayloadErrMsg, pver, btcnet, 0, wireErr, 0},
// Force error due to exceeding max payload for message type.
{exceedPayloadErrMsg, pver, btcnet, 0, wireErr, 0},
// Force error in header write.
{bogusMsg, pver, btcnet, 0, io.ErrShortWrite, 0},
// Force error in payload write.
{bogusMsg, pver, btcnet, 24, io.ErrShortWrite, 24},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode wire format.
w := newFixedWriter(test.max)
nw, err := WriteMessageN(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
}
// Ensure the number of bytes written match the expected value.
if nw != test.bytes {
t.Errorf("WriteMessage #%d unexpected num bytes "+
"written - got %d, want %d", i, nw, test.bytes)
}
// For errors which are not of type MessageError, check them for
// equality.
if _, ok := err.(*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
}
}
}
}
+143
View File
@@ -0,0 +1,143 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MaxAddrPerMsg is the maximum number of addresses that can be in a single
// bitcoin addr message (MsgAddr).
const MaxAddrPerMsg = 1000
// MsgAddr implements the Message interface and represents a bitcoin
// addr message. It is used to provide a list of known active peers on the
// network. An active peer is considered one that has transmitted a message
// within the last 3 hours. Nodes which have not transmitted in that time
// frame should be forgotten. Each message is limited to a maximum number of
// addresses, which is currently 1000. As a result, multiple messages must
// be used to relay the full list.
//
// Use the AddAddress function to build up the list of known addresses when
// sending an addr message to another peer.
type MsgAddr struct {
AddrList []*NetAddress
}
// AddAddress adds a known active peer to the message.
func (msg *MsgAddr) AddAddress(na *NetAddress) error {
if len(msg.AddrList)+1 > MaxAddrPerMsg {
str := fmt.Sprintf("too many addresses in message [max %v]",
MaxAddrPerMsg)
return messageError("MsgAddr.AddAddress", str)
}
msg.AddrList = append(msg.AddrList, na)
return nil
}
// AddAddresses adds multiple known active peers to the message.
func (msg *MsgAddr) AddAddresses(netAddrs ...*NetAddress) error {
for _, na := range netAddrs {
err := msg.AddAddress(na)
if err != nil {
return err
}
}
return nil
}
// ClearAddresses removes all addresses from the message.
func (msg *MsgAddr) ClearAddresses() {
msg.AddrList = []*NetAddress{}
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgAddr) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max addresses per message.
if count > MaxAddrPerMsg {
str := fmt.Sprintf("too many addresses for message "+
"[count %v, max %v]", count, MaxAddrPerMsg)
return messageError("MsgAddr.BtcDecode", str)
}
addrList := make([]NetAddress, count)
msg.AddrList = make([]*NetAddress, 0, count)
for i := uint64(0); i < count; i++ {
na := &addrList[i]
err := readNetAddress(r, pver, na, true)
if err != nil {
return err
}
msg.AddAddress(na)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgAddr) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Protocol versions before MultipleAddressVersion only allowed 1 address
// per message.
count := len(msg.AddrList)
if pver < MultipleAddressVersion && count > 1 {
str := fmt.Sprintf("too many addresses for message of "+
"protocol version %v [count %v, max 1]", pver, count)
return messageError("MsgAddr.BtcEncode", str)
}
if count > MaxAddrPerMsg {
str := fmt.Sprintf("too many addresses for message "+
"[count %v, max %v]", count, MaxAddrPerMsg)
return messageError("MsgAddr.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, na := range msg.AddrList {
err = writeNetAddress(w, pver, na, true)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgAddr) Command() string {
return CmdAddr
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgAddr) MaxPayloadLength(pver uint32) uint32 {
if pver < MultipleAddressVersion {
// Num addresses (varInt) + a single net addresses.
return MaxVarIntPayload + maxNetAddressPayload(pver)
}
// Num addresses (varInt) + max allowed addresses.
return MaxVarIntPayload + (MaxAddrPerMsg * maxNetAddressPayload(pver))
}
// NewMsgAddr returns a new bitcoin addr message that conforms to the
// Message interface. See MsgAddr for details.
func NewMsgAddr() *MsgAddr {
return &MsgAddr{
AddrList: make([]*NetAddress, 0, MaxAddrPerMsg),
}
}
+320
View File
@@ -0,0 +1,320 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"net"
"reflect"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
)
// TestAddr tests the MsgAddr API.
func TestAddr(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "addr"
msg := NewMsgAddr()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgAddr: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num addresses (varInt) + max allowed addresses.
wantPayload := uint32(30009)
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 NetAddresses are added properly.
tcpAddr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
na := NewNetAddress(tcpAddr, SFNodeNetwork)
err := msg.AddAddress(na)
if err != nil {
t.Errorf("AddAddress: %v", err)
}
if msg.AddrList[0] != na {
t.Errorf("AddAddress: wrong address added - got %v, want %v",
spew.Sprint(msg.AddrList[0]), spew.Sprint(na))
}
// Ensure the address list is cleared properly.
msg.ClearAddresses()
if len(msg.AddrList) != 0 {
t.Errorf("ClearAddresses: address list is not empty - "+
"got %v [%v], want %v", len(msg.AddrList),
spew.Sprint(msg.AddrList[0]), 0)
}
// Ensure adding more than the max allowed addresses per message returns
// error.
for i := 0; i < MaxAddrPerMsg+1; i++ {
err = msg.AddAddress(na)
}
if err == nil {
t.Errorf("AddAddress: expected error on too many addresses " +
"not received")
}
err = msg.AddAddresses(na)
if err == nil {
t.Errorf("AddAddresses: expected error on too many addresses " +
"not received")
}
// Ensure max payload is expected value for protocol versions before
// timestamp was added to NetAddress.
// Num addresses (varInt) + max allowed addresses.
pver = NetAddressTimeVersion - 1
wantPayload = uint32(26009)
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 max payload is expected value for protocol versions before
// multiple addresses were allowed.
// Num addresses (varInt) + a single net addresses.
pver = MultipleAddressVersion - 1
wantPayload = uint32(35)
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)
}
}
// TestAddrWire tests the MsgAddr wire encode and decode for various numbers
// of addresses and protocol versions.
func TestAddrWire(t *testing.T) {
// A couple of NetAddresses to use for testing.
na := &NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
na2 := &NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8334,
}
// Empty address message.
noAddr := NewMsgAddr()
noAddrEncoded := []byte{
0x00, // Varint for number of addresses
}
// Address message with multiple addresses.
multiAddr := NewMsgAddr()
multiAddr.AddAddresses(na, na2)
multiAddrEncoded := []byte{
0x02, // Varint for number of addresses
0x29, 0xab, 0x5f, 0x49, // Timestamp
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1
0x20, 0x8d, // Port 8333 in big-endian
0x29, 0xab, 0x5f, 0x49, // Timestamp
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1
0x20, 0x8e, // Port 8334 in big-endian
}
tests := []struct {
in *MsgAddr // Message to encode
out *MsgAddr // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version with no addresses.
{
noAddr,
noAddr,
noAddrEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with multiple addresses.
{
multiAddr,
multiAddr,
multiAddrEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion-1 with no addresses.
{
noAddr,
noAddr,
noAddrEncoded,
MultipleAddressVersion - 1,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgAddr
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestAddrWireErrors performs negative tests against wire encode and decode
// of MsgAddr to confirm error paths work correctly.
func TestAddrWireErrors(t *testing.T) {
pver := ProtocolVersion
pverMA := MultipleAddressVersion
wireErr := &MessageError{}
// A couple of NetAddresses to use for testing.
na := &NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
na2 := &NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8334,
}
// Address message with multiple addresses.
baseAddr := NewMsgAddr()
baseAddr.AddAddresses(na, na2)
baseAddrEncoded := []byte{
0x02, // Varint for number of addresses
0x29, 0xab, 0x5f, 0x49, // Timestamp
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1
0x20, 0x8d, // Port 8333 in big-endian
0x29, 0xab, 0x5f, 0x49, // Timestamp
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1
0x20, 0x8e, // Port 8334 in big-endian
}
// Message that forces an error by having more than the max allowed
// addresses.
maxAddr := NewMsgAddr()
for i := 0; i < MaxAddrPerMsg; i++ {
maxAddr.AddAddress(na)
}
maxAddr.AddrList = append(maxAddr.AddrList, na)
maxAddrEncoded := []byte{
0xfd, 0x03, 0xe9, // Varint for number of addresses (1001)
}
tests := []struct {
in *MsgAddr // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 addresses count
{baseAddr, baseAddrEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in address list.
{baseAddr, baseAddrEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxAddr, maxAddrEncoded, pver, BaseEncoding, 3, wireErr, wireErr},
// Force error with greater than max inventory vectors for
// protocol versions before multiple addresses were allowed.
{maxAddr, maxAddrEncoded, pverMA - 1, BaseEncoding, 3, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgAddr
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+407
View File
@@ -0,0 +1,407 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"fmt"
"io"
)
// MsgAlert contains a payload and a signature:
//
// ===============================================
// | Field | Data Type | Size |
// ===============================================
// | payload | []uchar | ? |
// -----------------------------------------------
// | signature | []uchar | ? |
// -----------------------------------------------
//
// Here payload is an Alert serialized into a byte array to ensure that
// versions using incompatible alert formats can still relay
// alerts among one another.
//
// An Alert is the payload deserialized as follows:
//
// ===============================================
// | Field | Data Type | Size |
// ===============================================
// | Version | int32 | 4 |
// -----------------------------------------------
// | RelayUntil | int64 | 8 |
// -----------------------------------------------
// | Expiration | int64 | 8 |
// -----------------------------------------------
// | ID | int32 | 4 |
// -----------------------------------------------
// | Cancel | int32 | 4 |
// -----------------------------------------------
// | SetCancel | set<int32> | ? |
// -----------------------------------------------
// | MinVer | int32 | 4 |
// -----------------------------------------------
// | MaxVer | int32 | 4 |
// -----------------------------------------------
// | SetSubVer | set<string> | ? |
// -----------------------------------------------
// | Priority | int32 | 4 |
// -----------------------------------------------
// | Comment | string | ? |
// -----------------------------------------------
// | StatusBar | string | ? |
// -----------------------------------------------
// | Reserved | string | ? |
// -----------------------------------------------
// | Total (Fixed) | 45 |
// -----------------------------------------------
//
// NOTE:
// * string is a VarString i.e VarInt length followed by the string itself
// * set<string> is a VarInt followed by as many number of strings
// * set<int32> is a VarInt followed by as many number of ints
// * fixedAlertSize = 40 + 5*min(VarInt) = 40 + 5*1 = 45
//
// Now we can define bounds on Alert size, SetCancel and SetSubVer
// Fixed size of the alert payload
const fixedAlertSize = 45
// maxSignatureSize is the max size of an ECDSA signature.
// NOTE: Since this size is fixed and < 255, the size of VarInt required = 1.
const maxSignatureSize = 72
// maxAlertSize is the maximum size an alert.
//
// MessagePayload = VarInt(Alert) + Alert + VarInt(Signature) + Signature
// MaxMessagePayload = maxAlertSize + max(VarInt) + maxSignatureSize + 1
const maxAlertSize = MaxMessagePayload - maxSignatureSize - MaxVarIntPayload - 1
// maxCountSetCancel is the maximum number of cancel IDs that could possibly
// fit into a maximum size alert.
//
// maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
// for caculating maximum number of cancel IDs, set all other var sizes to 0
// maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(int32)
// x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
const maxCountSetCancel = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
// maxCountSetSubVer is the maximum number of subversions that could possibly
// fit into a maximum size alert.
//
// maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
// for caculating maximum number of subversions, set all other var sizes to 0
// maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(string)
// x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / sizeOf(string)
// subversion would typically be something like "/Satoshi:0.7.2/" (15 bytes)
// so assuming < 255 bytes, sizeOf(string) = sizeOf(uint8) + 255 = 256
const maxCountSetSubVer = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 256
// Alert contains the data deserialized from the MsgAlert payload.
type Alert struct {
// Alert format version
Version int32
// Timestamp beyond which nodes should stop relaying this alert
RelayUntil int64
// Timestamp beyond which this alert is no longer in effect and
// should be ignored
Expiration int64
// A unique ID number for this alert
ID int32
// All alerts with an ID less than or equal to this number should
// cancelled, deleted and not accepted in the future
Cancel int32
// All alert IDs contained in this set should be cancelled as above
SetCancel []int32
// This alert only applies to versions greater than or equal to this
// version. Other versions should still relay it.
MinVer int32
// This alert only applies to versions less than or equal to this version.
// Other versions should still relay it.
MaxVer int32
// If this set contains any elements, then only nodes that have their
// subVer contained in this set are affected by the alert. Other versions
// should still relay it.
SetSubVer []string
// Relative priority compared to other alerts
Priority int32
// A comment on the alert that is not displayed
Comment string
// The alert message that is displayed to the user
StatusBar string
// Reserved
Reserved string
}
// Serialize encodes the alert to w using the alert protocol encoding format.
func (alert *Alert) Serialize(w io.Writer, pver uint32) error {
err := writeElements(w, alert.Version, alert.RelayUntil,
alert.Expiration, alert.ID, alert.Cancel)
if err != nil {
return err
}
count := len(alert.SetCancel)
if count > maxCountSetCancel {
str := fmt.Sprintf("too many cancel alert IDs for alert "+
"[count %v, max %v]", count, maxCountSetCancel)
return messageError("Alert.Serialize", str)
}
err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for i := 0; i < count; i++ {
err = writeElement(w, alert.SetCancel[i])
if err != nil {
return err
}
}
err = writeElements(w, alert.MinVer, alert.MaxVer)
if err != nil {
return err
}
count = len(alert.SetSubVer)
if count > maxCountSetSubVer {
str := fmt.Sprintf("too many sub versions for alert "+
"[count %v, max %v]", count, maxCountSetSubVer)
return messageError("Alert.Serialize", str)
}
err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for i := 0; i < count; i++ {
err = WriteVarString(w, pver, alert.SetSubVer[i])
if err != nil {
return err
}
}
err = writeElement(w, alert.Priority)
if err != nil {
return err
}
err = WriteVarString(w, pver, alert.Comment)
if err != nil {
return err
}
err = WriteVarString(w, pver, alert.StatusBar)
if err != nil {
return err
}
return WriteVarString(w, pver, alert.Reserved)
}
// Deserialize decodes from r into the receiver using the alert protocol
// encoding format.
func (alert *Alert) Deserialize(r io.Reader, pver uint32) error {
err := readElements(r, &alert.Version, &alert.RelayUntil,
&alert.Expiration, &alert.ID, &alert.Cancel)
if err != nil {
return err
}
// SetCancel: first read a VarInt that contains
// count - the number of Cancel IDs, then
// iterate count times and read them
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > maxCountSetCancel {
str := fmt.Sprintf("too many cancel alert IDs for alert "+
"[count %v, max %v]", count, maxCountSetCancel)
return messageError("Alert.Deserialize", str)
}
alert.SetCancel = make([]int32, count)
for i := 0; i < int(count); i++ {
err := readElement(r, &alert.SetCancel[i])
if err != nil {
return err
}
}
err = readElements(r, &alert.MinVer, &alert.MaxVer)
if err != nil {
return err
}
// SetSubVer: similar to SetCancel
// but read count number of sub-version strings
count, err = ReadVarInt(r, pver)
if err != nil {
return err
}
if count > maxCountSetSubVer {
str := fmt.Sprintf("too many sub versions for alert "+
"[count %v, max %v]", count, maxCountSetSubVer)
return messageError("Alert.Deserialize", str)
}
alert.SetSubVer = make([]string, count)
for i := 0; i < int(count); i++ {
alert.SetSubVer[i], err = ReadVarString(r, pver)
if err != nil {
return err
}
}
err = readElement(r, &alert.Priority)
if err != nil {
return err
}
alert.Comment, err = ReadVarString(r, pver)
if err != nil {
return err
}
alert.StatusBar, err = ReadVarString(r, pver)
if err != nil {
return err
}
alert.Reserved, err = ReadVarString(r, pver)
return err
}
// NewAlert returns an new Alert with values provided.
func NewAlert(version int32, relayUntil int64, expiration int64,
id int32, cancel int32, setCancel []int32, minVer int32,
maxVer int32, setSubVer []string, priority int32, comment string,
statusBar string) *Alert {
return &Alert{
Version: version,
RelayUntil: relayUntil,
Expiration: expiration,
ID: id,
Cancel: cancel,
SetCancel: setCancel,
MinVer: minVer,
MaxVer: maxVer,
SetSubVer: setSubVer,
Priority: priority,
Comment: comment,
StatusBar: statusBar,
Reserved: "",
}
}
// NewAlertFromPayload returns an Alert with values deserialized from the
// serialized payload.
func NewAlertFromPayload(serializedPayload []byte, pver uint32) (*Alert, error) {
var alert Alert
r := bytes.NewReader(serializedPayload)
err := alert.Deserialize(r, pver)
if err != nil {
return nil, err
}
return &alert, nil
}
// MsgAlert implements the Message interface and defines a bitcoin alert
// message.
//
// This is a signed message that provides notifications that the client should
// display if the signature matches the key. bitcoind/bitcoin-qt only checks
// against a signature from the core developers.
type MsgAlert struct {
// SerializedPayload is the alert payload serialized as a string so that the
// version can change but the Alert can still be passed on by older
// clients.
SerializedPayload []byte
// Signature is the ECDSA signature of the message.
Signature []byte
// Deserialized Payload
Payload *Alert
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgAlert) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
var err error
msg.SerializedPayload, err = ReadVarBytes(r, pver, MaxMessagePayload,
"alert serialized payload")
if err != nil {
return err
}
msg.Payload, err = NewAlertFromPayload(msg.SerializedPayload, pver)
if err != nil {
msg.Payload = nil
}
msg.Signature, err = ReadVarBytes(r, pver, MaxMessagePayload,
"alert signature")
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgAlert) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
var err error
var serializedpayload []byte
if msg.Payload != nil {
// try to Serialize Payload if possible
r := new(bytes.Buffer)
err = msg.Payload.Serialize(r, pver)
if err != nil {
// Serialize failed - ignore & fallback
// to SerializedPayload
serializedpayload = msg.SerializedPayload
} else {
serializedpayload = r.Bytes()
}
} else {
serializedpayload = msg.SerializedPayload
}
slen := uint64(len(serializedpayload))
if slen == 0 {
return messageError("MsgAlert.BtcEncode", "empty serialized payload")
}
err = WriteVarBytes(w, pver, serializedpayload)
if err != nil {
return err
}
return WriteVarBytes(w, pver, msg.Signature)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgAlert) Command() string {
return CmdAlert
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgAlert) MaxPayloadLength(pver uint32) uint32 {
// Since this can vary depending on the message, make it the max
// size allowed.
return MaxMessagePayload
}
// NewMsgAlert returns a new bitcoin alert message that conforms to the Message
// interface. See MsgAlert for details.
func NewMsgAlert(serializedPayload []byte, signature []byte) *MsgAlert {
return &MsgAlert{
SerializedPayload: serializedPayload,
Signature: signature,
Payload: nil,
}
}
+475
View File
@@ -0,0 +1,475 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestMsgAlert tests the MsgAlert API.
func TestMsgAlert(t *testing.T) {
pver := ProtocolVersion
encoding := BaseEncoding
serializedpayload := []byte("some message")
signature := []byte("some sig")
// Ensure we get the same payload and signature back out.
msg := NewMsgAlert(serializedpayload, signature)
if !reflect.DeepEqual(msg.SerializedPayload, serializedpayload) {
t.Errorf("NewMsgAlert: wrong serializedpayload - got %v, want %v",
msg.SerializedPayload, serializedpayload)
}
if !reflect.DeepEqual(msg.Signature, signature) {
t.Errorf("NewMsgAlert: wrong signature - got %v, want %v",
msg.Signature, signature)
}
// Ensure the command is expected value.
wantCmd := "alert"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgAlert: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
wantPayload := uint32(1024 * 1024 * 32)
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 BtcEncode with Payload == nil
var buf bytes.Buffer
err := msg.BtcEncode(&buf, pver, encoding)
if err != nil {
t.Error(err.Error())
}
// expected = 0x0c + serializedpayload + 0x08 + signature
expectedBuf := append([]byte{0x0c}, serializedpayload...)
expectedBuf = append(expectedBuf, []byte{0x08}...)
expectedBuf = append(expectedBuf, signature...)
if !bytes.Equal(buf.Bytes(), expectedBuf) {
t.Errorf("BtcEncode got: %s want: %s",
spew.Sdump(buf.Bytes()), spew.Sdump(expectedBuf))
}
// Test BtcEncode with Payload != nil
// note: Payload is an empty Alert but not nil
msg.Payload = new(Alert)
buf = *new(bytes.Buffer)
err = msg.BtcEncode(&buf, pver, encoding)
if err != nil {
t.Error(err.Error())
}
// empty Alert is 45 null bytes, see Alert comments
// for details
// expected = 0x2d + 45*0x00 + 0x08 + signature
expectedBuf = append([]byte{0x2d}, bytes.Repeat([]byte{0x00}, 45)...)
expectedBuf = append(expectedBuf, []byte{0x08}...)
expectedBuf = append(expectedBuf, signature...)
if !bytes.Equal(buf.Bytes(), expectedBuf) {
t.Errorf("BtcEncode got: %s want: %s",
spew.Sdump(buf.Bytes()), spew.Sdump(expectedBuf))
}
}
// TestMsgAlertWire tests the MsgAlert wire encode and decode for various protocol
// versions.
func TestMsgAlertWire(t *testing.T) {
baseMsgAlert := NewMsgAlert([]byte("some payload"), []byte("somesig"))
baseMsgAlertEncoded := []byte{
0x0c, // Varint for payload length
0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x61, 0x79,
0x6c, 0x6f, 0x61, 0x64, // "some payload"
0x07, // Varint for signature length
0x73, 0x6f, 0x6d, 0x65, 0x73, 0x69, 0x67, // "somesig"
}
tests := []struct {
in *MsgAlert // Message to encode
out *MsgAlert // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgAlert
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestMsgAlertWireErrors performs negative tests against wire encode and decode
// of MsgAlert to confirm error paths work correctly.
func TestMsgAlertWireErrors(t *testing.T) {
pver := ProtocolVersion
encoding := BaseEncoding
baseMsgAlert := NewMsgAlert([]byte("some payload"), []byte("somesig"))
baseMsgAlertEncoded := []byte{
0x0c, // Varint for payload length
0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x61, 0x79,
0x6c, 0x6f, 0x61, 0x64, // "some payload"
0x07, // Varint for signature length
0x73, 0x6f, 0x6d, 0x65, 0x73, 0x69, 0x67, // "somesig"
}
tests := []struct {
in *MsgAlert // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in payload length.
{baseMsgAlert, baseMsgAlertEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in payload.
{baseMsgAlert, baseMsgAlertEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF},
// Force error in signature length.
{baseMsgAlert, baseMsgAlertEncoded, pver, BaseEncoding, 13, io.ErrShortWrite, io.EOF},
// Force error in signature.
{baseMsgAlert, baseMsgAlertEncoded, pver, BaseEncoding, 14, io.ErrShortWrite, io.EOF},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgAlert
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
// Test Error on empty Payload
baseMsgAlert.SerializedPayload = []byte{}
w := new(bytes.Buffer)
err := baseMsgAlert.BtcEncode(w, pver, encoding)
if _, ok := err.(*MessageError); !ok {
t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T",
err, MessageError{})
}
// Test Payload Serialize error
// overflow the max number of elements in SetCancel
baseMsgAlert.Payload = new(Alert)
baseMsgAlert.Payload.SetCancel = make([]int32, maxCountSetCancel+1)
buf := *new(bytes.Buffer)
err = baseMsgAlert.BtcEncode(&buf, pver, encoding)
if _, ok := err.(*MessageError); !ok {
t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T",
err, MessageError{})
}
// overflow the max number of elements in SetSubVer
baseMsgAlert.Payload = new(Alert)
baseMsgAlert.Payload.SetSubVer = make([]string, maxCountSetSubVer+1)
buf = *new(bytes.Buffer)
err = baseMsgAlert.BtcEncode(&buf, pver, encoding)
if _, ok := err.(*MessageError); !ok {
t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T",
err, MessageError{})
}
}
// TestAlert tests serialization and deserialization
// of the payload to Alert
func TestAlert(t *testing.T) {
pver := ProtocolVersion
alert := NewAlert(
1, 1337093712, 1368628812, 1015,
1013, []int32{1014}, 0, 40599, []string{"/Satoshi:0.7.2/"}, 5000, "",
"URGENT: upgrade required, see http://bitcoin.org/dos for details",
)
w := new(bytes.Buffer)
err := alert.Serialize(w, pver)
if err != nil {
t.Error(err.Error())
}
serializedpayload := w.Bytes()
newAlert, err := NewAlertFromPayload(serializedpayload, pver)
if err != nil {
t.Error(err.Error())
}
if alert.Version != newAlert.Version {
t.Errorf("NewAlertFromPayload: wrong Version - got %v, want %v ",
alert.Version, newAlert.Version)
}
if alert.RelayUntil != newAlert.RelayUntil {
t.Errorf("NewAlertFromPayload: wrong RelayUntil - got %v, want %v ",
alert.RelayUntil, newAlert.RelayUntil)
}
if alert.Expiration != newAlert.Expiration {
t.Errorf("NewAlertFromPayload: wrong Expiration - got %v, want %v ",
alert.Expiration, newAlert.Expiration)
}
if alert.ID != newAlert.ID {
t.Errorf("NewAlertFromPayload: wrong ID - got %v, want %v ",
alert.ID, newAlert.ID)
}
if alert.Cancel != newAlert.Cancel {
t.Errorf("NewAlertFromPayload: wrong Cancel - got %v, want %v ",
alert.Cancel, newAlert.Cancel)
}
if len(alert.SetCancel) != len(newAlert.SetCancel) {
t.Errorf("NewAlertFromPayload: wrong number of SetCancel - got %v, want %v ",
len(alert.SetCancel), len(newAlert.SetCancel))
}
for i := 0; i < len(alert.SetCancel); i++ {
if alert.SetCancel[i] != newAlert.SetCancel[i] {
t.Errorf("NewAlertFromPayload: wrong SetCancel[%v] - got %v, want %v ",
len(alert.SetCancel), alert.SetCancel[i], newAlert.SetCancel[i])
}
}
if alert.MinVer != newAlert.MinVer {
t.Errorf("NewAlertFromPayload: wrong MinVer - got %v, want %v ",
alert.MinVer, newAlert.MinVer)
}
if alert.MaxVer != newAlert.MaxVer {
t.Errorf("NewAlertFromPayload: wrong MaxVer - got %v, want %v ",
alert.MaxVer, newAlert.MaxVer)
}
if len(alert.SetSubVer) != len(newAlert.SetSubVer) {
t.Errorf("NewAlertFromPayload: wrong number of SetSubVer - got %v, want %v ",
len(alert.SetSubVer), len(newAlert.SetSubVer))
}
for i := 0; i < len(alert.SetSubVer); i++ {
if alert.SetSubVer[i] != newAlert.SetSubVer[i] {
t.Errorf("NewAlertFromPayload: wrong SetSubVer[%v] - got %v, want %v ",
len(alert.SetSubVer), alert.SetSubVer[i], newAlert.SetSubVer[i])
}
}
if alert.Priority != newAlert.Priority {
t.Errorf("NewAlertFromPayload: wrong Priority - got %v, want %v ",
alert.Priority, newAlert.Priority)
}
if alert.Comment != newAlert.Comment {
t.Errorf("NewAlertFromPayload: wrong Comment - got %v, want %v ",
alert.Comment, newAlert.Comment)
}
if alert.StatusBar != newAlert.StatusBar {
t.Errorf("NewAlertFromPayload: wrong StatusBar - got %v, want %v ",
alert.StatusBar, newAlert.StatusBar)
}
if alert.Reserved != newAlert.Reserved {
t.Errorf("NewAlertFromPayload: wrong Reserved - got %v, want %v ",
alert.Reserved, newAlert.Reserved)
}
}
// TestAlertErrors performs negative tests against payload serialization,
// deserialization of Alert to confirm error paths work correctly.
func TestAlertErrors(t *testing.T) {
pver := ProtocolVersion
baseAlert := NewAlert(
1, 1337093712, 1368628812, 1015,
1013, []int32{1014}, 0, 40599, []string{"/Satoshi:0.7.2/"}, 5000, "",
"URGENT",
)
baseAlertEncoded := []byte{
0x01, 0x00, 0x00, 0x00, 0x50, 0x6e, 0xb2, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9e, 0x93, 0x51, //|....Pn.O....L..Q|
0x00, 0x00, 0x00, 0x00, 0xf7, 0x03, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0x01, 0xf6, 0x03, 0x00, //|................|
0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9e, 0x00, 0x00, 0x01, 0x0f, 0x2f, 0x53, 0x61, 0x74, 0x6f, //|.........../Sato|
0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x37, 0x2e, 0x32, 0x2f, 0x88, 0x13, 0x00, 0x00, 0x00, 0x06, //|shi:0.7.2/......|
0x55, 0x52, 0x47, 0x45, 0x4e, 0x54, 0x00, //|URGENT.|
}
tests := []struct {
in *Alert // 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
}{
// Force error in Version
{baseAlert, baseAlertEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in SetCancel VarInt.
{baseAlert, baseAlertEncoded, pver, 28, io.ErrShortWrite, io.EOF},
// Force error in SetCancel ints.
{baseAlert, baseAlertEncoded, pver, 29, io.ErrShortWrite, io.EOF},
// Force error in MinVer
{baseAlert, baseAlertEncoded, pver, 40, io.ErrShortWrite, io.EOF},
// Force error in SetSubVer string VarInt.
{baseAlert, baseAlertEncoded, pver, 41, io.ErrShortWrite, io.EOF},
// Force error in SetSubVer strings.
{baseAlert, baseAlertEncoded, pver, 48, io.ErrShortWrite, io.EOF},
// Force error in Priority
{baseAlert, baseAlertEncoded, pver, 60, io.ErrShortWrite, io.EOF},
// Force error in Comment string.
{baseAlert, baseAlertEncoded, pver, 62, io.ErrShortWrite, io.EOF},
// Force error in StatusBar string.
{baseAlert, baseAlertEncoded, pver, 64, io.ErrShortWrite, io.EOF},
// Force error in Reserved string.
{baseAlert, baseAlertEncoded, pver, 70, io.ErrShortWrite, io.EOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
w := newFixedWriter(test.max)
err := test.in.Serialize(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("Alert.Serialize #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
var alert Alert
r := newFixedReader(test.max, test.buf)
err = alert.Deserialize(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("Alert.Deserialize #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
// overflow the max number of elements in SetCancel
// maxCountSetCancel + 1 == 8388575 == \xdf\xff\x7f\x00
// replace bytes 29-33
badAlertEncoded := []byte{
0x01, 0x00, 0x00, 0x00, 0x50, 0x6e, 0xb2, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9e, 0x93, 0x51, //|....Pn.O....L..Q|
0x00, 0x00, 0x00, 0x00, 0xf7, 0x03, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0xfe, 0xdf, 0xff, 0x7f, //|................|
0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9e, 0x00, 0x00, 0x01, 0x0f, 0x2f, 0x53, 0x61, 0x74, 0x6f, //|.........../Sato|
0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x37, 0x2e, 0x32, 0x2f, 0x88, 0x13, 0x00, 0x00, 0x00, 0x06, //|shi:0.7.2/......|
0x55, 0x52, 0x47, 0x45, 0x4e, 0x54, 0x00, //|URGENT.|
}
var alert Alert
r := bytes.NewReader(badAlertEncoded)
err := alert.Deserialize(r, pver)
if _, ok := err.(*MessageError); !ok {
t.Errorf("Alert.Deserialize wrong error got: %T, want: %T",
err, MessageError{})
}
// overflow the max number of elements in SetSubVer
// maxCountSetSubVer + 1 == 131071 + 1 == \x00\x00\x02\x00
// replace bytes 42-46
badAlertEncoded = []byte{
0x01, 0x00, 0x00, 0x00, 0x50, 0x6e, 0xb2, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9e, 0x93, 0x51, //|....Pn.O....L..Q|
0x00, 0x00, 0x00, 0x00, 0xf7, 0x03, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0x01, 0xf6, 0x03, 0x00, //|................|
0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9e, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x74, 0x6f, //|.........../Sato|
0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x37, 0x2e, 0x32, 0x2f, 0x88, 0x13, 0x00, 0x00, 0x00, 0x06, //|shi:0.7.2/......|
0x55, 0x52, 0x47, 0x45, 0x4e, 0x54, 0x00, //|URGENT.|
}
r = bytes.NewReader(badAlertEncoded)
err = alert.Deserialize(r, pver)
if _, ok := err.(*MessageError); !ok {
t.Errorf("Alert.Deserialize wrong error got: %T, want: %T",
err, MessageError{})
}
}
+290
View File
@@ -0,0 +1,290 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// defaultTransactionAlloc is the default size used for the backing array
// for transactions. The transaction array will dynamically grow as needed, but
// this figure is intended to provide enough space for the number of
// transactions in the vast majority of blocks without needing to grow the
// backing array multiple times.
const defaultTransactionAlloc = 2048
// MaxBlocksPerMsg is the maximum number of blocks allowed per message.
const MaxBlocksPerMsg = 500
// MaxBlockPayload is the maximum bytes a block message can be in bytes.
// After Segregated Witness, the max block payload has been raised to 4MB.
const MaxBlockPayload = 4000000
// maxTxPerBlock is the maximum number of transactions that could
// possibly fit into a block.
const maxTxPerBlock = (MaxBlockPayload / minTxPayload) + 1
// TxLoc holds locator data for the offset and length of where a transaction is
// located within a MsgBlock data buffer.
type TxLoc struct {
TxStart int
TxLen int
}
// MsgBlock implements the Message interface and represents a bitcoin
// block message. It is used to deliver block and transaction information in
// response to a getdata message (MsgGetData) for a given block hash.
type MsgBlock struct {
Header BlockHeader
Transactions []*MsgTx
}
// AddTransaction adds a transaction to the message.
func (msg *MsgBlock) AddTransaction(tx *MsgTx) error {
msg.Transactions = append(msg.Transactions, tx)
return nil
}
// ClearTransactions removes all transactions from the message.
func (msg *MsgBlock) ClearTransactions() {
msg.Transactions = make([]*MsgTx, 0, defaultTransactionAlloc)
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
// See Deserialize for decoding blocks stored to disk, such as in a database, as
// opposed to decoding blocks from the wire.
func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
err := readBlockHeader(r, pver, &msg.Header)
if err != nil {
return err
}
txCount, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Prevent more transactions than could possibly fit into a block.
// It would be possible to cause memory exhaustion and panics without
// a sane upper bound on this count.
if txCount > maxTxPerBlock {
str := fmt.Sprintf("too many transactions to fit into a block "+
"[count %d, max %d]", txCount, maxTxPerBlock)
return messageError("MsgBlock.BtcDecode", str)
}
msg.Transactions = make([]*MsgTx, 0, txCount)
for i := uint64(0); i < txCount; i++ {
tx := MsgTx{}
err := tx.BtcDecode(r, pver, enc)
if err != nil {
return err
}
msg.Transactions = append(msg.Transactions, &tx)
}
return nil
}
// Deserialize decodes a block from r into the receiver using a format that is
// suitable for long-term storage such as a database while respecting the
// Version field in the block. This function differs from BtcDecode in that
// BtcDecode decodes from the bitcoin wire protocol as it was sent across the
// network. The wire encoding can technically differ depending on the protocol
// version and doesn't even really need to match the format of a stored block at
// all. As of the time this comment was written, the encoded block is the same
// in both instances, but there is a distinct difference and separating the two
// allows the API to be flexible enough to deal with changes.
func (msg *MsgBlock) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of BtcDecode.
//
// Passing an encoding type of WitnessEncoding to BtcEncode for the
// MessageEncoding parameter indicates that the transactions within the
// block are expected to be serialized according to the new
// serialization structure defined in BIP0141.
return msg.BtcDecode(r, 0, WitnessEncoding)
}
// DeserializeNoWitness decodes a block from r into the receiver similar to
// Deserialize, however DeserializeWitness strips all (if any) witness data
// from the transactions within the block before encoding them.
func (msg *MsgBlock) DeserializeNoWitness(r io.Reader) error {
return msg.BtcDecode(r, 0, BaseEncoding)
}
// DeserializeTxLoc decodes r in the same manner Deserialize does, but it takes
// a byte buffer instead of a generic reader and returns a slice containing the
// start and length of each transaction within the raw data that is being
// deserialized.
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
fullLen := r.Len()
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of existing wire protocol functions.
err := readBlockHeader(r, 0, &msg.Header)
if err != nil {
return nil, err
}
txCount, err := ReadVarInt(r, 0)
if err != nil {
return nil, err
}
// Prevent more transactions than could possibly fit into a block.
// It would be possible to cause memory exhaustion and panics without
// a sane upper bound on this count.
if txCount > maxTxPerBlock {
str := fmt.Sprintf("too many transactions to fit into a block "+
"[count %d, max %d]", txCount, maxTxPerBlock)
return nil, messageError("MsgBlock.DeserializeTxLoc", str)
}
// Deserialize each transaction while keeping track of its location
// within the byte stream.
msg.Transactions = make([]*MsgTx, 0, txCount)
txLocs := make([]TxLoc, txCount)
for i := uint64(0); i < txCount; i++ {
txLocs[i].TxStart = fullLen - r.Len()
tx := MsgTx{}
err := tx.Deserialize(r)
if err != nil {
return nil, err
}
msg.Transactions = append(msg.Transactions, &tx)
txLocs[i].TxLen = (fullLen - r.Len()) - txLocs[i].TxStart
}
return txLocs, nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
// See Serialize for encoding blocks to be stored to disk, such as in a
// database, as opposed to encoding blocks for the wire.
func (msg *MsgBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
err := writeBlockHeader(w, pver, &msg.Header)
if err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(len(msg.Transactions)))
if err != nil {
return err
}
for _, tx := range msg.Transactions {
err = tx.BtcEncode(w, pver, enc)
if err != nil {
return err
}
}
return nil
}
// Serialize encodes the block to w using a format that suitable for long-term
// storage such as a database while respecting the Version field in the block.
// This function differs from BtcEncode in that BtcEncode encodes the block to
// the bitcoin wire protocol in order to be sent across the network. The wire
// encoding can technically differ depending on the protocol version and doesn't
// even really need to match the format of a stored block at all. As of the
// time this comment was written, the encoded block is the same in both
// instances, but there is a distinct difference and separating the two allows
// the API to be flexible enough to deal with changes.
func (msg *MsgBlock) Serialize(w io.Writer) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of BtcEncode.
//
// Passing WitnessEncoding as the encoding type here indicates that
// each of the transactions should be serialized using the witness
// serialization structure defined in BIP0141.
return msg.BtcEncode(w, 0, WitnessEncoding)
}
// SerializeNoWitness encodes a block to w using an identical format to
// Serialize, with all (if any) witness data stripped from all transactions.
// This method is provided in additon to the regular Serialize, in order to
// allow one to selectively encode transaction witness data to non-upgraded
// peers which are unaware of the new encoding.
func (msg *MsgBlock) SerializeNoWitness(w io.Writer) error {
return msg.BtcEncode(w, 0, BaseEncoding)
}
// SerializeSize returns the number of bytes it would take to serialize the
// block, factoring in any witness data within transaction.
func (msg *MsgBlock) SerializeSize() int {
// Block header bytes + Serialized varint size for the number of
// transactions.
n := blockHeaderLen + VarIntSerializeSize(uint64(len(msg.Transactions)))
for _, tx := range msg.Transactions {
n += tx.SerializeSize()
}
return n
}
// SerializeSizeStripped returns the number of bytes it would take to serialize
// the block, excluding any witness data (if any).
func (msg *MsgBlock) SerializeSizeStripped() int {
// Block header bytes + Serialized varint size for the number of
// transactions.
n := blockHeaderLen + VarIntSerializeSize(uint64(len(msg.Transactions)))
for _, tx := range msg.Transactions {
n += tx.SerializeSizeStripped()
}
return n
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgBlock) Command() string {
return CmdBlock
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
// Block header at 80 bytes + transaction count + max transactions
// which can vary up to the MaxBlockPayload (including the block header
// and transaction count).
return MaxBlockPayload
}
// BlockHash computes the block identifier hash for this block.
func (msg *MsgBlock) BlockHash() chainhash.Hash {
return msg.Header.BlockHash()
}
// TxHashes returns a slice of hashes of all of transactions in this block.
func (msg *MsgBlock) TxHashes() ([]chainhash.Hash, error) {
hashList := make([]chainhash.Hash, 0, len(msg.Transactions))
for _, tx := range msg.Transactions {
hashList = append(hashList, tx.TxHash())
}
return hashList, nil
}
// NewMsgBlock returns a new bitcoin block message that conforms to the
// Message interface. See MsgBlock for details.
func NewMsgBlock(blockHeader *BlockHeader) *MsgBlock {
return &MsgBlock{
Header: *blockHeader,
Transactions: make([]*MsgTx, 0, defaultTransactionAlloc),
}
}
+589
View File
@@ -0,0 +1,589 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestBlock tests the MsgBlock API.
func TestBlock(t *testing.T) {
pver := ProtocolVersion
// Block 1 header.
prevHash := &blockOne.Header.PrevBlock
merkleHash := &blockOne.Header.MerkleRoot
bits := blockOne.Header.Bits
nonce := blockOne.Header.Nonce
bh := NewBlockHeader(1, prevHash, merkleHash, bits, nonce)
// Ensure the command is expected value.
wantCmd := "block"
msg := NewMsgBlock(bh)
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgBlock: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num addresses (varInt) + max allowed addresses.
wantPayload := uint32(4000000)
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 we get the same block header data back out.
if !reflect.DeepEqual(&msg.Header, bh) {
t.Errorf("NewMsgBlock: wrong block header - got %v, want %v",
spew.Sdump(&msg.Header), spew.Sdump(bh))
}
// Ensure transactions are added properly.
tx := blockOne.Transactions[0].Copy()
msg.AddTransaction(tx)
if !reflect.DeepEqual(msg.Transactions, blockOne.Transactions) {
t.Errorf("AddTransaction: wrong transactions - got %v, want %v",
spew.Sdump(msg.Transactions),
spew.Sdump(blockOne.Transactions))
}
// Ensure transactions are properly cleared.
msg.ClearTransactions()
if len(msg.Transactions) != 0 {
t.Errorf("ClearTransactions: wrong transactions - got %v, want %v",
len(msg.Transactions), 0)
}
}
// TestBlockTxHashes tests the ability to generate a slice of all transaction
// hashes from a block accurately.
func TestBlockTxHashes(t *testing.T) {
// Block 1, transaction 1 hash.
hashStr := "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"
wantHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
return
}
wantHashes := []chainhash.Hash{*wantHash}
hashes, err := blockOne.TxHashes()
if err != nil {
t.Errorf("TxHashes: %v", err)
}
if !reflect.DeepEqual(hashes, wantHashes) {
t.Errorf("TxHashes: wrong transaction hashes - got %v, want %v",
spew.Sdump(hashes), spew.Sdump(wantHashes))
}
}
// TestBlockHash tests the ability to generate the hash of a block accurately.
func TestBlockHash(t *testing.T) {
// Block 1 hash.
hashStr := "839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"
wantHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Ensure the hash produced is expected.
blockHash := blockOne.BlockHash()
if !blockHash.IsEqual(wantHash) {
t.Errorf("BlockHash: wrong hash - got %v, want %v",
spew.Sprint(blockHash), spew.Sprint(wantHash))
}
}
// TestBlockWire tests the MsgBlock wire encode and decode for various numbers
// of transaction inputs and outputs and protocol versions.
func TestBlockWire(t *testing.T) {
tests := []struct {
in *MsgBlock // Message to encode
out *MsgBlock // Expected decoded message
buf []byte // Wire encoding
txLocs []TxLoc // Expected transaction locations
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
MultipleAddressVersion,
BaseEncoding,
},
// TODO(roasbeef): add case for witnessy block
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgBlock
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(&msg), spew.Sdump(test.out))
continue
}
}
}
// TestBlockWireErrors performs negative tests against wire encode and decode
// of MsgBlock to confirm error paths work correctly.
func TestBlockWireErrors(t *testing.T) {
// Use protocol version 60002 specifically here instead of the latest
// because the test data is using bytes encoded with that protocol
// version.
pver := uint32(60002)
tests := []struct {
in *MsgBlock // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in version.
{&blockOne, blockOneBytes, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in prev block hash.
{&blockOne, blockOneBytes, pver, BaseEncoding, 4, io.ErrShortWrite, io.EOF},
// Force error in merkle root.
{&blockOne, blockOneBytes, pver, BaseEncoding, 36, io.ErrShortWrite, io.EOF},
// Force error in timestamp.
{&blockOne, blockOneBytes, pver, BaseEncoding, 68, io.ErrShortWrite, io.EOF},
// Force error in difficulty bits.
{&blockOne, blockOneBytes, pver, BaseEncoding, 72, io.ErrShortWrite, io.EOF},
// Force error in header nonce.
{&blockOne, blockOneBytes, pver, BaseEncoding, 76, io.ErrShortWrite, io.EOF},
// Force error in transaction count.
{&blockOne, blockOneBytes, pver, BaseEncoding, 80, io.ErrShortWrite, io.EOF},
// Force error in transactions.
{&blockOne, blockOneBytes, pver, BaseEncoding, 81, io.ErrShortWrite, io.EOF},
}
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, test.enc)
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 MsgBlock
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestBlockSerialize tests MsgBlock serialize and deserialize.
func TestBlockSerialize(t *testing.T) {
tests := []struct {
in *MsgBlock // Message to encode
out *MsgBlock // Expected decoded message
buf []byte // Serialized data
txLocs []TxLoc // Expected transaction locations
}{
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the block.
var buf bytes.Buffer
err := test.in.Serialize(&buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("Serialize #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Deserialize the block.
var block MsgBlock
rbuf := bytes.NewReader(test.buf)
err = block.Deserialize(rbuf)
if err != nil {
t.Errorf("Deserialize #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&block, test.out) {
t.Errorf("Deserialize #%d\n got: %s want: %s", i,
spew.Sdump(&block), spew.Sdump(test.out))
continue
}
// Deserialize the block while gathering transaction location
// information.
var txLocBlock MsgBlock
br := bytes.NewBuffer(test.buf)
txLocs, err := txLocBlock.DeserializeTxLoc(br)
if err != nil {
t.Errorf("DeserializeTxLoc #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&txLocBlock, test.out) {
t.Errorf("DeserializeTxLoc #%d\n got: %s want: %s", i,
spew.Sdump(&txLocBlock), spew.Sdump(test.out))
continue
}
if !reflect.DeepEqual(txLocs, test.txLocs) {
t.Errorf("DeserializeTxLoc #%d\n got: %s want: %s", i,
spew.Sdump(txLocs), spew.Sdump(test.txLocs))
continue
}
}
}
// TestBlockSerializeErrors performs negative tests against wire encode and
// decode of MsgBlock to confirm error paths work correctly.
func TestBlockSerializeErrors(t *testing.T) {
tests := []struct {
in *MsgBlock // Value to encode
buf []byte // Serialized data
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in version.
{&blockOne, blockOneBytes, 0, io.ErrShortWrite, io.EOF},
// Force error in prev block hash.
{&blockOne, blockOneBytes, 4, io.ErrShortWrite, io.EOF},
// Force error in merkle root.
{&blockOne, blockOneBytes, 36, io.ErrShortWrite, io.EOF},
// Force error in timestamp.
{&blockOne, blockOneBytes, 68, io.ErrShortWrite, io.EOF},
// Force error in difficulty bits.
{&blockOne, blockOneBytes, 72, io.ErrShortWrite, io.EOF},
// Force error in header nonce.
{&blockOne, blockOneBytes, 76, io.ErrShortWrite, io.EOF},
// Force error in transaction count.
{&blockOne, blockOneBytes, 80, io.ErrShortWrite, io.EOF},
// Force error in transactions.
{&blockOne, blockOneBytes, 81, io.ErrShortWrite, io.EOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the block.
w := newFixedWriter(test.max)
err := test.in.Serialize(w)
if err != test.writeErr {
t.Errorf("Serialize #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// Deserialize the block.
var block MsgBlock
r := newFixedReader(test.max, test.buf)
err = block.Deserialize(r)
if err != test.readErr {
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
var txLocBlock MsgBlock
br := bytes.NewBuffer(test.buf[0:test.max])
_, err = txLocBlock.DeserializeTxLoc(br)
if err != test.readErr {
t.Errorf("DeserializeTxLoc #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestBlockOverflowErrors performs tests to ensure deserializing blocks which
// are intentionally crafted to use large values for the number of transactions
// are handled properly. This could otherwise potentially be used as an attack
// vector.
func TestBlockOverflowErrors(t *testing.T) {
// Use protocol version 70001 specifically here instead of the latest
// protocol version because the test data is using bytes encoded with
// that version.
pver := uint32(70001)
tests := []struct {
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
err error // Expected error
}{
// Block that claims to have ~uint64(0) transactions.
{
[]byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, // TxnCount
}, pver, BaseEncoding, &MessageError{},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
var msg MsgBlock
r := bytes.NewReader(test.buf)
err := msg.BtcDecode(r, test.pver, test.enc)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, reflect.TypeOf(test.err))
continue
}
// Deserialize from wire format.
r = bytes.NewReader(test.buf)
err = msg.Deserialize(r)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
i, err, reflect.TypeOf(test.err))
continue
}
// Deserialize with transaction location info from wire format.
br := bytes.NewBuffer(test.buf)
_, err = msg.DeserializeTxLoc(br)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("DeserializeTxLoc #%d wrong error got: %v, "+
"want: %v", i, err, reflect.TypeOf(test.err))
continue
}
}
}
// TestBlockSerializeSize performs tests to ensure the serialize size for
// various blocks is accurate.
func TestBlockSerializeSize(t *testing.T) {
// Block with no transactions.
noTxBlock := NewMsgBlock(&blockOne.Header)
tests := []struct {
in *MsgBlock // Block to encode
size int // Expected serialized size
}{
// Block with no transactions.
{noTxBlock, 81},
// First block in the mainnet block chain.
{&blockOne, len(blockOneBytes)},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
serializedSize := test.in.SerializeSize()
if serializedSize != test.size {
t.Errorf("MsgBlock.SerializeSize: #%d got: %d, want: "+
"%d", i, serializedSize, test.size)
continue
}
}
}
// blockOne is the first block in the mainnet block chain.
var blockOne = MsgBlock{
Header: BlockHeader{
Version: 1,
PrevBlock: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
MerkleRoot: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
}),
Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST
Bits: 0x1d00ffff, // 486604799
Nonce: 0x9962e301, // 2573394689
},
Transactions: []*MsgTx{
{
Version: 1,
TxIn: []*TxIn{
{
PreviousOutPoint: OutPoint{
Hash: chainhash.Hash{},
Index: 0xffffffff,
},
SignatureScript: []byte{
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04,
},
Sequence: 0xffffffff,
},
},
TxOut: []*TxOut{
{
Value: 0x12a05f200,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
0xee, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
},
}
// Block one serialized bytes.
var blockOneBytes = []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0x01, // TxnCount
0x01, 0x00, 0x00, 0x00, // Version
0x01, // Varint for number of transaction inputs
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
0xff, 0xff, 0xff, 0xff, // Prevous output index
0x07, // Varint for length of signature script
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script (coinbase)
0xff, 0xff, 0xff, 0xff, // Sequence
0x01, // Varint for number of transaction outputs
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
0xee, // 65-byte uncompressed public key
0xac, // OP_CHECKSIG
0x00, 0x00, 0x00, 0x00, // Lock time
}
// Transaction location information for block one transactions.
var blockOneTxLocs = []TxLoc{
{TxStart: 81, TxLen: 134},
}
+64
View File
@@ -0,0 +1,64 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgFeeFilter implements the Message interface and represents a bitcoin
// feefilter message. It is used to request the receiving peer does not
// announce any transactions below the specified minimum fee rate.
//
// This message was not added until protocol versions starting with
// FeeFilterVersion.
type MsgFeeFilter struct {
MinFee int64
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgFeeFilter) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < FeeFilterVersion {
str := fmt.Sprintf("feefilter message invalid for protocol "+
"version %d", pver)
return messageError("MsgFeeFilter.BtcDecode", str)
}
return readElement(r, &msg.MinFee)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgFeeFilter) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < FeeFilterVersion {
str := fmt.Sprintf("feefilter message invalid for protocol "+
"version %d", pver)
return messageError("MsgFeeFilter.BtcEncode", str)
}
return writeElement(w, msg.MinFee)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgFeeFilter) Command() string {
return CmdFeeFilter
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgFeeFilter) MaxPayloadLength(pver uint32) uint32 {
return 8
}
// NewMsgFeeFilter returns a new bitcoin feefilter message that conforms to
// the Message interface. See MsgFeeFilter for details.
func NewMsgFeeFilter(minfee int64) *MsgFeeFilter {
return &MsgFeeFilter{
MinFee: minfee,
}
}
+190
View File
@@ -0,0 +1,190 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"math/rand"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestFeeFilterLatest tests the MsgFeeFilter API against the latest protocol version.
func TestFeeFilterLatest(t *testing.T) {
pver := ProtocolVersion
minfee := rand.Int63()
msg := NewMsgFeeFilter(minfee)
if msg.MinFee != minfee {
t.Errorf("NewMsgFeeFilter: wrong minfee - got %v, want %v",
msg.MinFee, minfee)
}
// Ensure the command is expected value.
wantCmd := "feefilter"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgFeeFilter: 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, BaseEncoding)
if err != nil {
t.Errorf("encode of MsgFeeFilter failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := NewMsgFeeFilter(0)
err = readmsg.BtcDecode(&buf, pver, BaseEncoding)
if err != nil {
t.Errorf("decode of MsgFeeFilter failed [%v] err <%v>", buf, err)
}
// Ensure minfee is the same.
if msg.MinFee != readmsg.MinFee {
t.Errorf("Should get same minfee for protocol version %d", pver)
}
}
// TestFeeFilterWire tests the MsgFeeFilter wire encode and decode for various protocol
// versions.
func TestFeeFilterWire(t *testing.T) {
tests := []struct {
in MsgFeeFilter // Message to encode
out MsgFeeFilter // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
MsgFeeFilter{MinFee: 123123}, // 0x1e0f3
MsgFeeFilter{MinFee: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
ProtocolVersion,
},
// Protocol version FeeFilterVersion
{
MsgFeeFilter{MinFee: 456456}, // 0x6f708
MsgFeeFilter{MinFee: 456456}, // 0x6f708
[]byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
FeeFilterVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, BaseEncoding)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgFeeFilter
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, BaseEncoding)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestFeeFilterWireErrors performs negative tests against wire encode and decode
// of MsgFeeFilter to confirm error paths work correctly.
func TestFeeFilterWireErrors(t *testing.T) {
pver := ProtocolVersion
pverNoFeeFilter := FeeFilterVersion - 1
wireErr := &MessageError{}
baseFeeFilter := NewMsgFeeFilter(123123) // 0x1e0f3
baseFeeFilterEncoded := []byte{
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
}
tests := []struct {
in *MsgFeeFilter // 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 minfee.
{baseFeeFilter, baseFeeFilterEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error due to unsupported protocol version.
{baseFeeFilter, baseFeeFilterEncoded, pverNoFeeFilter, 4, wireErr, wireErr},
}
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, BaseEncoding)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgFeeFilter
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, BaseEncoding)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2014-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
const (
// MaxFilterAddDataSize is the maximum byte size of a data
// element to add to the Bloom filter. It is equal to the
// maximum element size of a script.
MaxFilterAddDataSize = 520
)
// MsgFilterAdd implements the Message interface and represents a bitcoin
// filteradd message. It is used to add a data element to an existing Bloom
// filter.
//
// This message was not added until protocol version BIP0037Version.
type MsgFilterAdd struct {
Data []byte
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgFilterAdd) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("filteradd message invalid for protocol "+
"version %d", pver)
return messageError("MsgFilterAdd.BtcDecode", str)
}
var err error
msg.Data, err = ReadVarBytes(r, pver, MaxFilterAddDataSize,
"filteradd data")
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("filteradd message invalid for protocol "+
"version %d", pver)
return messageError("MsgFilterAdd.BtcEncode", str)
}
size := len(msg.Data)
if size > MaxFilterAddDataSize {
str := fmt.Sprintf("filteradd size too large for message "+
"[size %v, max %v]", size, MaxFilterAddDataSize)
return messageError("MsgFilterAdd.BtcEncode", str)
}
return WriteVarBytes(w, pver, msg.Data)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgFilterAdd) Command() string {
return CmdFilterAdd
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgFilterAdd) MaxPayloadLength(pver uint32) uint32 {
return uint32(VarIntSerializeSize(MaxFilterAddDataSize)) +
MaxFilterAddDataSize
}
// NewMsgFilterAdd returns a new bitcoin filteradd message that conforms to the
// Message interface. See MsgFilterAdd for details.
func NewMsgFilterAdd(data []byte) *MsgFilterAdd {
return &MsgFilterAdd{
Data: data,
}
}
+187
View File
@@ -0,0 +1,187 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
)
// TestFilterAddLatest tests the MsgFilterAdd API against the latest protocol
// version.
func TestFilterAddLatest(t *testing.T) {
enc := BaseEncoding
pver := ProtocolVersion
data := []byte{0x01, 0x02}
msg := NewMsgFilterAdd(data)
// Ensure the command is expected value.
wantCmd := "filteradd"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgFilterAdd: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(523)
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, enc)
if err != nil {
t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
var readmsg MsgFilterAdd
err = readmsg.BtcDecode(&buf, pver, enc)
if err != nil {
t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err)
}
}
// TestFilterAddCrossProtocol tests the MsgFilterAdd API when encoding with the
// latest protocol version and decoding with BIP0031Version.
func TestFilterAddCrossProtocol(t *testing.T) {
data := []byte{0x01, 0x02}
msg := NewMsgFilterAdd(data)
if !bytes.Equal(msg.Data, data) {
t.Errorf("should get same data back out")
}
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, LatestEncoding)
if err != nil {
t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
var readmsg MsgFilterAdd
err = readmsg.BtcDecode(&buf, BIP0031Version, LatestEncoding)
if err == nil {
t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+
"have %v", msg)
}
// Since one of the protocol versions doesn't support the filteradd
// message, make sure the data didn't get encoded and decoded back out.
if bytes.Equal(msg.Data, readmsg.Data) {
t.Error("should not get same data for cross protocol")
}
}
// TestFilterAddMaxDataSize tests the MsgFilterAdd API maximum data size.
func TestFilterAddMaxDataSize(t *testing.T) {
data := bytes.Repeat([]byte{0xff}, 521)
msg := NewMsgFilterAdd(data)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, LatestEncoding)
if err == nil {
t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't "+
"have %v", msg)
}
// Decode with latest protocol version.
readbuf := bytes.NewReader(data)
err = msg.BtcDecode(readbuf, ProtocolVersion, LatestEncoding)
if err == nil {
t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+
"have %v", msg)
}
}
// TestFilterAddWireErrors performs negative tests against wire encode and decode
// of MsgFilterAdd to confirm error paths work correctly.
func TestFilterAddWireErrors(t *testing.T) {
pver := ProtocolVersion
pverNoFilterAdd := BIP0037Version - 1
wireErr := &MessageError{}
baseData := []byte{0x01, 0x02, 0x03, 0x04}
baseFilterAdd := NewMsgFilterAdd(baseData)
baseFilterAddEncoded := append([]byte{0x04}, baseData...)
tests := []struct {
in *MsgFilterAdd // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 data size.
{
baseFilterAdd, baseFilterAddEncoded, pver, BaseEncoding, 0,
io.ErrShortWrite, io.EOF,
},
// Force error in data.
{
baseFilterAdd, baseFilterAddEncoded, pver, BaseEncoding, 1,
io.ErrShortWrite, io.EOF,
},
// Force error due to unsupported protocol version.
{
baseFilterAdd, baseFilterAddEncoded, pverNoFilterAdd, BaseEncoding, 5,
wireErr, wireErr,
},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgFilterAdd
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2014-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgFilterClear implements the Message interface and represents a bitcoin
// filterclear message which is used to reset a Bloom filter.
//
// This message was not added until protocol version BIP0037Version and has
// no payload.
type MsgFilterClear struct{}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgFilterClear) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("filterclear message invalid for protocol "+
"version %d", pver)
return messageError("MsgFilterClear.BtcDecode", str)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgFilterClear) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("filterclear message invalid for protocol "+
"version %d", pver)
return messageError("MsgFilterClear.BtcEncode", str)
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgFilterClear) Command() string {
return CmdFilterClear
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgFilterClear) MaxPayloadLength(pver uint32) uint32 {
return 0
}
// NewMsgFilterClear returns a new bitcoin filterclear message that conforms to the Message
// interface. See MsgFilterClear for details.
func NewMsgFilterClear() *MsgFilterClear {
return &MsgFilterClear{}
}
+199
View File
@@ -0,0 +1,199 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestFilterCLearLatest tests the MsgFilterClear API against the latest
// protocol version.
func TestFilterClearLatest(t *testing.T) {
pver := ProtocolVersion
msg := NewMsgFilterClear()
// Ensure the command is expected value.
wantCmd := "filterclear"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgFilterClear: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest 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)
}
}
// TestFilterClearCrossProtocol tests the MsgFilterClear API when encoding with
// the latest protocol version and decoding with BIP0031Version.
func TestFilterClearCrossProtocol(t *testing.T) {
msg := NewMsgFilterClear()
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, LatestEncoding)
if err != nil {
t.Errorf("encode of MsgFilterClear failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
var readmsg MsgFilterClear
err = readmsg.BtcDecode(&buf, BIP0031Version, LatestEncoding)
if err == nil {
t.Errorf("decode of MsgFilterClear succeeded when it "+
"shouldn't have %v", msg)
}
}
// TestFilterClearWire tests the MsgFilterClear wire encode and decode for
// various protocol versions.
func TestFilterClearWire(t *testing.T) {
msgFilterClear := NewMsgFilterClear()
msgFilterClearEncoded := []byte{}
tests := []struct {
in *MsgFilterClear // Message to encode
out *MsgFilterClear // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
msgFilterClear,
msgFilterClear,
msgFilterClearEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0037Version + 1.
{
msgFilterClear,
msgFilterClear,
msgFilterClearEncoded,
BIP0037Version + 1,
BaseEncoding,
},
// Protocol version BIP0037Version.
{
msgFilterClear,
msgFilterClear,
msgFilterClearEncoded,
BIP0037Version,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgFilterClear
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestFilterClearWireErrors performs negative tests against wire encode and
// decode of MsgFilterClear to confirm error paths work correctly.
func TestFilterClearWireErrors(t *testing.T) {
pverNoFilterClear := BIP0037Version - 1
wireErr := &MessageError{}
baseFilterClear := NewMsgFilterClear()
baseFilterClearEncoded := []byte{}
tests := []struct {
in *MsgFilterClear // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error due to unsupported protocol version.
{
baseFilterClear, baseFilterClearEncoded,
pverNoFilterClear, BaseEncoding, 4, wireErr, wireErr,
},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgFilterClear
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) 2014-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// BloomUpdateType specifies how the filter is updated when a match is found
type BloomUpdateType uint8
const (
// BloomUpdateNone indicates the filter is not adjusted when a match is
// found.
BloomUpdateNone BloomUpdateType = 0
// BloomUpdateAll indicates if the filter matches any data element in a
// public key script, the outpoint is serialized and inserted into the
// filter.
BloomUpdateAll BloomUpdateType = 1
// BloomUpdateP2PubkeyOnly indicates if the filter matches a data
// element in a public key script and the script is of the standard
// pay-to-pubkey or multisig, the outpoint is serialized and inserted
// into the filter.
BloomUpdateP2PubkeyOnly BloomUpdateType = 2
)
const (
// MaxFilterLoadHashFuncs is the maximum number of hash functions to
// load into the Bloom filter.
MaxFilterLoadHashFuncs = 50
// MaxFilterLoadFilterSize is the maximum size in bytes a filter may be.
MaxFilterLoadFilterSize = 36000
)
// MsgFilterLoad implements the Message interface and represents a bitcoin
// filterload message which is used to reset a Bloom filter.
//
// This message was not added until protocol version BIP0037Version.
type MsgFilterLoad struct {
Filter []byte
HashFuncs uint32
Tweak uint32
Flags BloomUpdateType
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgFilterLoad) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("filterload message invalid for protocol "+
"version %d", pver)
return messageError("MsgFilterLoad.BtcDecode", str)
}
var err error
msg.Filter, err = ReadVarBytes(r, pver, MaxFilterLoadFilterSize,
"filterload filter size")
if err != nil {
return err
}
err = readElements(r, &msg.HashFuncs, &msg.Tweak, &msg.Flags)
if err != nil {
return err
}
if msg.HashFuncs > MaxFilterLoadHashFuncs {
str := fmt.Sprintf("too many filter hash functions for message "+
"[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs)
return messageError("MsgFilterLoad.BtcDecode", str)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("filterload message invalid for protocol "+
"version %d", pver)
return messageError("MsgFilterLoad.BtcEncode", str)
}
size := len(msg.Filter)
if size > MaxFilterLoadFilterSize {
str := fmt.Sprintf("filterload filter size too large for message "+
"[size %v, max %v]", size, MaxFilterLoadFilterSize)
return messageError("MsgFilterLoad.BtcEncode", str)
}
if msg.HashFuncs > MaxFilterLoadHashFuncs {
str := fmt.Sprintf("too many filter hash functions for message "+
"[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs)
return messageError("MsgFilterLoad.BtcEncode", str)
}
err := WriteVarBytes(w, pver, msg.Filter)
if err != nil {
return err
}
return writeElements(w, msg.HashFuncs, msg.Tweak, msg.Flags)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgFilterLoad) Command() string {
return CmdFilterLoad
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgFilterLoad) MaxPayloadLength(pver uint32) uint32 {
// Num filter bytes (varInt) + filter + 4 bytes hash funcs +
// 4 bytes tweak + 1 byte flags.
return uint32(VarIntSerializeSize(MaxFilterLoadFilterSize)) +
MaxFilterLoadFilterSize + 9
}
// NewMsgFilterLoad returns a new bitcoin filterload message that conforms to
// the Message interface. See MsgFilterLoad for details.
func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad {
return &MsgFilterLoad{
Filter: filter,
HashFuncs: hashFuncs,
Tweak: tweak,
Flags: flags,
}
}
+227
View File
@@ -0,0 +1,227 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
)
// TestFilterCLearLatest tests the MsgFilterLoad API against the latest protocol
// version.
func TestFilterLoadLatest(t *testing.T) {
pver := ProtocolVersion
enc := BaseEncoding
data := []byte{0x01, 0x02}
msg := NewMsgFilterLoad(data, 10, 0, 0)
// Ensure the command is expected value.
wantCmd := "filterload"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgFilterLoad: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(36012)
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, enc)
if err != nil {
t.Errorf("encode of MsgFilterLoad failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := MsgFilterLoad{}
err = readmsg.BtcDecode(&buf, pver, enc)
if err != nil {
t.Errorf("decode of MsgFilterLoad failed [%v] err <%v>", buf, err)
}
}
// TestFilterLoadCrossProtocol tests the MsgFilterLoad API when encoding with
// the latest protocol version and decoding with BIP0031Version.
func TestFilterLoadCrossProtocol(t *testing.T) {
data := []byte{0x01, 0x02}
msg := NewMsgFilterLoad(data, 10, 0, 0)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, BaseEncoding)
if err != nil {
t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg,
err)
}
// Decode with old protocol version.
var readmsg MsgFilterLoad
err = readmsg.BtcDecode(&buf, BIP0031Version, BaseEncoding)
if err == nil {
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v",
msg)
}
}
// TestFilterLoadMaxFilterSize tests the MsgFilterLoad API maximum filter size.
func TestFilterLoadMaxFilterSize(t *testing.T) {
data := bytes.Repeat([]byte{0xff}, 36001)
msg := NewMsgFilterLoad(data, 10, 0, 0)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, BaseEncoding)
if err == nil {
t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't "+
"have %v", msg)
}
// Decode with latest protocol version.
readbuf := bytes.NewReader(data)
err = msg.BtcDecode(readbuf, ProtocolVersion, BaseEncoding)
if err == nil {
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't "+
"have %v", msg)
}
}
// TestFilterLoadMaxHashFuncsSize tests the MsgFilterLoad API maximum hash functions.
func TestFilterLoadMaxHashFuncsSize(t *testing.T) {
data := bytes.Repeat([]byte{0xff}, 10)
msg := NewMsgFilterLoad(data, 61, 0, 0)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, BaseEncoding)
if err == nil {
t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v",
msg)
}
newBuf := []byte{
0x0a, // filter size
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // filter
0x3d, 0x00, 0x00, 0x00, // max hash funcs
0x00, 0x00, 0x00, 0x00, // tweak
0x00, // update Type
}
// Decode with latest protocol version.
readbuf := bytes.NewReader(newBuf)
err = msg.BtcDecode(readbuf, ProtocolVersion, BaseEncoding)
if err == nil {
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v",
msg)
}
}
// TestFilterLoadWireErrors performs negative tests against wire encode and decode
// of MsgFilterLoad to confirm error paths work correctly.
func TestFilterLoadWireErrors(t *testing.T) {
pver := ProtocolVersion
pverNoFilterLoad := BIP0037Version - 1
wireErr := &MessageError{}
baseFilter := []byte{0x01, 0x02, 0x03, 0x04}
baseFilterLoad := NewMsgFilterLoad(baseFilter, 10, 0, BloomUpdateNone)
baseFilterLoadEncoded := append([]byte{0x04}, baseFilter...)
baseFilterLoadEncoded = append(baseFilterLoadEncoded,
0x00, 0x00, 0x00, 0x0a, // HashFuncs
0x00, 0x00, 0x00, 0x00, // Tweak
0x00) // Flags
tests := []struct {
in *MsgFilterLoad // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 filter size.
{
baseFilterLoad, baseFilterLoadEncoded, pver, BaseEncoding, 0,
io.ErrShortWrite, io.EOF,
},
// Force error in filter.
{
baseFilterLoad, baseFilterLoadEncoded, pver, BaseEncoding, 1,
io.ErrShortWrite, io.EOF,
},
// Force error in hash funcs.
{
baseFilterLoad, baseFilterLoadEncoded, pver, BaseEncoding, 5,
io.ErrShortWrite, io.EOF,
},
// Force error in tweak.
{
baseFilterLoad, baseFilterLoadEncoded, pver, BaseEncoding, 9,
io.ErrShortWrite, io.EOF,
},
// Force error in flags.
{
baseFilterLoad, baseFilterLoadEncoded, pver, BaseEncoding, 13,
io.ErrShortWrite, io.EOF,
},
// Force error due to unsupported protocol version.
{
baseFilterLoad, baseFilterLoadEncoded, pverNoFilterLoad, BaseEncoding,
10, wireErr, wireErr,
},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgFilterLoad
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+47
View File
@@ -0,0 +1,47 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"io"
)
// MsgGetAddr implements the Message interface and represents a bitcoin
// getaddr message. It is used to request a list of known active peers on the
// network from a peer to help identify potential nodes. The list is returned
// via one or more addr messages (MsgAddr).
//
// This message has no payload.
type MsgGetAddr struct{}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetAddr) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetAddr) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetAddr) Command() string {
return CmdGetAddr
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetAddr) MaxPayloadLength(pver uint32) uint32 {
return 0
}
// NewMsgGetAddr returns a new bitcoin getaddr message that conforms to the
// Message interface. See MsgGetAddr for details.
func NewMsgGetAddr() *MsgGetAddr {
return &MsgGetAddr{}
}
+126
View File
@@ -0,0 +1,126 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestGetAddr tests the MsgGetAddr API.
func TestGetAddr(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "getaddr"
msg := NewMsgGetAddr()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgGetAddr: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num addresses (varInt) + max allowed addresses.
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)
}
}
// TestGetAddrWire tests the MsgGetAddr wire encode and decode for various
// protocol versions.
func TestGetAddrWire(t *testing.T) {
msgGetAddr := NewMsgGetAddr()
msgGetAddrEncoded := []byte{}
tests := []struct {
in *MsgGetAddr // Message to encode
out *MsgGetAddr // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding variant.
}{
// Latest protocol version.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgGetAddr
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
+139
View File
@@ -0,0 +1,139 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
// per message.
const MaxBlockLocatorsPerMsg = 500
// MsgGetBlocks implements the Message interface and represents a bitcoin
// getblocks message. It is used to request a list of blocks starting after the
// last known hash in the slice of block locator hashes. The list is returned
// via an inv message (MsgInv) and is limited by a specific hash to stop at or
// the maximum number of blocks per message, which is currently 500.
//
// Set the HashStop field to the hash at which to stop and use
// AddBlockLocatorHash to build up the list of block locator hashes.
//
// The algorithm for building the block locator hashes should be to add the
// hashes in reverse order until you reach the genesis block. In order to keep
// the list of locator hashes to a reasonable number of entries, first add the
// most recent 10 block hashes, then double the step each loop iteration to
// exponentially decrease the number of hashes the further away from head and
// closer to the genesis block you get.
type MsgGetBlocks struct {
ProtocolVersion uint32
BlockLocatorHashes []*chainhash.Hash
HashStop chainhash.Hash
}
// AddBlockLocatorHash adds a new block locator hash to the message.
func (msg *MsgGetBlocks) AddBlockLocatorHash(hash *chainhash.Hash) error {
if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message [max %v]",
MaxBlockLocatorsPerMsg)
return messageError("MsgGetBlocks.AddBlockLocatorHash", str)
}
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
err := readElement(r, &msg.ProtocolVersion)
if err != nil {
return err
}
// Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
return messageError("MsgGetBlocks.BtcDecode", str)
}
// Create a contiguous slice of hashes to deserialize into in order to
// reduce the number of allocations.
locatorHashes := make([]chainhash.Hash, count)
msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
hash := &locatorHashes[i]
err := readElement(r, hash)
if err != nil {
return err
}
msg.AddBlockLocatorHash(hash)
}
return readElement(r, &msg.HashStop)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
count := len(msg.BlockLocatorHashes)
if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
return messageError("MsgGetBlocks.BtcEncode", str)
}
err := writeElement(w, msg.ProtocolVersion)
if err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, hash := range msg.BlockLocatorHashes {
err = writeElement(w, hash)
if err != nil {
return err
}
}
return writeElement(w, &msg.HashStop)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetBlocks) Command() string {
return CmdGetBlocks
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetBlocks) MaxPayloadLength(pver uint32) uint32 {
// Protocol version 4 bytes + num hashes (varInt) + max block locator
// hashes + hash stop.
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * chainhash.HashSize) + chainhash.HashSize
}
// NewMsgGetBlocks returns a new bitcoin getblocks message that conforms to the
// Message interface using the passed parameters and defaults for the remaining
// fields.
func NewMsgGetBlocks(hashStop *chainhash.Hash) *MsgGetBlocks {
return &MsgGetBlocks{
ProtocolVersion: ProtocolVersion,
BlockLocatorHashes: make([]*chainhash.Hash, 0, MaxBlockLocatorsPerMsg),
HashStop: *hashStop,
}
}
+400
View File
@@ -0,0 +1,400 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestGetBlocks tests the MsgGetBlocks API.
func TestGetBlocks(t *testing.T) {
pver := ProtocolVersion
// Block 99500 hash.
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
locatorHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Ensure we get the same data back out.
msg := NewMsgGetBlocks(hashStop)
if !msg.HashStop.IsEqual(hashStop) {
t.Errorf("NewMsgGetBlocks: wrong stop hash - got %v, want %v",
msg.HashStop, hashStop)
}
// Ensure the command is expected value.
wantCmd := "getblocks"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgGetBlocks: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Protocol version 4 bytes + num hashes (varInt) + max block locator
// hashes + hash stop.
wantPayload := uint32(16045)
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 block locator hashes are added properly.
err = msg.AddBlockLocatorHash(locatorHash)
if err != nil {
t.Errorf("AddBlockLocatorHash: %v", err)
}
if msg.BlockLocatorHashes[0] != locatorHash {
t.Errorf("AddBlockLocatorHash: wrong block locator added - "+
"got %v, want %v",
spew.Sprint(msg.BlockLocatorHashes[0]),
spew.Sprint(locatorHash))
}
// Ensure adding more than the max allowed block locator hashes per
// message returns an error.
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
err = msg.AddBlockLocatorHash(locatorHash)
}
if err == nil {
t.Errorf("AddBlockLocatorHash: expected error on too many " +
"block locator hashes not received")
}
}
// TestGetBlocksWire tests the MsgGetBlocks wire encode and decode for various
// numbers of block locator hashes and protocol versions.
func TestGetBlocksWire(t *testing.T) {
// Set protocol inside getblocks message.
pver := uint32(60002)
// Block 99499 hash.
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
hashLocator, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// MsgGetBlocks message with no block locators or stop hash.
noLocators := NewMsgGetBlocks(&chainhash.Hash{})
noLocators.ProtocolVersion = pver
noLocatorsEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x00, // Varint for number of block locator hashes
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
}
// MsgGetBlocks message with multiple block locators and a stop hash.
multiLocators := NewMsgGetBlocks(hashStop)
multiLocators.AddBlockLocatorHash(hashLocator2)
multiLocators.AddBlockLocatorHash(hashLocator)
multiLocators.ProtocolVersion = pver
multiLocatorsEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x02, // Varint for number of block locator hashes
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
}
tests := []struct {
in *MsgGetBlocks // Message to encode
out *MsgGetBlocks // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0035Version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version BIP0031Versionwith multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
MultipleAddressVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgGetBlocks
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(&msg), spew.Sdump(test.out))
continue
}
}
}
// TestGetBlocksWireErrors performs negative tests against wire encode and
// decode of MsgGetBlocks to confirm error paths work correctly.
func TestGetBlocksWireErrors(t *testing.T) {
// Set protocol inside getheaders message. Use protocol version 60002
// specifically here instead of the latest because the test data is
// using bytes encoded with that protocol version.
pver := uint32(60002)
wireErr := &MessageError{}
// Block 99499 hash.
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
hashLocator, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// MsgGetBlocks message with multiple block locators and a stop hash.
baseGetBlocks := NewMsgGetBlocks(hashStop)
baseGetBlocks.ProtocolVersion = pver
baseGetBlocks.AddBlockLocatorHash(hashLocator2)
baseGetBlocks.AddBlockLocatorHash(hashLocator)
baseGetBlocksEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x02, // Varint for number of block locator hashes
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
}
// Message that forces an error by having more than the max allowed
// block locator hashes.
maxGetBlocks := NewMsgGetBlocks(hashStop)
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
maxGetBlocks.AddBlockLocatorHash(&mainNetGenesisHash)
}
maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes,
&mainNetGenesisHash)
maxGetBlocksEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
}
tests := []struct {
in *MsgGetBlocks // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in protocol version.
{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in block locator hash count.
{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 4, io.ErrShortWrite, io.EOF},
// Force error in block locator hashes.
{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 5, io.ErrShortWrite, io.EOF},
// Force error in stop hash.
{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 69, io.ErrShortWrite, io.EOF},
// Force error with greater than max block locator hashes.
{maxGetBlocks, maxGetBlocksEncoded, pver, BaseEncoding, 7, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgGetBlocks
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+133
View File
@@ -0,0 +1,133 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgGetData implements the Message interface and represents a bitcoin
// getdata message. It is used to request data such as blocks and transactions
// from another peer. It should be used in response to the inv (MsgInv) message
// to request the actual data referenced by each inventory vector the receiving
// peer doesn't already have. Each message is limited to a maximum number of
// inventory vectors, which is currently 50,000. As a result, multiple messages
// must be used to request larger amounts of data.
//
// Use the AddInvVect function to build up the list of inventory vectors when
// sending a getdata message to another peer.
type MsgGetData struct {
InvList []*InvVect
}
// AddInvVect adds an inventory vector to the message.
func (msg *MsgGetData) AddInvVect(iv *InvVect) error {
if len(msg.InvList)+1 > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [max %v]",
MaxInvPerMsg)
return messageError("MsgGetData.AddInvVect", str)
}
msg.InvList = append(msg.InvList, iv)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max inventory vectors per message.
if count > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [%v]", count)
return messageError("MsgGetData.BtcDecode", str)
}
// Create a contiguous slice of inventory vectors to deserialize into in
// order to reduce the number of allocations.
invList := make([]InvVect, count)
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := &invList[i]
err := readInvVect(r, pver, iv)
if err != nil {
return err
}
msg.AddInvVect(iv)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetData) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Limit to max inventory vectors per message.
count := len(msg.InvList)
if count > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [%v]", count)
return messageError("MsgGetData.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetData) Command() string {
return CmdGetData
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetData) MaxPayloadLength(pver uint32) uint32 {
// Num inventory vectors (varInt) + max allowed inventory vectors.
return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload)
}
// NewMsgGetData returns a new bitcoin getdata message that conforms to the
// Message interface. See MsgGetData for details.
func NewMsgGetData() *MsgGetData {
return &MsgGetData{
InvList: make([]*InvVect, 0, defaultInvListAlloc),
}
}
// NewMsgGetDataSizeHint returns a new bitcoin getdata message that conforms to
// the Message interface. See MsgGetData for details. This function differs
// from NewMsgGetData in that it allows a default allocation size for the
// backing array which houses the inventory vector list. This allows callers
// who know in advance how large the inventory list will grow to avoid the
// overhead of growing the internal backing array several times when appending
// large amounts of inventory vectors with AddInvVect. Note that the specified
// hint is just that - a hint that is used for the default allocation size.
// Adding more (or less) inventory vectors will still work properly. The size
// hint is limited to MaxInvPerMsg.
func NewMsgGetDataSizeHint(sizeHint uint) *MsgGetData {
// Limit the specified hint to the maximum allow per message.
if sizeHint > MaxInvPerMsg {
sizeHint = MaxInvPerMsg
}
return &MsgGetData{
InvList: make([]*InvVect, 0, sizeHint),
}
}
+341
View File
@@ -0,0 +1,341 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestGetData tests the MsgGetData API.
func TestGetData(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "getdata"
msg := NewMsgGetData()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgGetData: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num inventory vectors (varInt) + max allowed inventory vectors.
wantPayload := uint32(1800009)
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 inventory vectors are added properly.
hash := chainhash.Hash{}
iv := NewInvVect(InvTypeBlock, &hash)
err := msg.AddInvVect(iv)
if err != nil {
t.Errorf("AddInvVect: %v", err)
}
if msg.InvList[0] != iv {
t.Errorf("AddInvVect: wrong invvect added - got %v, want %v",
spew.Sprint(msg.InvList[0]), spew.Sprint(iv))
}
// Ensure adding more than the max allowed inventory vectors per
// message returns an error.
for i := 0; i < MaxInvPerMsg; i++ {
err = msg.AddInvVect(iv)
}
if err == nil {
t.Errorf("AddInvVect: expected error on too many inventory " +
"vectors not received")
}
// Ensure creating the message with a size hint larger than the max
// works as expected.
msg = NewMsgGetDataSizeHint(MaxInvPerMsg + 1)
wantCap := MaxInvPerMsg
if cap(msg.InvList) != wantCap {
t.Errorf("NewMsgGetDataSizeHint: wrong cap for size hint - "+
"got %v, want %v", cap(msg.InvList), wantCap)
}
}
// TestGetDataWire tests the MsgGetData wire encode and decode for various
// numbers of inventory vectors and protocol versions.
func TestGetDataWire(t *testing.T) {
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Transation 1 of Block 203707 hash.
hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
txHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
iv := NewInvVect(InvTypeBlock, blockHash)
iv2 := NewInvVect(InvTypeTx, txHash)
// Empty MsgGetData message.
NoInv := NewMsgGetData()
NoInvEncoded := []byte{
0x00, // Varint for number of inventory vectors
}
// MsgGetData message with multiple inventory vectors.
MultiInv := NewMsgGetData()
MultiInv.AddInvVect(iv)
MultiInv.AddInvVect(iv2)
MultiInvEncoded := []byte{
0x02, // Varint for number of inv vectors
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
0x01, 0x00, 0x00, 0x00, // InvTypeTx
0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf,
0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8,
0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98,
0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash
}
tests := []struct {
in *MsgGetData // Message to encode
out *MsgGetData // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version with no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0035Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version BIP0031Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
MultipleAddressVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgGetData
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestGetDataWireErrors performs negative tests against wire encode and decode
// of MsgGetData to confirm error paths work correctly.
func TestGetDataWireErrors(t *testing.T) {
pver := ProtocolVersion
wireErr := &MessageError{}
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
iv := NewInvVect(InvTypeBlock, blockHash)
// Base message used to induce errors.
baseGetData := NewMsgGetData()
baseGetData.AddInvVect(iv)
baseGetDataEncoded := []byte{
0x02, // Varint for number of inv vectors
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
}
// Message that forces an error by having more than the max allowed inv
// vectors.
maxGetData := NewMsgGetData()
for i := 0; i < MaxInvPerMsg; i++ {
maxGetData.AddInvVect(iv)
}
maxGetData.InvList = append(maxGetData.InvList, iv)
maxGetDataEncoded := []byte{
0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001)
}
tests := []struct {
in *MsgGetData // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 inventory vector count
{baseGetData, baseGetDataEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in inventory list.
{baseGetData, baseGetDataEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxGetData, maxGetDataEncoded, pver, BaseEncoding, 3, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgGetData
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// MsgGetHeaders implements the Message interface and represents a bitcoin
// getheaders message. It is used to request a list of block headers for
// blocks starting after the last known hash in the slice of block locator
// hashes. The list is returned via a headers message (MsgHeaders) and is
// limited by a specific hash to stop at or the maximum number of block headers
// per message, which is currently 2000.
//
// Set the HashStop field to the hash at which to stop and use
// AddBlockLocatorHash to build up the list of block locator hashes.
//
// The algorithm for building the block locator hashes should be to add the
// hashes in reverse order until you reach the genesis block. In order to keep
// the list of locator hashes to a resonable number of entries, first add the
// most recent 10 block hashes, then double the step each loop iteration to
// exponentially decrease the number of hashes the further away from head and
// closer to the genesis block you get.
type MsgGetHeaders struct {
ProtocolVersion uint32
BlockLocatorHashes []*chainhash.Hash
HashStop chainhash.Hash
}
// AddBlockLocatorHash adds a new block locator hash to the message.
func (msg *MsgGetHeaders) AddBlockLocatorHash(hash *chainhash.Hash) error {
if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message [max %v]",
MaxBlockLocatorsPerMsg)
return messageError("MsgGetHeaders.AddBlockLocatorHash", str)
}
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
err := readElement(r, &msg.ProtocolVersion)
if err != nil {
return err
}
// Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
return messageError("MsgGetHeaders.BtcDecode", str)
}
// Create a contiguous slice of hashes to deserialize into in order to
// reduce the number of allocations.
locatorHashes := make([]chainhash.Hash, count)
msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
hash := &locatorHashes[i]
err := readElement(r, hash)
if err != nil {
return err
}
msg.AddBlockLocatorHash(hash)
}
return readElement(r, &msg.HashStop)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Limit to max block locator hashes per message.
count := len(msg.BlockLocatorHashes)
if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
return messageError("MsgGetHeaders.BtcEncode", str)
}
err := writeElement(w, msg.ProtocolVersion)
if err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, hash := range msg.BlockLocatorHashes {
err := writeElement(w, hash)
if err != nil {
return err
}
}
return writeElement(w, &msg.HashStop)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetHeaders) Command() string {
return CmdGetHeaders
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 {
// Version 4 bytes + num block locator hashes (varInt) + max allowed block
// locators + hash stop.
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
chainhash.HashSize) + chainhash.HashSize
}
// NewMsgGetHeaders returns a new bitcoin getheaders message that conforms to
// the Message interface. See MsgGetHeaders for details.
func NewMsgGetHeaders() *MsgGetHeaders {
return &MsgGetHeaders{
BlockLocatorHashes: make([]*chainhash.Hash, 0,
MaxBlockLocatorsPerMsg),
}
}
+391
View File
@@ -0,0 +1,391 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestGetHeaders tests the MsgGetHeader API.
func TestGetHeaders(t *testing.T) {
pver := ProtocolVersion
// Block 99500 hash.
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
locatorHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Ensure the command is expected value.
wantCmd := "getheaders"
msg := NewMsgGetHeaders()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Protocol version 4 bytes + num hashes (varInt) + max block locator
// hashes + hash stop.
wantPayload := uint32(16045)
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 block locator hashes are added properly.
err = msg.AddBlockLocatorHash(locatorHash)
if err != nil {
t.Errorf("AddBlockLocatorHash: %v", err)
}
if msg.BlockLocatorHashes[0] != locatorHash {
t.Errorf("AddBlockLocatorHash: wrong block locator added - "+
"got %v, want %v",
spew.Sprint(msg.BlockLocatorHashes[0]),
spew.Sprint(locatorHash))
}
// Ensure adding more than the max allowed block locator hashes per
// message returns an error.
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
err = msg.AddBlockLocatorHash(locatorHash)
}
if err == nil {
t.Errorf("AddBlockLocatorHash: expected error on too many " +
"block locator hashes not received")
}
}
// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode for various
// numbers of block locator hashes and protocol versions.
func TestGetHeadersWire(t *testing.T) {
// Set protocol inside getheaders message. Use protocol version 60002
// specifically here instead of the latest because the test data is
// using bytes encoded with that protocol version.
pver := uint32(60002)
// Block 99499 hash.
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
hashLocator, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// MsgGetHeaders message with no block locators or stop hash.
noLocators := NewMsgGetHeaders()
noLocators.ProtocolVersion = pver
noLocatorsEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x00, // Varint for number of block locator hashes
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
}
// MsgGetHeaders message with multiple block locators and a stop hash.
multiLocators := NewMsgGetHeaders()
multiLocators.ProtocolVersion = pver
multiLocators.HashStop = *hashStop
multiLocators.AddBlockLocatorHash(hashLocator2)
multiLocators.AddBlockLocatorHash(hashLocator)
multiLocatorsEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x02, // Varint for number of block locator hashes
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
}
tests := []struct {
in *MsgGetHeaders // Message to encode
out *MsgGetHeaders // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0035Version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version BIP0031Versionwith multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
MultipleAddressVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgGetHeaders
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(&msg), spew.Sdump(test.out))
continue
}
}
}
// TestGetHeadersWireErrors performs negative tests against wire encode and
// decode of MsgGetHeaders to confirm error paths work correctly.
func TestGetHeadersWireErrors(t *testing.T) {
// Set protocol inside getheaders message. Use protocol version 60002
// specifically here instead of the latest because the test data is
// using bytes encoded with that protocol version.
pver := uint32(60002)
wireErr := &MessageError{}
// Block 99499 hash.
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
hashLocator, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// MsgGetHeaders message with multiple block locators and a stop hash.
baseGetHeaders := NewMsgGetHeaders()
baseGetHeaders.ProtocolVersion = pver
baseGetHeaders.HashStop = *hashStop
baseGetHeaders.AddBlockLocatorHash(hashLocator2)
baseGetHeaders.AddBlockLocatorHash(hashLocator)
baseGetHeadersEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x02, // Varint for number of block locator hashes
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
}
// Message that forces an error by having more than the max allowed
// block locator hashes.
maxGetHeaders := NewMsgGetHeaders()
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
maxGetHeaders.AddBlockLocatorHash(&mainNetGenesisHash)
}
maxGetHeaders.BlockLocatorHashes = append(maxGetHeaders.BlockLocatorHashes,
&mainNetGenesisHash)
maxGetHeadersEncoded := []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
}
tests := []struct {
in *MsgGetHeaders // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in protocol version.
{baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in block locator hash count.
{baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 4, io.ErrShortWrite, io.EOF},
// Force error in block locator hashes.
{baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 5, io.ErrShortWrite, io.EOF},
// Force error in stop hash.
{baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 69, io.ErrShortWrite, io.EOF},
// Force error with greater than max block locator hashes.
{maxGetHeaders, maxGetHeadersEncoded, pver, BaseEncoding, 7, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgGetHeaders
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MaxBlockHeadersPerMsg is the maximum number of block headers that can be in
// a single bitcoin headers message.
const MaxBlockHeadersPerMsg = 2000
// MsgHeaders implements the Message interface and represents a bitcoin headers
// message. It is used to deliver block header information in response
// to a getheaders message (MsgGetHeaders). The maximum number of block headers
// per message is currently 2000. See MsgGetHeaders for details on requesting
// the headers.
type MsgHeaders struct {
Headers []*BlockHeader
}
// AddBlockHeader adds a new block header to the message.
func (msg *MsgHeaders) AddBlockHeader(bh *BlockHeader) error {
if len(msg.Headers)+1 > MaxBlockHeadersPerMsg {
str := fmt.Sprintf("too many block headers in message [max %v]",
MaxBlockHeadersPerMsg)
return messageError("MsgHeaders.AddBlockHeader", str)
}
msg.Headers = append(msg.Headers, bh)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max block headers per message.
if count > MaxBlockHeadersPerMsg {
str := fmt.Sprintf("too many block headers for message "+
"[count %v, max %v]", count, MaxBlockHeadersPerMsg)
return messageError("MsgHeaders.BtcDecode", str)
}
// Create a contiguous slice of headers to deserialize into in order to
// reduce the number of allocations.
headers := make([]BlockHeader, count)
msg.Headers = make([]*BlockHeader, 0, count)
for i := uint64(0); i < count; i++ {
bh := &headers[i]
err := readBlockHeader(r, pver, bh)
if err != nil {
return err
}
txCount, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Ensure the transaction count is zero for headers.
if txCount > 0 {
str := fmt.Sprintf("block headers may not contain "+
"transactions [count %v]", txCount)
return messageError("MsgHeaders.BtcDecode", str)
}
msg.AddBlockHeader(bh)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Limit to max block headers per message.
count := len(msg.Headers)
if count > MaxBlockHeadersPerMsg {
str := fmt.Sprintf("too many block headers for message "+
"[count %v, max %v]", count, MaxBlockHeadersPerMsg)
return messageError("MsgHeaders.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, bh := range msg.Headers {
err := writeBlockHeader(w, pver, bh)
if err != nil {
return err
}
// The wire protocol encoding always includes a 0 for the number
// of transactions on header messages. This is really just an
// artifact of the way the original implementation serializes
// block headers, but it is required.
err = WriteVarInt(w, pver, 0)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgHeaders) Command() string {
return CmdHeaders
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgHeaders) MaxPayloadLength(pver uint32) uint32 {
// Num headers (varInt) + max allowed headers (header length + 1 byte
// for the number of transactions which is always 0).
return MaxVarIntPayload + ((MaxBlockHeaderPayload + 1) *
MaxBlockHeadersPerMsg)
}
// NewMsgHeaders returns a new bitcoin headers message that conforms to the
// Message interface. See MsgHeaders for details.
func NewMsgHeaders() *MsgHeaders {
return &MsgHeaders{
Headers: make([]*BlockHeader, 0, MaxBlockHeadersPerMsg),
}
}
+359
View File
@@ -0,0 +1,359 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestHeaders tests the MsgHeaders API.
func TestHeaders(t *testing.T) {
pver := uint32(60002)
// Ensure the command is expected value.
wantCmd := "headers"
msg := NewMsgHeaders()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgHeaders: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num headers (varInt) + max allowed headers (header length + 1 byte
// for the number of transactions which is always 0).
wantPayload := uint32(162009)
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 headers are added properly.
bh := &blockOne.Header
msg.AddBlockHeader(bh)
if !reflect.DeepEqual(msg.Headers[0], bh) {
t.Errorf("AddHeader: wrong header - got %v, want %v",
spew.Sdump(msg.Headers),
spew.Sdump(bh))
}
// Ensure adding more than the max allowed headers per message returns
// error.
var err error
for i := 0; i < MaxBlockHeadersPerMsg+1; i++ {
err = msg.AddBlockHeader(bh)
}
if reflect.TypeOf(err) != reflect.TypeOf(&MessageError{}) {
t.Errorf("AddBlockHeader: expected error on too many headers " +
"not received")
}
}
// TestHeadersWire tests the MsgHeaders wire encode and decode for various
// numbers of headers and protocol versions.
func TestHeadersWire(t *testing.T) {
hash := mainNetGenesisHash
merkleHash := blockOne.Header.MerkleRoot
bits := uint32(0x1d00ffff)
nonce := uint32(0x9962e301)
bh := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
bh.Version = blockOne.Header.Version
bh.Timestamp = blockOne.Header.Timestamp
// Empty headers message.
noHeaders := NewMsgHeaders()
noHeadersEncoded := []byte{
0x00, // Varint for number of headers
}
// Headers message with one header.
oneHeader := NewMsgHeaders()
oneHeader.AddBlockHeader(bh)
oneHeaderEncoded := []byte{
0x01, // VarInt for number of headers.
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0x00, // TxnCount (0 for headers message)
}
tests := []struct {
in *MsgHeaders // Message to encode
out *MsgHeaders // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0035Version with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version BIP0031Version with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
MultipleAddressVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgHeaders
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(&msg), spew.Sdump(test.out))
continue
}
}
}
// TestHeadersWireErrors performs negative tests against wire encode and decode
// of MsgHeaders to confirm error paths work correctly.
func TestHeadersWireErrors(t *testing.T) {
pver := ProtocolVersion
wireErr := &MessageError{}
hash := mainNetGenesisHash
merkleHash := blockOne.Header.MerkleRoot
bits := uint32(0x1d00ffff)
nonce := uint32(0x9962e301)
bh := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
bh.Version = blockOne.Header.Version
bh.Timestamp = blockOne.Header.Timestamp
// Headers message with one header.
oneHeader := NewMsgHeaders()
oneHeader.AddBlockHeader(bh)
oneHeaderEncoded := []byte{
0x01, // VarInt for number of headers.
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0x00, // TxnCount (0 for headers message)
}
// Message that forces an error by having more than the max allowed
// headers.
maxHeaders := NewMsgHeaders()
for i := 0; i < MaxBlockHeadersPerMsg; i++ {
maxHeaders.AddBlockHeader(bh)
}
maxHeaders.Headers = append(maxHeaders.Headers, bh)
maxHeadersEncoded := []byte{
0xfd, 0xd1, 0x07, // Varint for number of addresses (2001)7D1
}
// Intentionally invalid block header that has a transaction count used
// to force errors.
bhTrans := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
bhTrans.Version = blockOne.Header.Version
bhTrans.Timestamp = blockOne.Header.Timestamp
transHeader := NewMsgHeaders()
transHeader.AddBlockHeader(bhTrans)
transHeaderEncoded := []byte{
0x01, // VarInt for number of headers.
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0x01, // TxnCount (should be 0 for headers message, but 1 to force error)
}
tests := []struct {
in *MsgHeaders // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 header count.
{oneHeader, oneHeaderEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in block header.
{oneHeader, oneHeaderEncoded, pver, BaseEncoding, 5, io.ErrShortWrite, io.EOF},
// Force error with greater than max headers.
{maxHeaders, maxHeadersEncoded, pver, BaseEncoding, 3, wireErr, wireErr},
// Force error with number of transactions.
{transHeader, transHeaderEncoded, pver, BaseEncoding, 81, io.ErrShortWrite, io.EOF},
// Force error with included transactions.
{transHeader, transHeaderEncoded, pver, BaseEncoding, len(transHeaderEncoded), nil, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgHeaders
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+141
View File
@@ -0,0 +1,141 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// defaultInvListAlloc is the default size used for the backing array for an
// inventory list. The array will dynamically grow as needed, but this
// figure is intended to provide enough space for the max number of inventory
// vectors in a *typical* inventory message without needing to grow the backing
// array multiple times. Technically, the list can grow to MaxInvPerMsg, but
// rather than using that large figure, this figure more accurately reflects the
// typical case.
const defaultInvListAlloc = 1000
// MsgInv implements the Message interface and represents a bitcoin inv message.
// It is used to advertise a peer's known data such as blocks and transactions
// through inventory vectors. It may be sent unsolicited to inform other peers
// of the data or in response to a getblocks message (MsgGetBlocks). Each
// message is limited to a maximum number of inventory vectors, which is
// currently 50,000.
//
// Use the AddInvVect function to build up the list of inventory vectors when
// sending an inv message to another peer.
type MsgInv struct {
InvList []*InvVect
}
// AddInvVect adds an inventory vector to the message.
func (msg *MsgInv) AddInvVect(iv *InvVect) error {
if len(msg.InvList)+1 > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [max %v]",
MaxInvPerMsg)
return messageError("MsgInv.AddInvVect", str)
}
msg.InvList = append(msg.InvList, iv)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max inventory vectors per message.
if count > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [%v]", count)
return messageError("MsgInv.BtcDecode", str)
}
// Create a contiguous slice of inventory vectors to deserialize into in
// order to reduce the number of allocations.
invList := make([]InvVect, count)
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := &invList[i]
err := readInvVect(r, pver, iv)
if err != nil {
return err
}
msg.AddInvVect(iv)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgInv) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Limit to max inventory vectors per message.
count := len(msg.InvList)
if count > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [%v]", count)
return messageError("MsgInv.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgInv) Command() string {
return CmdInv
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgInv) MaxPayloadLength(pver uint32) uint32 {
// Num inventory vectors (varInt) + max allowed inventory vectors.
return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload)
}
// NewMsgInv returns a new bitcoin inv message that conforms to the Message
// interface. See MsgInv for details.
func NewMsgInv() *MsgInv {
return &MsgInv{
InvList: make([]*InvVect, 0, defaultInvListAlloc),
}
}
// NewMsgInvSizeHint returns a new bitcoin inv message that conforms to the
// Message interface. See MsgInv for details. This function differs from
// NewMsgInv in that it allows a default allocation size for the backing array
// which houses the inventory vector list. This allows callers who know in
// advance how large the inventory list will grow to avoid the overhead of
// growing the internal backing array several times when appending large amounts
// of inventory vectors with AddInvVect. Note that the specified hint is just
// that - a hint that is used for the default allocation size. Adding more
// (or less) inventory vectors will still work properly. The size hint is
// limited to MaxInvPerMsg.
func NewMsgInvSizeHint(sizeHint uint) *MsgInv {
// Limit the specified hint to the maximum allow per message.
if sizeHint > MaxInvPerMsg {
sizeHint = MaxInvPerMsg
}
return &MsgInv{
InvList: make([]*InvVect, 0, sizeHint),
}
}
+342
View File
@@ -0,0 +1,342 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestInv tests the MsgInv API.
func TestInv(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "inv"
msg := NewMsgInv()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgInv: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num inventory vectors (varInt) + max allowed inventory vectors.
wantPayload := uint32(1800009)
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 inventory vectors are added properly.
hash := chainhash.Hash{}
iv := NewInvVect(InvTypeBlock, &hash)
err := msg.AddInvVect(iv)
if err != nil {
t.Errorf("AddInvVect: %v", err)
}
if msg.InvList[0] != iv {
t.Errorf("AddInvVect: wrong invvect added - got %v, want %v",
spew.Sprint(msg.InvList[0]), spew.Sprint(iv))
}
// Ensure adding more than the max allowed inventory vectors per
// message returns an error.
for i := 0; i < MaxInvPerMsg; i++ {
err = msg.AddInvVect(iv)
}
if err == nil {
t.Errorf("AddInvVect: expected error on too many inventory " +
"vectors not received")
}
// Ensure creating the message with a size hint larger than the max
// works as expected.
msg = NewMsgInvSizeHint(MaxInvPerMsg + 1)
wantCap := MaxInvPerMsg
if cap(msg.InvList) != wantCap {
t.Errorf("NewMsgInvSizeHint: wrong cap for size hint - "+
"got %v, want %v", cap(msg.InvList), wantCap)
}
}
// TestInvWire tests the MsgInv wire encode and decode for various numbers
// of inventory vectors and protocol versions.
func TestInvWire(t *testing.T) {
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Transation 1 of Block 203707 hash.
hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
txHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
iv := NewInvVect(InvTypeBlock, blockHash)
iv2 := NewInvVect(InvTypeTx, txHash)
// Empty inv message.
NoInv := NewMsgInv()
NoInvEncoded := []byte{
0x00, // Varint for number of inventory vectors
}
// Inv message with multiple inventory vectors.
MultiInv := NewMsgInv()
MultiInv.AddInvVect(iv)
MultiInv.AddInvVect(iv2)
MultiInvEncoded := []byte{
0x02, // Varint for number of inv vectors
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
0x01, 0x00, 0x00, 0x00, // InvTypeTx
0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf,
0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8,
0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98,
0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash
}
tests := []struct {
in *MsgInv // Message to encode
out *MsgInv // Expected decoded message
buf []byte // Wire encoding pver uint32
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encodinf format
}{
// Latest protocol version with no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0035Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version BIP0031Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
MultipleAddressVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgInv
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestInvWireErrors performs negative tests against wire encode and decode
// of MsgInv to confirm error paths work correctly.
func TestInvWireErrors(t *testing.T) {
pver := ProtocolVersion
wireErr := &MessageError{}
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
iv := NewInvVect(InvTypeBlock, blockHash)
// Base inv message used to induce errors.
baseInv := NewMsgInv()
baseInv.AddInvVect(iv)
baseInvEncoded := []byte{
0x02, // Varint for number of inv vectors
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
}
// Inv message that forces an error by having more than the max allowed
// inv vectors.
maxInv := NewMsgInv()
for i := 0; i < MaxInvPerMsg; i++ {
maxInv.AddInvVect(iv)
}
maxInv.InvList = append(maxInv.InvList, iv)
maxInvEncoded := []byte{
0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001)
}
tests := []struct {
in *MsgInv // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 inventory vector count
{baseInv, baseInvEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in inventory list.
{baseInv, baseInvEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxInv, maxInvEncoded, pver, BaseEncoding, 3, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgInv
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgMemPool implements the Message interface and represents a bitcoin mempool
// message. It is used to request a list of transactions still in the active
// memory pool of a relay.
//
// This message has no payload and was not added until protocol versions
// starting with BIP0035Version.
type MsgMemPool struct{}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgMemPool) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < BIP0035Version {
str := fmt.Sprintf("mempool message invalid for protocol "+
"version %d", pver)
return messageError("MsgMemPool.BtcDecode", str)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgMemPool) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < BIP0035Version {
str := fmt.Sprintf("mempool message invalid for protocol "+
"version %d", pver)
return messageError("MsgMemPool.BtcEncode", str)
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgMemPool) Command() string {
return CmdMemPool
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgMemPool) MaxPayloadLength(pver uint32) uint32 {
return 0
}
// NewMsgMemPool returns a new bitcoin pong message that conforms to the Message
// interface. See MsgPong for details.
func NewMsgMemPool() *MsgMemPool {
return &MsgMemPool{}
}
+63
View File
@@ -0,0 +1,63 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"testing"
)
func TestMemPool(t *testing.T) {
pver := ProtocolVersion
enc := BaseEncoding
// Ensure the command is expected value.
wantCmd := "mempool"
msg := NewMsgMemPool()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgMemPool: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
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 latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, pver, enc)
if err != nil {
t.Errorf("encode of MsgMemPool failed %v err <%v>", msg, err)
}
// Older protocol versions should fail encode since message didn't
// exist yet.
oldPver := BIP0035Version - 1
err = msg.BtcEncode(&buf, oldPver, enc)
if err == nil {
s := "encode of MsgMemPool passed for old protocol version %v err <%v>"
t.Errorf(s, msg, err)
}
// Test decode with latest protocol version.
readmsg := NewMsgMemPool()
err = readmsg.BtcDecode(&buf, pver, enc)
if err != nil {
t.Errorf("decode of MsgMemPool failed [%v] err <%v>", buf, err)
}
// Older protocol versions should fail decode since message didn't
// exist yet.
err = readmsg.BtcDecode(&buf, oldPver, enc)
if err == nil {
s := "decode of MsgMemPool passed for old protocol version %v err <%v>"
t.Errorf(s, msg, err)
}
}
+159
View File
@@ -0,0 +1,159 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// maxFlagsPerMerkleBlock is the maximum number of flag bytes that could
// possibly fit into a merkle block. Since each transaction is represented by
// a single bit, this is the max number of transactions per block divided by
// 8 bits per byte. Then an extra one to cover partials.
const maxFlagsPerMerkleBlock = maxTxPerBlock / 8
// MsgMerkleBlock implements the Message interface and represents a bitcoin
// merkleblock message which is used to reset a Bloom filter.
//
// This message was not added until protocol version BIP0037Version.
type MsgMerkleBlock struct {
Header BlockHeader
Transactions uint32
Hashes []*chainhash.Hash
Flags []byte
}
// AddTxHash adds a new transaction hash to the message.
func (msg *MsgMerkleBlock) AddTxHash(hash *chainhash.Hash) error {
if len(msg.Hashes)+1 > maxTxPerBlock {
str := fmt.Sprintf("too many tx hashes for message [max %v]",
maxTxPerBlock)
return messageError("MsgMerkleBlock.AddTxHash", str)
}
msg.Hashes = append(msg.Hashes, hash)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("merkleblock message invalid for protocol "+
"version %d", pver)
return messageError("MsgMerkleBlock.BtcDecode", str)
}
err := readBlockHeader(r, pver, &msg.Header)
if err != nil {
return err
}
err = readElement(r, &msg.Transactions)
if err != nil {
return err
}
// Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > maxTxPerBlock {
str := fmt.Sprintf("too many transaction hashes for message "+
"[count %v, max %v]", count, maxTxPerBlock)
return messageError("MsgMerkleBlock.BtcDecode", str)
}
// Create a contiguous slice of hashes to deserialize into in order to
// reduce the number of allocations.
hashes := make([]chainhash.Hash, count)
msg.Hashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
hash := &hashes[i]
err := readElement(r, hash)
if err != nil {
return err
}
msg.AddTxHash(hash)
}
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock,
"merkle block flags size")
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < BIP0037Version {
str := fmt.Sprintf("merkleblock message invalid for protocol "+
"version %d", pver)
return messageError("MsgMerkleBlock.BtcEncode", str)
}
// Read num transaction hashes and limit to max.
numHashes := len(msg.Hashes)
if numHashes > maxTxPerBlock {
str := fmt.Sprintf("too many transaction hashes for message "+
"[count %v, max %v]", numHashes, maxTxPerBlock)
return messageError("MsgMerkleBlock.BtcDecode", str)
}
numFlagBytes := len(msg.Flags)
if numFlagBytes > maxFlagsPerMerkleBlock {
str := fmt.Sprintf("too many flag bytes for message [count %v, "+
"max %v]", numFlagBytes, maxFlagsPerMerkleBlock)
return messageError("MsgMerkleBlock.BtcDecode", str)
}
err := writeBlockHeader(w, pver, &msg.Header)
if err != nil {
return err
}
err = writeElement(w, msg.Transactions)
if err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(numHashes))
if err != nil {
return err
}
for _, hash := range msg.Hashes {
err = writeElement(w, hash)
if err != nil {
return err
}
}
return WriteVarBytes(w, pver, msg.Flags)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgMerkleBlock) Command() string {
return CmdMerkleBlock
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
return MaxBlockPayload
}
// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to
// the Message interface. See MsgMerkleBlock for details.
func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock {
return &MsgMerkleBlock{
Header: *bh,
Transactions: 0,
Hashes: make([]*chainhash.Hash, 0),
Flags: make([]byte, 0),
}
}
+430
View File
@@ -0,0 +1,430 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"crypto/rand"
"io"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestMerkleBlock tests the MsgMerkleBlock API.
func TestMerkleBlock(t *testing.T) {
pver := ProtocolVersion
enc := BaseEncoding
// Block 1 header.
prevHash := &blockOne.Header.PrevBlock
merkleHash := &blockOne.Header.MerkleRoot
bits := blockOne.Header.Bits
nonce := blockOne.Header.Nonce
bh := NewBlockHeader(1, prevHash, merkleHash, bits, nonce)
// Ensure the command is expected value.
wantCmd := "merkleblock"
msg := NewMsgMerkleBlock(bh)
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgBlock: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num addresses (varInt) + max allowed addresses.
wantPayload := uint32(4000000)
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)
}
// Load maxTxPerBlock hashes
data := make([]byte, 32)
for i := 0; i < maxTxPerBlock; i++ {
rand.Read(data)
hash, err := chainhash.NewHash(data)
if err != nil {
t.Errorf("NewHash failed: %v\n", err)
return
}
if err = msg.AddTxHash(hash); err != nil {
t.Errorf("AddTxHash failed: %v\n", err)
return
}
}
// Add one more Tx to test failure.
rand.Read(data)
hash, err := chainhash.NewHash(data)
if err != nil {
t.Errorf("NewHash failed: %v\n", err)
return
}
if err = msg.AddTxHash(hash); err == nil {
t.Errorf("AddTxHash succeeded when it should have failed")
return
}
// Test encode with latest protocol version.
var buf bytes.Buffer
err = msg.BtcEncode(&buf, pver, enc)
if err != nil {
t.Errorf("encode of MsgMerkleBlock failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := MsgMerkleBlock{}
err = readmsg.BtcDecode(&buf, pver, enc)
if err != nil {
t.Errorf("decode of MsgMerkleBlock failed [%v] err <%v>", buf, err)
}
// Force extra hash to test maxTxPerBlock.
msg.Hashes = append(msg.Hashes, hash)
err = msg.BtcEncode(&buf, pver, enc)
if err == nil {
t.Errorf("encode of MsgMerkleBlock succeeded with too many " +
"tx hashes when it should have failed")
return
}
// Force too many flag bytes to test maxFlagsPerMerkleBlock.
// Reset the number of hashes back to a valid value.
msg.Hashes = msg.Hashes[len(msg.Hashes)-1:]
msg.Flags = make([]byte, maxFlagsPerMerkleBlock+1)
err = msg.BtcEncode(&buf, pver, enc)
if err == nil {
t.Errorf("encode of MsgMerkleBlock succeeded with too many " +
"flag bytes when it should have failed")
return
}
}
// TestMerkleBlockCrossProtocol tests the MsgMerkleBlock API when encoding with
// the latest protocol version and decoding with BIP0031Version.
func TestMerkleBlockCrossProtocol(t *testing.T) {
// Block 1 header.
prevHash := &blockOne.Header.PrevBlock
merkleHash := &blockOne.Header.MerkleRoot
bits := blockOne.Header.Bits
nonce := blockOne.Header.Nonce
bh := NewBlockHeader(1, prevHash, merkleHash, bits, nonce)
msg := NewMsgMerkleBlock(bh)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, BaseEncoding)
if err != nil {
t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg,
err)
}
// Decode with old protocol version.
var readmsg MsgFilterLoad
err = readmsg.BtcDecode(&buf, BIP0031Version, BaseEncoding)
if err == nil {
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v",
msg)
}
}
// TestMerkleBlockWire tests the MsgMerkleBlock wire encode and decode for
// various numbers of transaction hashes and protocol versions.
func TestMerkleBlockWire(t *testing.T) {
tests := []struct {
in *MsgMerkleBlock // Message to encode
out *MsgMerkleBlock // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
&merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes,
ProtocolVersion, BaseEncoding,
},
// Protocol version BIP0037Version.
{
&merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes,
BIP0037Version, BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgMerkleBlock
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(&msg), spew.Sdump(test.out))
continue
}
}
}
// TestMerkleBlockWireErrors performs negative tests against wire encode and
// decode of MsgBlock to confirm error paths work correctly.
func TestMerkleBlockWireErrors(t *testing.T) {
// Use protocol version 70001 specifically here instead of the latest
// because the test data is using bytes encoded with that protocol
// version.
pver := uint32(70001)
pverNoMerkleBlock := BIP0037Version - 1
wireErr := &MessageError{}
tests := []struct {
in *MsgMerkleBlock // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in version.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 0,
io.ErrShortWrite, io.EOF,
},
// Force error in prev block hash.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 4,
io.ErrShortWrite, io.EOF,
},
// Force error in merkle root.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 36,
io.ErrShortWrite, io.EOF,
},
// Force error in timestamp.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 68,
io.ErrShortWrite, io.EOF,
},
// Force error in difficulty bits.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 72,
io.ErrShortWrite, io.EOF,
},
// Force error in header nonce.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 76,
io.ErrShortWrite, io.EOF,
},
// Force error in transaction count.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 80,
io.ErrShortWrite, io.EOF,
},
// Force error in num hashes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 84,
io.ErrShortWrite, io.EOF,
},
// Force error in hashes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 85,
io.ErrShortWrite, io.EOF,
},
// Force error in num flag bytes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 117,
io.ErrShortWrite, io.EOF,
},
// Force error in flag bytes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, BaseEncoding, 118,
io.ErrShortWrite, io.EOF,
},
// Force error due to unsupported protocol version.
{
&merkleBlockOne, merkleBlockOneBytes, pverNoMerkleBlock,
BaseEncoding, 119, wireErr, wireErr,
},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgMerkleBlock
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
// TestMerkleBlockOverflowErrors performs tests to ensure encoding and decoding
// merkle blocks that are intentionally crafted to use large values for the
// number of hashes and flags are handled properly. This could otherwise
// potentially be used as an attack vector.
func TestMerkleBlockOverflowErrors(t *testing.T) {
// Use protocol version 70001 specifically here instead of the latest
// protocol version because the test data is using bytes encoded with
// that version.
pver := uint32(70001)
// Create bytes for a merkle block that claims to have more than the max
// allowed tx hashes.
var buf bytes.Buffer
WriteVarInt(&buf, pver, maxTxPerBlock+1)
numHashesOffset := 84
exceedMaxHashes := make([]byte, numHashesOffset)
copy(exceedMaxHashes, merkleBlockOneBytes[:numHashesOffset])
exceedMaxHashes = append(exceedMaxHashes, buf.Bytes()...)
// Create bytes for a merkle block that claims to have more than the max
// allowed flag bytes.
buf.Reset()
WriteVarInt(&buf, pver, maxFlagsPerMerkleBlock+1)
numFlagBytesOffset := 117
exceedMaxFlagBytes := make([]byte, numFlagBytesOffset)
copy(exceedMaxFlagBytes, merkleBlockOneBytes[:numFlagBytesOffset])
exceedMaxFlagBytes = append(exceedMaxFlagBytes, buf.Bytes()...)
tests := []struct {
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
err error // Expected error
}{
// Block that claims to have more than max allowed hashes.
{exceedMaxHashes, pver, BaseEncoding, &MessageError{}},
// Block that claims to have more than max allowed flag bytes.
{exceedMaxFlagBytes, pver, BaseEncoding, &MessageError{}},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
var msg MsgMerkleBlock
r := bytes.NewReader(test.buf)
err := msg.BtcDecode(r, test.pver, test.enc)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, reflect.TypeOf(test.err))
continue
}
}
}
// merkleBlockOne is a merkle block created from block one of the block chain
// where the first transaction matches.
var merkleBlockOne = MsgMerkleBlock{
Header: BlockHeader{
Version: 1,
PrevBlock: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
MerkleRoot: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
}),
Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST
Bits: 0x1d00ffff, // 486604799
Nonce: 0x9962e301, // 2573394689
},
Transactions: 1,
Hashes: []*chainhash.Hash{
(*chainhash.Hash)(&[chainhash.HashSize]byte{ // Make go vet happy.
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
}),
},
Flags: []byte{0x80},
}
// merkleBlockOneBytes is the serialized bytes for a merkle block created from
// block one of the block chain where the first transation matches.
var merkleBlockOneBytes = []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0x01, 0x00, 0x00, 0x00, // TxnCount
0x01, // Num hashes
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // Hash
0x01, // Num flag bytes
0x80, // Flags
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgNotFound defines a bitcoin notfound message which is sent in response to
// a getdata message if any of the requested data in not available on the peer.
// Each message is limited to a maximum number of inventory vectors, which is
// currently 50,000.
//
// Use the AddInvVect function to build up the list of inventory vectors when
// sending a notfound message to another peer.
type MsgNotFound struct {
InvList []*InvVect
}
// AddInvVect adds an inventory vector to the message.
func (msg *MsgNotFound) AddInvVect(iv *InvVect) error {
if len(msg.InvList)+1 > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [max %v]",
MaxInvPerMsg)
return messageError("MsgNotFound.AddInvVect", str)
}
msg.InvList = append(msg.InvList, iv)
return nil
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max inventory vectors per message.
if count > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [%v]", count)
return messageError("MsgNotFound.BtcDecode", str)
}
// Create a contiguous slice of inventory vectors to deserialize into in
// order to reduce the number of allocations.
invList := make([]InvVect, count)
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := &invList[i]
err := readInvVect(r, pver, iv)
if err != nil {
return err
}
msg.AddInvVect(iv)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgNotFound) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// Limit to max inventory vectors per message.
count := len(msg.InvList)
if count > MaxInvPerMsg {
str := fmt.Sprintf("too many invvect in message [%v]", count)
return messageError("MsgNotFound.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgNotFound) Command() string {
return CmdNotFound
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgNotFound) MaxPayloadLength(pver uint32) uint32 {
// Max var int 9 bytes + max InvVects at 36 bytes each.
// Num inventory vectors (varInt) + max allowed inventory vectors.
return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload)
}
// NewMsgNotFound returns a new bitcoin notfound message that conforms to the
// Message interface. See MsgNotFound for details.
func NewMsgNotFound() *MsgNotFound {
return &MsgNotFound{
InvList: make([]*InvVect, 0, defaultInvListAlloc),
}
}
+331
View File
@@ -0,0 +1,331 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/davecgh/go-spew/spew"
)
// TestNotFound tests the MsgNotFound API.
func TestNotFound(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "notfound"
msg := NewMsgNotFound()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgNotFound: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
// Num inventory vectors (varInt) + max allowed inventory vectors.
wantPayload := uint32(1800009)
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 inventory vectors are added properly.
hash := chainhash.Hash{}
iv := NewInvVect(InvTypeBlock, &hash)
err := msg.AddInvVect(iv)
if err != nil {
t.Errorf("AddInvVect: %v", err)
}
if msg.InvList[0] != iv {
t.Errorf("AddInvVect: wrong invvect added - got %v, want %v",
spew.Sprint(msg.InvList[0]), spew.Sprint(iv))
}
// Ensure adding more than the max allowed inventory vectors per
// message returns an error.
for i := 0; i < MaxInvPerMsg; i++ {
err = msg.AddInvVect(iv)
}
if err == nil {
t.Errorf("AddInvVect: expected error on too many inventory " +
"vectors not received")
}
}
// TestNotFoundWire tests the MsgNotFound wire encode and decode for various
// numbers of inventory vectors and protocol versions.
func TestNotFoundWire(t *testing.T) {
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Transation 1 of Block 203707 hash.
hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
txHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
iv := NewInvVect(InvTypeBlock, blockHash)
iv2 := NewInvVect(InvTypeTx, txHash)
// Empty notfound message.
NoInv := NewMsgNotFound()
NoInvEncoded := []byte{
0x00, // Varint for number of inventory vectors
}
// NotFound message with multiple inventory vectors.
MultiInv := NewMsgNotFound()
MultiInv.AddInvVect(iv)
MultiInv.AddInvVect(iv2)
MultiInvEncoded := []byte{
0x02, // Varint for number of inv vectors
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
0x01, 0x00, 0x00, 0x00, // InvTypeTx
0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf,
0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8,
0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98,
0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash
}
tests := []struct {
in *MsgNotFound // Message to encode
out *MsgNotFound // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version with no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0035Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version BIP0031Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
MultipleAddressVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgNotFound
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestNotFoundWireErrors performs negative tests against wire encode and decode
// of MsgNotFound to confirm error paths work correctly.
func TestNotFoundWireErrors(t *testing.T) {
pver := ProtocolVersion
wireErr := &MessageError{}
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := chainhash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
iv := NewInvVect(InvTypeBlock, blockHash)
// Base message used to induce errors.
baseNotFound := NewMsgNotFound()
baseNotFound.AddInvVect(iv)
baseNotFoundEncoded := []byte{
0x02, // Varint for number of inv vectors
0x02, 0x00, 0x00, 0x00, // InvTypeBlock
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
}
// Message that forces an error by having more than the max allowed inv
// vectors.
maxNotFound := NewMsgNotFound()
for i := 0; i < MaxInvPerMsg; i++ {
maxNotFound.AddInvVect(iv)
}
maxNotFound.InvList = append(maxNotFound.InvList, iv)
maxNotFoundEncoded := []byte{
0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001)
}
tests := []struct {
in *MsgNotFound // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in inventory vector count
{baseNotFound, baseNotFoundEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in inventory list.
{baseNotFound, baseNotFoundEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxNotFound, maxNotFoundEncoded, pver, BaseEncoding, 3, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgNotFound
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+87
View File
@@ -0,0 +1,87 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"io"
)
// MsgPing implements the Message interface and represents a bitcoin ping
// message.
//
// For versions BIP0031Version and earlier, it is used primarily to confirm
// that a connection is still valid. A transmission error is typically
// interpreted as a closed connection and that the peer should be removed.
// For versions AFTER BIP0031Version it contains an identifier which can be
// returned in the pong message to determine network timing.
//
// The payload for this message just consists of a nonce used for identifying
// it later.
type MsgPing struct {
// Unique value associated with message that is used to identify
// specific ping message.
Nonce uint64
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgPing) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
// There was no nonce for BIP0031Version and earlier.
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others.
if pver > BIP0031Version {
err := readElement(r, &msg.Nonce)
if err != nil {
return err
}
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgPing) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// There was no nonce for BIP0031Version and earlier.
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others.
if pver > BIP0031Version {
err := writeElement(w, msg.Nonce)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgPing) Command() string {
return CmdPing
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgPing) MaxPayloadLength(pver uint32) uint32 {
plen := uint32(0)
// There was no nonce for BIP0031Version and earlier.
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others.
if pver > BIP0031Version {
// Nonce 8 bytes.
plen += 8
}
return plen
}
// NewMsgPing returns a new bitcoin ping message that conforms to the Message
// interface. See MsgPing for details.
func NewMsgPing(nonce uint64) *MsgPing {
return &MsgPing{
Nonce: nonce,
}
}
+245
View File
@@ -0,0 +1,245 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestPing tests the MsgPing API against the latest protocol version.
func TestPing(t *testing.T) {
pver := ProtocolVersion
// Ensure we get the same nonce back out.
nonce, err := RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
msg := 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)
}
}
// 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 := BIP0031Version
enc := BaseEncoding
nonce, err := RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
msg := 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, enc)
if err != nil {
t.Errorf("encode of MsgPing failed %v err <%v>", msg, err)
}
// Test decode with old protocol version.
readmsg := NewMsgPing(0)
err = readmsg.BtcDecode(&buf, pver, enc)
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)
}
}
// TestPingCrossProtocol tests the MsgPing API when encoding with the latest
// protocol version and decoding with BIP0031Version.
func TestPingCrossProtocol(t *testing.T) {
nonce, err := RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
msg := 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, ProtocolVersion, BaseEncoding)
if err != nil {
t.Errorf("encode of MsgPing failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
readmsg := NewMsgPing(0)
err = readmsg.BtcDecode(&buf, BIP0031Version, BaseEncoding)
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")
}
}
// TestPingWire tests the MsgPing wire encode and decode for various protocol
// versions.
func TestPingWire(t *testing.T) {
tests := []struct {
in MsgPing // Message to encode
out MsgPing // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
MsgPing{Nonce: 123123}, // 0x1e0f3
MsgPing{Nonce: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0031Version+1
{
MsgPing{Nonce: 456456}, // 0x6f708
MsgPing{Nonce: 456456}, // 0x6f708
[]byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
BIP0031Version + 1,
BaseEncoding,
},
// Protocol version BIP0031Version
{
MsgPing{Nonce: 789789}, // 0xc0d1d
MsgPing{Nonce: 0}, // No nonce for pver
[]byte{}, // No nonce for pver
BIP0031Version,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgPing
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestPingWireErrors performs negative tests against wire encode and decode
// of MsgPing to confirm error paths work correctly.
func TestPingWireErrors(t *testing.T) {
pver := ProtocolVersion
tests := []struct {
in *MsgPing // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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.
{
&MsgPing{Nonce: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00},
pver,
BaseEncoding,
2,
io.ErrShortWrite,
io.ErrUnexpectedEOF,
},
}
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, test.enc)
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 MsgPing
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
+78
View File
@@ -0,0 +1,78 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgPong implements the Message interface and represents a bitcoin pong
// message which is used primarily to confirm that a connection is still valid
// in response to a bitcoin ping message (MsgPing).
//
// This message was not added until protocol versions AFTER BIP0031Version.
type MsgPong struct {
// Unique value associated with message that is used to identify
// specific ping message.
Nonce uint64
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgPong) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
// NOTE: <= is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others.
if pver <= BIP0031Version {
str := fmt.Sprintf("pong message invalid for protocol "+
"version %d", pver)
return messageError("MsgPong.BtcDecode", str)
}
return readElement(r, &msg.Nonce)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgPong) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
// NOTE: <= is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others.
if pver <= BIP0031Version {
str := fmt.Sprintf("pong message invalid for protocol "+
"version %d", pver)
return messageError("MsgPong.BtcEncode", str)
}
return writeElement(w, msg.Nonce)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgPong) Command() string {
return CmdPong
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgPong) MaxPayloadLength(pver uint32) uint32 {
plen := uint32(0)
// The pong message did not exist for BIP0031Version and earlier.
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others.
if pver > BIP0031Version {
// Nonce 8 bytes.
plen += 8
}
return plen
}
// NewMsgPong returns a new bitcoin pong message that conforms to the Message
// interface. See MsgPong for details.
func NewMsgPong(nonce uint64) *MsgPong {
return &MsgPong{
Nonce: nonce,
}
}
+277
View File
@@ -0,0 +1,277 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestPongLatest tests the MsgPong API against the latest protocol version.
func TestPongLatest(t *testing.T) {
enc := BaseEncoding
pver := ProtocolVersion
nonce, err := RandomUint64()
if err != nil {
t.Errorf("RandomUint64: error generating nonce: %v", err)
}
msg := 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, enc)
if err != nil {
t.Errorf("encode of MsgPong failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := NewMsgPong(0)
err = readmsg.BtcDecode(&buf, pver, enc)
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)
}
}
// 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 := BIP0031Version
enc := BaseEncoding
nonce, err := RandomUint64()
if err != nil {
t.Errorf("Error generating nonce: %v", err)
}
msg := 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, enc)
if err == nil {
t.Errorf("encode of MsgPong succeeded when it shouldn't have %v",
msg)
}
// Test decode with old protocol version.
readmsg := NewMsgPong(0)
err = readmsg.BtcDecode(&buf, pver, enc)
if err == nil {
t.Errorf("decode of MsgPong succeeded when it shouldn't have %v",
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)
}
}
// TestPongCrossProtocol tests the MsgPong API when encoding with the latest
// protocol version and decoding with BIP0031Version.
func TestPongCrossProtocol(t *testing.T) {
nonce, err := RandomUint64()
if err != nil {
t.Errorf("Error generating nonce: %v", err)
}
msg := 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, ProtocolVersion, BaseEncoding)
if err != nil {
t.Errorf("encode of MsgPong failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
readmsg := NewMsgPong(0)
err = readmsg.BtcDecode(&buf, BIP0031Version, BaseEncoding)
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")
}
}
// TestPongWire tests the MsgPong wire encode and decode for various protocol
// versions.
func TestPongWire(t *testing.T) {
tests := []struct {
in MsgPong // Message to encode
out MsgPong // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
MsgPong{Nonce: 123123}, // 0x1e0f3
MsgPong{Nonce: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0031Version+1
{
MsgPong{Nonce: 456456}, // 0x6f708
MsgPong{Nonce: 456456}, // 0x6f708
[]byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
BIP0031Version + 1,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgPong
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", 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 := ProtocolVersion
pverNoPong := BIP0031Version
wireErr := &MessageError{}
basePong := NewMsgPong(123123) // 0x1e0f3
basePongEncoded := []byte{
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
}
tests := []struct {
in *MsgPong // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error due to unsupported protocol version.
{basePong, basePongEncoded, pverNoPong, BaseEncoding, 4, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgPong
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+186
View File
@@ -0,0 +1,186 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// RejectCode represents a numeric value by which a remote peer indicates
// why a message was rejected.
type RejectCode uint8
// These constants define the various supported reject codes.
const (
RejectMalformed RejectCode = 0x01
RejectInvalid RejectCode = 0x10
RejectObsolete RejectCode = 0x11
RejectDuplicate RejectCode = 0x12
RejectNonstandard RejectCode = 0x40
RejectDust RejectCode = 0x41
RejectInsufficientFee RejectCode = 0x42
RejectCheckpoint RejectCode = 0x43
)
// Map of reject codes back strings for pretty printing.
var rejectCodeStrings = map[RejectCode]string{
RejectMalformed: "REJECT_MALFORMED",
RejectInvalid: "REJECT_INVALID",
RejectObsolete: "REJECT_OBSOLETE",
RejectDuplicate: "REJECT_DUPLICATE",
RejectNonstandard: "REJECT_NONSTANDARD",
RejectDust: "REJECT_DUST",
RejectInsufficientFee: "REJECT_INSUFFICIENTFEE",
RejectCheckpoint: "REJECT_CHECKPOINT",
}
// String returns the RejectCode in human-readable form.
func (code RejectCode) String() string {
if s, ok := rejectCodeStrings[code]; ok {
return s
}
return fmt.Sprintf("Unknown RejectCode (%d)", uint8(code))
}
// MsgReject implements the Message interface and represents a bitcoin reject
// message.
//
// This message was not added until protocol version RejectVersion.
type MsgReject struct {
// Cmd is the command for the message which was rejected such as
// as CmdBlock or CmdTx. This can be obtained from the Command function
// of a Message.
Cmd string
// RejectCode is a code indicating why the command was rejected. It
// is encoded as a uint8 on the wire.
Code RejectCode
// Reason is a human-readable string with specific details (over and
// above the reject code) about why the command was rejected.
Reason string
// Hash identifies a specific block or transaction that was rejected
// and therefore only applies the MsgBlock and MsgTx messages.
Hash chainhash.Hash
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < RejectVersion {
str := fmt.Sprintf("reject message invalid for protocol "+
"version %d", pver)
return messageError("MsgReject.BtcDecode", str)
}
// Command that was rejected.
cmd, err := ReadVarString(r, pver)
if err != nil {
return err
}
msg.Cmd = cmd
// Code indicating why the command was rejected.
err = readElement(r, &msg.Code)
if err != nil {
return err
}
// Human readable string with specific details (over and above the
// reject code above) about why the command was rejected.
reason, err := ReadVarString(r, pver)
if err != nil {
return err
}
msg.Reason = reason
// CmdBlock and CmdTx messages have an additional hash field that
// identifies the specific block or transaction.
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
err := readElement(r, &msg.Hash)
if err != nil {
return err
}
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < RejectVersion {
str := fmt.Sprintf("reject message invalid for protocol "+
"version %d", pver)
return messageError("MsgReject.BtcEncode", str)
}
// Command that was rejected.
err := WriteVarString(w, pver, msg.Cmd)
if err != nil {
return err
}
// Code indicating why the command was rejected.
err = writeElement(w, msg.Code)
if err != nil {
return err
}
// Human readable string with specific details (over and above the
// reject code above) about why the command was rejected.
err = WriteVarString(w, pver, msg.Reason)
if err != nil {
return err
}
// CmdBlock and CmdTx messages have an additional hash field that
// identifies the specific block or transaction.
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
err := writeElement(w, &msg.Hash)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgReject) Command() string {
return CmdReject
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgReject) MaxPayloadLength(pver uint32) uint32 {
plen := uint32(0)
// The reject message did not exist before protocol version
// RejectVersion.
if pver >= RejectVersion {
// Unfortunately the bitcoin protocol does not enforce a sane
// limit on the length of the reason, so the max payload is the
// overall maximum message payload.
plen = MaxMessagePayload
}
return plen
}
// NewMsgReject returns a new bitcoin reject message that conforms to the
// Message interface. See MsgReject for details.
func NewMsgReject(command string, code RejectCode, reason string) *MsgReject {
return &MsgReject{
Cmd: command,
Code: code,
Reason: reason,
}
}
+389
View File
@@ -0,0 +1,389 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestRejectCodeStringer tests the stringized output for the reject code type.
func TestRejectCodeStringer(t *testing.T) {
tests := []struct {
in RejectCode
want string
}{
{RejectMalformed, "REJECT_MALFORMED"},
{RejectInvalid, "REJECT_INVALID"},
{RejectObsolete, "REJECT_OBSOLETE"},
{RejectDuplicate, "REJECT_DUPLICATE"},
{RejectNonstandard, "REJECT_NONSTANDARD"},
{RejectDust, "REJECT_DUST"},
{RejectInsufficientFee, "REJECT_INSUFFICIENTFEE"},
{RejectCheckpoint, "REJECT_CHECKPOINT"},
{0xff, "Unknown RejectCode (255)"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.String()
if result != test.want {
t.Errorf("String #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
// TestRejectLatest tests the MsgPong API against the latest protocol version.
func TestRejectLatest(t *testing.T) {
pver := ProtocolVersion
enc := BaseEncoding
// Create reject message data.
rejCommand := (&MsgBlock{}).Command()
rejCode := RejectDuplicate
rejReason := "duplicate block"
rejHash := mainNetGenesisHash
// Ensure we get the correct data back out.
msg := NewMsgReject(rejCommand, rejCode, rejReason)
msg.Hash = rejHash
if msg.Cmd != rejCommand {
t.Errorf("NewMsgReject: wrong rejected command - got %v, "+
"want %v", msg.Cmd, rejCommand)
}
if msg.Code != rejCode {
t.Errorf("NewMsgReject: wrong rejected code - got %v, "+
"want %v", msg.Code, rejCode)
}
if msg.Reason != rejReason {
t.Errorf("NewMsgReject: wrong rejected reason - got %v, "+
"want %v", msg.Reason, rejReason)
}
// Ensure the command is expected value.
wantCmd := "reject"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgReject: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(MaxMessagePayload)
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, enc)
if err != nil {
t.Errorf("encode of MsgReject failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readMsg := MsgReject{}
err = readMsg.BtcDecode(&buf, pver, enc)
if err != nil {
t.Errorf("decode of MsgReject failed %v err <%v>", buf.Bytes(),
err)
}
// Ensure decoded data is the same.
if msg.Cmd != readMsg.Cmd {
t.Errorf("Should get same reject command - got %v, want %v",
readMsg.Cmd, msg.Cmd)
}
if msg.Code != readMsg.Code {
t.Errorf("Should get same reject code - got %v, want %v",
readMsg.Code, msg.Code)
}
if msg.Reason != readMsg.Reason {
t.Errorf("Should get same reject reason - got %v, want %v",
readMsg.Reason, msg.Reason)
}
if msg.Hash != readMsg.Hash {
t.Errorf("Should get same reject hash - got %v, want %v",
readMsg.Hash, msg.Hash)
}
}
// TestRejectBeforeAdded tests the MsgReject API against a protocol version
// before the version which introduced it (RejectVersion).
func TestRejectBeforeAdded(t *testing.T) {
// Use the protocol version just prior to RejectVersion.
pver := RejectVersion - 1
enc := BaseEncoding
// Create reject message data.
rejCommand := (&MsgBlock{}).Command()
rejCode := RejectDuplicate
rejReason := "duplicate block"
rejHash := mainNetGenesisHash
msg := NewMsgReject(rejCommand, rejCode, rejReason)
msg.Hash = rejHash
// 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 reject protocol version %d.",
pver)
}
// Test encode with old protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, pver, enc)
if err == nil {
t.Errorf("encode of MsgReject succeeded when it shouldn't "+
"have %v", msg)
}
// // Test decode with old protocol version.
readMsg := MsgReject{}
err = readMsg.BtcDecode(&buf, pver, enc)
if err == nil {
t.Errorf("decode of MsgReject succeeded when it shouldn't "+
"have %v", spew.Sdump(buf.Bytes()))
}
// Since this protocol version doesn't support reject, make sure various
// fields didn't get encoded and decoded back out.
if msg.Cmd == readMsg.Cmd {
t.Errorf("Should not get same reject command for protocol "+
"version %d", pver)
}
if msg.Code == readMsg.Code {
t.Errorf("Should not get same reject code for protocol "+
"version %d", pver)
}
if msg.Reason == readMsg.Reason {
t.Errorf("Should not get same reject reason for protocol "+
"version %d", pver)
}
if msg.Hash == readMsg.Hash {
t.Errorf("Should not get same reject hash for protocol "+
"version %d", pver)
}
}
// TestRejectCrossProtocol tests the MsgReject API when encoding with the latest
// protocol version and decoded with a version before the version which
// introduced it (RejectVersion).
func TestRejectCrossProtocol(t *testing.T) {
// Create reject message data.
rejCommand := (&MsgBlock{}).Command()
rejCode := RejectDuplicate
rejReason := "duplicate block"
rejHash := mainNetGenesisHash
msg := NewMsgReject(rejCommand, rejCode, rejReason)
msg.Hash = rejHash
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, BaseEncoding)
if err != nil {
t.Errorf("encode of MsgReject failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
readMsg := MsgReject{}
err = readMsg.BtcDecode(&buf, RejectVersion-1, BaseEncoding)
if err == nil {
t.Errorf("encode of MsgReject succeeded when it shouldn't "+
"have %v", msg)
}
// Since one of the protocol versions doesn't support the reject
// message, make sure the various fields didn't get encoded and decoded
// back out.
if msg.Cmd == readMsg.Cmd {
t.Errorf("Should not get same reject command for cross protocol")
}
if msg.Code == readMsg.Code {
t.Errorf("Should not get same reject code for cross protocol")
}
if msg.Reason == readMsg.Reason {
t.Errorf("Should not get same reject reason for cross protocol")
}
if msg.Hash == readMsg.Hash {
t.Errorf("Should not get same reject hash for cross protocol")
}
}
// TestRejectWire tests the MsgReject wire encode and decode for various
// protocol versions.
func TestRejectWire(t *testing.T) {
tests := []struct {
msg MsgReject // Message to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version rejected command version (no hash).
{
MsgReject{
Cmd: "version",
Code: RejectDuplicate,
Reason: "duplicate version",
},
[]byte{
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version"
0x12, // RejectDuplicate
0x11, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, // "duplicate version"
},
ProtocolVersion,
BaseEncoding,
},
// Latest protocol version rejected command block (has hash).
{
MsgReject{
Cmd: "block",
Code: RejectDuplicate,
Reason: "duplicate block",
Hash: mainNetGenesisHash,
},
[]byte{
0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "block"
0x12, // RejectDuplicate
0x0f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "duplicate block"
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // mainNetGenesisHash
},
ProtocolVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.msg.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgReject
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(msg, test.msg) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.msg))
continue
}
}
}
// TestRejectWireErrors performs negative tests against wire encode and decode
// of MsgReject to confirm error paths work correctly.
func TestRejectWireErrors(t *testing.T) {
pver := ProtocolVersion
pverNoReject := RejectVersion - 1
wireErr := &MessageError{}
baseReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
baseReject.Hash = mainNetGenesisHash
baseRejectEncoded := []byte{
0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "block"
0x12, // RejectDuplicate
0x0f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "duplicate block"
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // mainNetGenesisHash
}
tests := []struct {
in *MsgReject // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
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 reject command.
{baseReject, baseRejectEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in reject code.
{baseReject, baseRejectEncoded, pver, BaseEncoding, 6, io.ErrShortWrite, io.EOF},
// Force error in reject reason.
{baseReject, baseRejectEncoded, pver, BaseEncoding, 7, io.ErrShortWrite, io.EOF},
// Force error in reject hash.
{baseReject, baseRejectEncoded, pver, BaseEncoding, 23, io.ErrShortWrite, io.EOF},
// Force error due to unsupported protocol version.
{baseReject, baseRejectEncoded, pverNoReject, BaseEncoding, 6, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgReject
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// MsgSendHeaders implements the Message interface and represents a bitcoin
// sendheaders message. It is used to request the peer send block headers
// rather than inventory vectors.
//
// This message has no payload and was not added until protocol versions
// starting with SendHeadersVersion.
type MsgSendHeaders struct{}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgSendHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < SendHeadersVersion {
str := fmt.Sprintf("sendheaders message invalid for protocol "+
"version %d", pver)
return messageError("MsgSendHeaders.BtcDecode", str)
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgSendHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < SendHeadersVersion {
str := fmt.Sprintf("sendheaders message invalid for protocol "+
"version %d", pver)
return messageError("MsgSendHeaders.BtcEncode", str)
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgSendHeaders) Command() string {
return CmdSendHeaders
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgSendHeaders) MaxPayloadLength(pver uint32) uint32 {
return 0
}
// NewMsgSendHeaders returns a new bitcoin sendheaders message that conforms to
// the Message interface. See MsgSendHeaders for details.
func NewMsgSendHeaders() *MsgSendHeaders {
return &MsgSendHeaders{}
}
+193
View File
@@ -0,0 +1,193 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestSendHeaders tests the MsgSendHeaders API against the latest protocol
// version.
func TestSendHeaders(t *testing.T) {
pver := ProtocolVersion
enc := BaseEncoding
// Ensure the command is expected value.
wantCmd := "sendheaders"
msg := NewMsgSendHeaders()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgSendHeaders: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
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 latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, pver, enc)
if err != nil {
t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg,
err)
}
// Older protocol versions should fail encode since message didn't
// exist yet.
oldPver := SendHeadersVersion - 1
err = msg.BtcEncode(&buf, oldPver, enc)
if err == nil {
s := "encode of MsgSendHeaders passed for old protocol " +
"version %v err <%v>"
t.Errorf(s, msg, err)
}
// Test decode with latest protocol version.
readmsg := NewMsgSendHeaders()
err = readmsg.BtcDecode(&buf, pver, enc)
if err != nil {
t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf,
err)
}
// Older protocol versions should fail decode since message didn't
// exist yet.
err = readmsg.BtcDecode(&buf, oldPver, enc)
if err == nil {
s := "decode of MsgSendHeaders passed for old protocol " +
"version %v err <%v>"
t.Errorf(s, msg, err)
}
}
// TestSendHeadersBIP0130 tests the MsgSendHeaders API against the protocol
// prior to version SendHeadersVersion.
func TestSendHeadersBIP0130(t *testing.T) {
// Use the protocol version just prior to SendHeadersVersion changes.
pver := SendHeadersVersion - 1
enc := BaseEncoding
msg := NewMsgSendHeaders()
// Test encode with old protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, pver, enc)
if err == nil {
t.Errorf("encode of MsgSendHeaders succeeded when it should " +
"have failed")
}
// Test decode with old protocol version.
readmsg := NewMsgSendHeaders()
err = readmsg.BtcDecode(&buf, pver, enc)
if err == nil {
t.Errorf("decode of MsgSendHeaders succeeded when it should " +
"have failed")
}
}
// TestSendHeadersCrossProtocol tests the MsgSendHeaders API when encoding with
// the latest protocol version and decoding with SendHeadersVersion.
func TestSendHeadersCrossProtocol(t *testing.T) {
enc := BaseEncoding
msg := NewMsgSendHeaders()
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, ProtocolVersion, enc)
if err != nil {
t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg,
err)
}
// Decode with old protocol version.
readmsg := NewMsgSendHeaders()
err = readmsg.BtcDecode(&buf, SendHeadersVersion, enc)
if err != nil {
t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf,
err)
}
}
// TestSendHeadersWire tests the MsgSendHeaders wire encode and decode for
// various protocol versions.
func TestSendHeadersWire(t *testing.T) {
msgSendHeaders := NewMsgSendHeaders()
msgSendHeadersEncoded := []byte{}
tests := []struct {
in *MsgSendHeaders // Message to encode
out *MsgSendHeaders // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
msgSendHeaders,
msgSendHeaders,
msgSendHeadersEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version SendHeadersVersion+1
{
msgSendHeaders,
msgSendHeaders,
msgSendHeadersEncoded,
SendHeadersVersion + 1,
BaseEncoding,
},
// Protocol version SendHeadersVersion
{
msgSendHeaders,
msgSendHeaders,
msgSendHeadersEncoded,
SendHeadersVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgSendHeaders
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
+1027
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"io"
)
// MsgVerAck defines a bitcoin verack message which is used for a peer to
// acknowledge a version message (MsgVersion) after it has used the information
// to negotiate parameters. It implements the Message interface.
//
// This message has no payload.
type MsgVerAck struct{}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgVerAck) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgVerAck) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgVerAck) Command() string {
return CmdVerAck
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgVerAck) MaxPayloadLength(pver uint32) uint32 {
return 0
}
// NewMsgVerAck returns a new bitcoin verack message that conforms to the
// Message interface.
func NewMsgVerAck() *MsgVerAck {
return &MsgVerAck{}
}
+125
View File
@@ -0,0 +1,125 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestVerAck tests the MsgVerAck API.
func TestVerAck(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "verack"
msg := NewMsgVerAck()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgVerAck: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
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)
}
}
// TestVerAckWire tests the MsgVerAck wire encode and decode for various
// protocol versions.
func TestVerAckWire(t *testing.T) {
msgVerAck := NewMsgVerAck()
msgVerAckEncoded := []byte{}
tests := []struct {
in *MsgVerAck // Message to encode
out *MsgVerAck // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0035Version.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgVerAck
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
+269
View File
@@ -0,0 +1,269 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"fmt"
"io"
"strings"
"time"
)
// MaxUserAgentLen is the maximum allowed length for the user agent field in a
// version message (MsgVersion).
const MaxUserAgentLen = 256
// DefaultUserAgent for wire in the stack
const DefaultUserAgent = "/btcwire:0.5.0/"
// MsgVersion implements the Message interface and represents a bitcoin version
// message. It is used for a peer to advertise itself as soon as an outbound
// connection is made. The remote peer then uses this information along with
// its own to negotiate. The remote peer must then respond with a version
// message of its own containing the negotiated values followed by a verack
// message (MsgVerAck). This exchange must take place before any further
// communication is allowed to proceed.
type MsgVersion struct {
// Version of the protocol the node is using.
ProtocolVersion int32
// Bitfield which identifies the enabled services.
Services ServiceFlag
// Time the message was generated. This is encoded as an int64 on the wire.
Timestamp time.Time
// Address of the remote peer.
AddrYou NetAddress
// Address of the local peer.
AddrMe NetAddress
// Unique value associated with message that is used to detect self
// connections.
Nonce uint64
// The user agent that generated messsage. This is a encoded as a varString
// on the wire. This has a max length of MaxUserAgentLen.
UserAgent string
// Last block seen by the generator of the version message.
LastBlock int32
// Don't announce transactions to peer.
DisableRelayTx bool
}
// HasService returns whether the specified service is supported by the peer
// that generated the message.
func (msg *MsgVersion) HasService(service ServiceFlag) bool {
return msg.Services&service == service
}
// AddService adds service as a supported service by the peer generating the
// message.
func (msg *MsgVersion) AddService(service ServiceFlag) {
msg.Services |= service
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// The version message is special in that the protocol version hasn't been
// negotiated yet. As a result, the pver field is ignored and any fields which
// are added in new versions are optional. This also mean that r must be a
// *bytes.Buffer so the number of remaining bytes can be ascertained.
//
// This is part of the Message interface implementation.
func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
buf, ok := r.(*bytes.Buffer)
if !ok {
return fmt.Errorf("MsgVersion.BtcDecode reader is not a " +
"*bytes.Buffer")
}
err := readElements(buf, &msg.ProtocolVersion, &msg.Services,
(*int64Time)(&msg.Timestamp))
if err != nil {
return err
}
err = readNetAddress(buf, pver, &msg.AddrYou, false)
if err != nil {
return err
}
// Protocol versions >= 106 added a from address, nonce, and user agent
// field and they are only considered present if there are bytes
// remaining in the message.
if buf.Len() > 0 {
err = readNetAddress(buf, pver, &msg.AddrMe, false)
if err != nil {
return err
}
}
if buf.Len() > 0 {
err = readElement(buf, &msg.Nonce)
if err != nil {
return err
}
}
if buf.Len() > 0 {
userAgent, err := ReadVarString(buf, pver)
if err != nil {
return err
}
err = validateUserAgent(userAgent)
if err != nil {
return err
}
msg.UserAgent = userAgent
}
// Protocol versions >= 209 added a last known block field. It is only
// considered present if there are bytes remaining in the message.
if buf.Len() > 0 {
err = readElement(buf, &msg.LastBlock)
if err != nil {
return err
}
}
// There was no relay transactions field before BIP0037Version, but
// the default behavior prior to the addition of the field was to always
// relay transactions.
if buf.Len() > 0 {
// It's safe to ignore the error here since the buffer has at
// least one byte and that byte will result in a boolean value
// regardless of its value. Also, the wire encoding for the
// field is true when transactions should be relayed, so reverse
// it for the DisableRelayTx field.
var relayTx bool
readElement(r, &relayTx)
msg.DisableRelayTx = !relayTx
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
err := validateUserAgent(msg.UserAgent)
if err != nil {
return err
}
err = writeElements(w, msg.ProtocolVersion, msg.Services,
msg.Timestamp.Unix())
if err != nil {
return err
}
err = writeNetAddress(w, pver, &msg.AddrYou, false)
if err != nil {
return err
}
err = writeNetAddress(w, pver, &msg.AddrMe, false)
if err != nil {
return err
}
err = writeElement(w, msg.Nonce)
if err != nil {
return err
}
err = WriteVarString(w, pver, msg.UserAgent)
if err != nil {
return err
}
err = writeElement(w, msg.LastBlock)
if err != nil {
return err
}
// There was no relay transactions field before BIP0037Version. Also,
// the wire encoding for the field is true when transactions should be
// relayed, so reverse it from the DisableRelayTx field.
if pver >= BIP0037Version {
err = writeElement(w, !msg.DisableRelayTx)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgVersion) Command() string {
return CmdVersion
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 {
// XXX: <= 106 different
// 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 useragent length + last block 4 bytes +
// relay transactions flag 1 byte.
return 33 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload +
MaxUserAgentLen
}
// NewMsgVersion returns a new bitcoin version message that conforms to the
// Message interface using the passed parameters and defaults for the remaining
// fields.
func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
lastBlock int32) *MsgVersion {
// Limit the timestamp to one second precision since the protocol
// doesn't support better.
return &MsgVersion{
ProtocolVersion: int32(ProtocolVersion),
Services: 0,
Timestamp: time.Unix(time.Now().Unix(), 0),
AddrYou: *you,
AddrMe: *me,
Nonce: nonce,
UserAgent: DefaultUserAgent,
LastBlock: lastBlock,
DisableRelayTx: false,
}
}
// validateUserAgent checks userAgent length against MaxUserAgentLen
func validateUserAgent(userAgent string) error {
if len(userAgent) > MaxUserAgentLen {
str := fmt.Sprintf("user agent too long [len %v, max %v]",
len(userAgent), MaxUserAgentLen)
return messageError("MsgVersion", str)
}
return nil
}
// AddUserAgent adds a user agent to the user agent string for the version
// message. The version string is not defined to any strict format, although
// it is recommended to use the form "major.minor.revision" e.g. "2.6.41".
func (msg *MsgVersion) AddUserAgent(name string, version string,
comments ...string) error {
newUserAgent := fmt.Sprintf("%s:%s", name, version)
if len(comments) != 0 {
newUserAgent = fmt.Sprintf("%s(%s)", newUserAgent,
strings.Join(comments, "; "))
}
newUserAgent = fmt.Sprintf("%s%s/", msg.UserAgent, newUserAgent)
err := validateUserAgent(newUserAgent)
if err != nil {
return err
}
msg.UserAgent = newUserAgent
return nil
}
+564
View File
@@ -0,0 +1,564 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"net"
"reflect"
"strings"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
)
// TestVersion tests the MsgVersion API.
func TestVersion(t *testing.T) {
pver := ProtocolVersion
// Create version message data.
lastBlock := int32(234234)
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}
you := NewNetAddress(tcpAddrYou, SFNodeNetwork)
nonce, err := RandomUint64()
if err != nil {
t.Errorf("RandomUint64: error generating nonce: %v", err)
}
// Ensure we get the correct data back out.
msg := 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 != DefaultUserAgent {
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
msg.UserAgent, DefaultUserAgent)
}
if msg.LastBlock != lastBlock {
t.Errorf("NewMsgVersion: wrong last block - got %v, want %v",
msg.LastBlock, lastBlock)
}
if msg.DisableRelayTx {
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 := 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",
MaxUserAgentLen-len(customUserAgent)-2+1), "")
if _, ok := err.(*MessageError); !ok {
t.Errorf("AddUserAgent: expected error not received "+
"- got %v, want %T", err, 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(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(358)
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(SFNodeNetwork)
if msg.Services != SFNodeNetwork {
t.Errorf("AddService: wrong services - got %v, want %v",
msg.Services, SFNodeNetwork)
}
if !msg.HasService(SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service not set")
}
}
// TestVersionWire tests the MsgVersion wire encode and decode for various
// protocol versions.
func TestVersionWire(t *testing.T) {
// verRelayTxFalse and verRelayTxFalseEncoded is a version message as of
// BIP0037Version with the transaction relay disabled.
baseVersionBIP0037Copy := *baseVersionBIP0037
verRelayTxFalse := &baseVersionBIP0037Copy
verRelayTxFalse.DisableRelayTx = true
verRelayTxFalseEncoded := make([]byte, len(baseVersionBIP0037Encoded))
copy(verRelayTxFalseEncoded, baseVersionBIP0037Encoded)
verRelayTxFalseEncoded[len(verRelayTxFalseEncoded)-1] = 0
tests := []struct {
in *MsgVersion // Message to encode
out *MsgVersion // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
// Latest protocol version.
{
baseVersionBIP0037,
baseVersionBIP0037,
baseVersionBIP0037Encoded,
ProtocolVersion,
BaseEncoding,
},
// Protocol version BIP0037Version with relay transactions field
// true.
{
baseVersionBIP0037,
baseVersionBIP0037,
baseVersionBIP0037Encoded,
BIP0037Version,
BaseEncoding,
},
// Protocol version BIP0037Version with relay transactions field
// false.
{
verRelayTxFalse,
verRelayTxFalse,
verRelayTxFalseEncoded,
BIP0037Version,
BaseEncoding,
},
// Protocol version BIP0035Version.
{
baseVersion,
baseVersion,
baseVersionEncoded,
BIP0035Version,
BaseEncoding,
},
// Protocol version BIP0031Version.
{
baseVersion,
baseVersion,
baseVersionEncoded,
BIP0031Version,
BaseEncoding,
},
// Protocol version NetAddressTimeVersion.
{
baseVersion,
baseVersion,
baseVersionEncoded,
NetAddressTimeVersion,
BaseEncoding,
},
// Protocol version MultipleAddressVersion.
{
baseVersion,
baseVersion,
baseVersionEncoded,
MultipleAddressVersion,
BaseEncoding,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.BtcEncode(&buf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgVersion
rbuf := bytes.NewBuffer(test.buf)
err = msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}
// TestVersionWireErrors performs negative tests against wire encode and
// decode of MsgGetHeaders to confirm error paths work correctly.
func TestVersionWireErrors(t *testing.T) {
// Use protocol version 60002 specifically here instead of the latest
// because the test data is using bytes encoded with that protocol
// version.
pver := uint32(60002)
enc := BaseEncoding
wireErr := &MessageError{}
// Ensure calling MsgVersion.BtcDecode with a non *bytes.Buffer returns
// error.
fr := newFixedReader(0, []byte{})
if err := baseVersion.BtcDecode(fr, pver, enc); err == nil {
t.Errorf("Did not received error when calling " +
"MsgVersion.BtcDecode with non *bytes.Buffer")
}
// Copy the base version and change the user agent to exceed max limits.
bvc := *baseVersion
exceedUAVer := &bvc
newUA := "/" + strings.Repeat("t", MaxUserAgentLen-8+1) + ":0.0.1/"
exceedUAVer.UserAgent = newUA
// Encode the new UA length as a varint.
var newUAVarIntBuf bytes.Buffer
err := WriteVarInt(&newUAVarIntBuf, pver, uint64(len(newUA)))
if err != nil {
t.Errorf("WriteVarInt: error %v", err)
}
// Make a new buffer big enough to hold the base version plus the new
// bytes for the bigger varint to hold the new size of the user agent
// and the new user agent string. Then stich it all together.
newLen := len(baseVersionEncoded) - len(baseVersion.UserAgent)
newLen = newLen + len(newUAVarIntBuf.Bytes()) - 1 + len(newUA)
exceedUAVerEncoded := make([]byte, newLen)
copy(exceedUAVerEncoded, baseVersionEncoded[0:80])
copy(exceedUAVerEncoded[80:], newUAVarIntBuf.Bytes())
copy(exceedUAVerEncoded[83:], []byte(newUA))
copy(exceedUAVerEncoded[83+len(newUA):], baseVersionEncoded[97:100])
tests := []struct {
in *MsgVersion // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Force error in protocol version.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
// Force error in services.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 4, io.ErrShortWrite, io.EOF},
// Force error in timestamp.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 12, io.ErrShortWrite, io.EOF},
// Force error in remote address.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 20, io.ErrShortWrite, io.EOF},
// Force error in local address.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 47, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in nonce.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 73, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in user agent length.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 81, io.ErrShortWrite, io.EOF},
// Force error in user agent.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 82, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in last block.
{baseVersion, baseVersionEncoded, pver, BaseEncoding, 98, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in relay tx - no read error should happen since
// it's optional.
{
baseVersionBIP0037, baseVersionBIP0037Encoded,
BIP0037Version, BaseEncoding, 101, io.ErrShortWrite, nil,
},
// Force error due to user agent too big
{exceedUAVer, exceedUAVerEncoded, pver, BaseEncoding, newLen, wireErr, wireErr},
}
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, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*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 MsgVersion
buf := bytes.NewBuffer(test.buf[0:test.max])
err = msg.BtcDecode(buf, test.pver, test.enc)
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 MessageError, check them for
// equality.
if _, ok := err.(*MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}
// TestVersionOptionalFields performs tests to ensure that an encoded version
// messages that omit optional fields are handled correctly.
func TestVersionOptionalFields(t *testing.T) {
// onlyRequiredVersion is a version message that only contains the
// required versions and all other values set to their default values.
onlyRequiredVersion := MsgVersion{
ProtocolVersion: 60002,
Services: SFNodeNetwork,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST)
AddrYou: NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8333,
},
}
onlyRequiredVersionEncoded := make([]byte, len(baseVersionEncoded)-55)
copy(onlyRequiredVersionEncoded, baseVersionEncoded)
// addrMeVersion is a version message that contains all fields through
// the AddrMe field.
addrMeVersion := onlyRequiredVersion
addrMeVersion.AddrMe = NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
addrMeVersionEncoded := make([]byte, len(baseVersionEncoded)-29)
copy(addrMeVersionEncoded, baseVersionEncoded)
// nonceVersion is a version message that contains all fields through
// the Nonce field.
nonceVersion := addrMeVersion
nonceVersion.Nonce = 123123 // 0x1e0f3
nonceVersionEncoded := make([]byte, len(baseVersionEncoded)-21)
copy(nonceVersionEncoded, baseVersionEncoded)
// uaVersion is a version message that contains all fields through
// the UserAgent field.
uaVersion := nonceVersion
uaVersion.UserAgent = "/btcdtest:0.0.1/"
uaVersionEncoded := make([]byte, len(baseVersionEncoded)-4)
copy(uaVersionEncoded, baseVersionEncoded)
// lastBlockVersion is a version message that contains all fields
// through the LastBlock field.
lastBlockVersion := uaVersion
lastBlockVersion.LastBlock = 234234 // 0x392fa
lastBlockVersionEncoded := make([]byte, len(baseVersionEncoded))
copy(lastBlockVersionEncoded, baseVersionEncoded)
tests := []struct {
msg *MsgVersion // Expected message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
enc MessageEncoding // Message encoding format
}{
{
&onlyRequiredVersion,
onlyRequiredVersionEncoded,
ProtocolVersion,
BaseEncoding,
},
{
&addrMeVersion,
addrMeVersionEncoded,
ProtocolVersion,
BaseEncoding,
},
{
&nonceVersion,
nonceVersionEncoded,
ProtocolVersion,
BaseEncoding,
},
{
&uaVersion,
uaVersionEncoded,
ProtocolVersion,
BaseEncoding,
},
{
&lastBlockVersion,
lastBlockVersionEncoded,
ProtocolVersion,
BaseEncoding,
},
}
for i, test := range tests {
// Decode the message from wire format.
var msg MsgVersion
rbuf := bytes.NewBuffer(test.buf)
err := msg.BtcDecode(rbuf, test.pver, test.enc)
if err != nil {
t.Errorf("BtcDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.msg) {
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.msg))
continue
}
}
}
// baseVersion is used in the various tests as a baseline MsgVersion.
var baseVersion = &MsgVersion{
ProtocolVersion: 60002,
Services: SFNodeNetwork,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST)
AddrYou: NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8333,
},
AddrMe: NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
},
Nonce: 123123, // 0x1e0f3
UserAgent: "/btcdtest:0.0.1/",
LastBlock: 234234, // 0x392fa
}
// baseVersionEncoded is the wire encoded bytes for baseVersion using protocol
// version 60002 and is used in the various tests.
var baseVersionEncoded = []byte{
0x62, 0xea, 0x00, 0x00, // Protocol version 60002
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x29, 0xab, 0x5f, 0x49, 0x00, 0x00, 0x00, 0x00, // 64-bit Timestamp
// AddrYou -- No timestamp for NetAddress in version message
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1
0x20, 0x8d, // Port 8333 in big-endian
// AddrMe -- No timestamp for NetAddress in version message
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1
0x20, 0x8d, // Port 8333 in big-endian
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Nonce
0x10, // Varint for user agent length
0x2f, 0x62, 0x74, 0x63, 0x64, 0x74, 0x65, 0x73,
0x74, 0x3a, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2f, // User agent
0xfa, 0x92, 0x03, 0x00, // Last block
}
// baseVersionBIP0037 is used in the various tests as a baseline MsgVersion for
// BIP0037.
var baseVersionBIP0037 = &MsgVersion{
ProtocolVersion: 70001,
Services: SFNodeNetwork,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST)
AddrYou: NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8333,
},
AddrMe: NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
},
Nonce: 123123, // 0x1e0f3
UserAgent: "/btcdtest:0.0.1/",
LastBlock: 234234, // 0x392fa
}
// baseVersionBIP0037Encoded is the wire encoded bytes for baseVersionBIP0037
// using protocol version BIP0037Version and is used in the various tests.
var baseVersionBIP0037Encoded = []byte{
0x71, 0x11, 0x01, 0x00, // Protocol version 70001
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x29, 0xab, 0x5f, 0x49, 0x00, 0x00, 0x00, 0x00, // 64-bit Timestamp
// AddrYou -- No timestamp for NetAddress in version message
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1
0x20, 0x8d, // Port 8333 in big-endian
// AddrMe -- No timestamp for NetAddress in version message
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1
0x20, 0x8d, // Port 8333 in big-endian
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Nonce
0x10, // Varint for user agent length
0x2f, 0x62, 0x74, 0x63, 0x64, 0x74, 0x65, 0x73,
0x74, 0x3a, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2f, // User agent
0xfa, 0x92, 0x03, 0x00, // Last block
0x01, // Relay tx
}
+149
View File
@@ -0,0 +1,149 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"encoding/binary"
"io"
"net"
"time"
)
// maxNetAddressPayload returns the max payload size for a bitcoin NetAddress
// based on the protocol version.
func maxNetAddressPayload(pver uint32) uint32 {
// Services 8 bytes + ip 16 bytes + port 2 bytes.
plen := uint32(26)
// NetAddressTimeVersion added a timestamp field.
if pver >= NetAddressTimeVersion {
// Timestamp 4 bytes.
plen += 4
}
return plen
}
// NetAddress defines information about a peer on the network including the time
// it was last seen, the services it supports, its IP address, and port.
type NetAddress struct {
// Last time the address was seen. This is, unfortunately, encoded as a
// uint32 on the wire and therefore is limited to 2106. This field is
// not present in the bitcoin version message (MsgVersion) nor was it
// added until protocol version >= NetAddressTimeVersion.
Timestamp time.Time
// Bitfield which identifies the services supported by the address.
Services ServiceFlag
// IP address of the peer.
IP net.IP
// Port the peer is using. This is encoded in big endian on the wire
// which differs from most everything else.
Port uint16
}
// HasService returns whether the specified service is supported by the address.
func (na *NetAddress) HasService(service ServiceFlag) bool {
return na.Services&service == service
}
// AddService adds service as a supported service by the peer generating the
// message.
func (na *NetAddress) AddService(service ServiceFlag) {
na.Services |= service
}
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
// supported services with defaults for the remaining fields.
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
return NewNetAddressTimestamp(time.Now(), services, ip, port)
}
// NewNetAddressTimestamp returns a new NetAddress using the provided
// timestamp, IP, port, and supported services. The timestamp is rounded to
// single second precision.
func NewNetAddressTimestamp(
timestamp time.Time, services ServiceFlag, ip net.IP, port uint16) *NetAddress {
// Limit the timestamp to one second precision since the protocol
// doesn't support better.
na := NetAddress{
Timestamp: time.Unix(timestamp.Unix(), 0),
Services: services,
IP: ip,
Port: port,
}
return &na
}
// NewNetAddress returns a new NetAddress using the provided TCP address and
// supported services with defaults for the remaining fields.
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
}
// readNetAddress reads an encoded NetAddress from r depending on the protocol
// version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp.
func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error {
var ip [16]byte
// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
// stop working somewhere around 2106. Also timestamp wasn't added until
// protocol version >= NetAddressTimeVersion
if ts && pver >= NetAddressTimeVersion {
err := readElement(r, (*uint32Time)(&na.Timestamp))
if err != nil {
return err
}
}
err := readElements(r, &na.Services, &ip)
if err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
port, err := binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
*na = NetAddress{
Timestamp: na.Timestamp,
Services: na.Services,
IP: net.IP(ip[:]),
Port: port,
}
return nil
}
// writeNetAddress serializes a NetAddress to w depending on the protocol
// version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp.
func writeNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error {
// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
// stop working somewhere around 2106. Also timestamp wasn't added until
// until protocol version >= NetAddressTimeVersion.
if ts && pver >= NetAddressTimeVersion {
err := writeElement(w, uint32(na.Timestamp.Unix()))
if err != nil {
return err
}
}
// Ensure to always write 16 bytes even if the ip is nil.
var ip [16]byte
if na.IP != nil {
copy(ip[:], na.IP.To16())
}
err := writeElements(w, na.Services, ip)
if err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
return binary.Write(w, bigEndian, na.Port)
}
+278
View File
@@ -0,0 +1,278 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io"
"net"
"reflect"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
)
// TestNetAddress tests the NetAddress API.
func TestNetAddress(t *testing.T) {
ip := net.ParseIP("127.0.0.1")
port := 8333
// Test NewNetAddress.
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port}, 0)
// Ensure we get the same ip, port, and services back out.
if !na.IP.Equal(ip) {
t.Errorf("NetNetAddress: wrong ip - got %v, want %v", na.IP, ip)
}
if na.Port != uint16(port) {
t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port,
port)
}
if na.Services != 0 {
t.Errorf("NetNetAddress: wrong services - got %v, want %v",
na.Services, 0)
}
if na.HasService(SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service is set")
}
// Ensure adding the full service node flag works.
na.AddService(SFNodeNetwork)
if na.Services != SFNodeNetwork {
t.Errorf("AddService: wrong services - got %v, want %v",
na.Services, SFNodeNetwork)
}
if !na.HasService(SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service not set")
}
// Ensure max payload is expected value for latest protocol version.
pver := ProtocolVersion
wantPayload := uint32(30)
maxPayload := maxNetAddressPayload(ProtocolVersion)
if maxPayload != wantPayload {
t.Errorf("maxNetAddressPayload: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
// Protocol version before NetAddressTimeVersion when timestamp was
// added. Ensure max payload is expected value for it.
pver = NetAddressTimeVersion - 1
wantPayload = 26
maxPayload = maxNetAddressPayload(pver)
if maxPayload != wantPayload {
t.Errorf("maxNetAddressPayload: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
}
// TestNetAddressWire tests the NetAddress wire encode and decode for various
// protocol versions and timestamp flag combinations.
func TestNetAddressWire(t *testing.T) {
// baseNetAddr is used in the various tests as a baseline NetAddress.
baseNetAddr := NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
// baseNetAddrNoTS is baseNetAddr with a zero value for the timestamp.
baseNetAddrNoTS := baseNetAddr
baseNetAddrNoTS.Timestamp = time.Time{}
// baseNetAddrEncoded is the wire encoded bytes of baseNetAddr.
baseNetAddrEncoded := []byte{
0x29, 0xab, 0x5f, 0x49, // Timestamp
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1
0x20, 0x8d, // Port 8333 in big-endian
}
// baseNetAddrNoTSEncoded is the wire encoded bytes of baseNetAddrNoTS.
baseNetAddrNoTSEncoded := []byte{
// No timestamp
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1
0x20, 0x8d, // Port 8333 in big-endian
}
tests := []struct {
in NetAddress // NetAddress to encode
out NetAddress // Expected decoded NetAddress
ts bool // Include timestamp?
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version without ts flag.
{
baseNetAddr,
baseNetAddrNoTS,
false,
baseNetAddrNoTSEncoded,
ProtocolVersion,
},
// Latest protocol version with ts flag.
{
baseNetAddr,
baseNetAddr,
true,
baseNetAddrEncoded,
ProtocolVersion,
},
// Protocol version NetAddressTimeVersion without ts flag.
{
baseNetAddr,
baseNetAddrNoTS,
false,
baseNetAddrNoTSEncoded,
NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with ts flag.
{
baseNetAddr,
baseNetAddr,
true,
baseNetAddrEncoded,
NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion-1 without ts flag.
{
baseNetAddr,
baseNetAddrNoTS,
false,
baseNetAddrNoTSEncoded,
NetAddressTimeVersion - 1,
},
// Protocol version NetAddressTimeVersion-1 with timestamp.
// Even though the timestamp flag is set, this shouldn't have a
// timestamp since it is a protocol version before it was
// added.
{
baseNetAddr,
baseNetAddrNoTS,
true,
baseNetAddrNoTSEncoded,
NetAddressTimeVersion - 1,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := writeNetAddress(&buf, test.pver, &test.in, test.ts)
if err != nil {
t.Errorf("writeNetAddress #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("writeNetAddress #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var na NetAddress
rbuf := bytes.NewReader(test.buf)
err = readNetAddress(rbuf, test.pver, &na, test.ts)
if err != nil {
t.Errorf("readNetAddress #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(na, test.out) {
t.Errorf("readNetAddress #%d\n got: %s want: %s", i,
spew.Sdump(na), spew.Sdump(test.out))
continue
}
}
}
// TestNetAddressWireErrors performs negative tests against wire encode and
// decode NetAddress to confirm error paths work correctly.
func TestNetAddressWireErrors(t *testing.T) {
pver := ProtocolVersion
pverNAT := NetAddressTimeVersion - 1
// baseNetAddr is used in the various tests as a baseline NetAddress.
baseNetAddr := NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
tests := []struct {
in *NetAddress // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
ts bool // Include timestamp flag
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Latest protocol version with timestamp and intentional
// read/write errors.
// Force errors on timestamp.
{&baseNetAddr, []byte{}, pver, true, 0, io.ErrShortWrite, io.EOF},
// Force errors on services.
{&baseNetAddr, []byte{}, pver, true, 4, io.ErrShortWrite, io.EOF},
// Force errors on ip.
{&baseNetAddr, []byte{}, pver, true, 12, io.ErrShortWrite, io.EOF},
// Force errors on port.
{&baseNetAddr, []byte{}, pver, true, 28, io.ErrShortWrite, io.EOF},
// Latest protocol version with no timestamp and intentional
// read/write errors.
// Force errors on services.
{&baseNetAddr, []byte{}, pver, false, 0, io.ErrShortWrite, io.EOF},
// Force errors on ip.
{&baseNetAddr, []byte{}, pver, false, 8, io.ErrShortWrite, io.EOF},
// Force errors on port.
{&baseNetAddr, []byte{}, pver, false, 24, io.ErrShortWrite, io.EOF},
// Protocol version before NetAddressTimeVersion with timestamp
// flag set (should not have timestamp due to old protocol
// version) and intentional read/write errors.
// Force errors on services.
{&baseNetAddr, []byte{}, pverNAT, true, 0, io.ErrShortWrite, io.EOF},
// Force errors on ip.
{&baseNetAddr, []byte{}, pverNAT, true, 8, io.ErrShortWrite, io.EOF},
// Force errors on port.
{&baseNetAddr, []byte{}, pverNAT, true, 24, io.ErrShortWrite, io.EOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
w := newFixedWriter(test.max)
err := writeNetAddress(w, test.pver, test.in, test.ts)
if err != test.writeErr {
t.Errorf("writeNetAddress #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// Decode from wire format.
var na NetAddress
r := newFixedReader(test.max, test.buf)
err = readNetAddress(r, test.pver, &na, test.ts)
if err != test.readErr {
t.Errorf("readNetAddress #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
+154
View File
@@ -0,0 +1,154 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"strconv"
"strings"
)
const (
// ProtocolVersion is the latest protocol version this package supports.
ProtocolVersion uint32 = 70013
// MultipleAddressVersion is the protocol version which added multiple
// addresses per message (pver >= MultipleAddressVersion).
MultipleAddressVersion uint32 = 209
// NetAddressTimeVersion is the protocol version which added the
// timestamp field (pver >= NetAddressTimeVersion).
NetAddressTimeVersion uint32 = 31402
// BIP0031Version is the protocol version AFTER which a pong message
// and nonce field in ping were added (pver > BIP0031Version).
BIP0031Version uint32 = 60000
// BIP0035Version is the protocol version which added the mempool
// message (pver >= BIP0035Version).
BIP0035Version uint32 = 60002
// BIP0037Version is the protocol version which added new connection
// bloom filtering related messages and extended the version message
// with a relay flag (pver >= BIP0037Version).
BIP0037Version uint32 = 70001
// RejectVersion is the protocol version which added a new reject
// message.
RejectVersion uint32 = 70002
// BIP0111Version is the protocol version which added the SFNodeBloom
// service flag.
BIP0111Version uint32 = 70011
// SendHeadersVersion is the protocol version which added a new
// sendheaders message.
SendHeadersVersion uint32 = 70012
// FeeFilterVersion is the protocol version which added a new
// feefilter message.
FeeFilterVersion uint32 = 70013
)
// ServiceFlag identifies services supported by a bitcoin peer.
type ServiceFlag uint64
const (
// SFNodeNetwork is a flag used to indicate a peer is a full node.
SFNodeNetwork ServiceFlag = 1 << iota
// SFNodeGetUTXO is a flag used to indicate a peer supports the
// getutxos and utxos commands (BIP0064).
SFNodeGetUTXO
// SFNodeBloom is a flag used to indicate a peer supports bloom
// filtering.
SFNodeBloom
// SFNodeWitness is a flag used to indicate a peer supports blocks
// and transactions including witness data (BIP0144).
SFNodeWitness
)
// Map of service flags back to their constant names for pretty printing.
var sfStrings = map[ServiceFlag]string{
SFNodeNetwork: "SFNodeNetwork",
SFNodeGetUTXO: "SFNodeGetUTXO",
SFNodeBloom: "SFNodeBloom",
SFNodeWitness: "SFNodeWitness",
}
// orderedSFStrings is an ordered list of service flags from highest to
// lowest.
var orderedSFStrings = []ServiceFlag{
SFNodeNetwork,
SFNodeGetUTXO,
SFNodeBloom,
SFNodeWitness,
}
// String returns the ServiceFlag in human-readable form.
func (f ServiceFlag) String() string {
// No flags are set.
if f == 0 {
return "0x0"
}
// Add individual bit flags.
s := ""
for _, flag := range orderedSFStrings {
if f&flag == flag {
s += sfStrings[flag] + "|"
f -= flag
}
}
// Add any remaining flags which aren't accounted for as hex.
s = strings.TrimRight(s, "|")
if f != 0 {
s += "|0x" + strconv.FormatUint(uint64(f), 16)
}
s = strings.TrimLeft(s, "|")
return s
}
// BitcoinNet represents which bitcoin network a message belongs to.
type BitcoinNet uint32
// Constants used to indicate the message bitcoin network. They can also be
// used to seek to the next message when a stream's state is unknown, but
// this package does not provide that functionality since it's generally a
// better idea to simply disconnect clients that are misbehaving over TCP.
const (
// MainNet represents the main bitcoin network.
MainNet BitcoinNet = 0xd9b4bef9
// TestNet represents the regression test network.
TestNet BitcoinNet = 0xdab5bffa
// TestNet3 represents the test network (version 3).
TestNet3 BitcoinNet = 0x0709110b
// SimNet represents the simulation test network.
SimNet BitcoinNet = 0x12141c16
)
// bnStrings is a map of bitcoin networks back to their constant names for
// pretty printing.
var bnStrings = map[BitcoinNet]string{
MainNet: "MainNet",
TestNet: "TestNet",
TestNet3: "TestNet3",
SimNet: "SimNet",
}
// String returns the BitcoinNet in human-readable form.
func (n BitcoinNet) String() string {
if s, ok := bnStrings[n]; ok {
return s
}
return fmt.Sprintf("Unknown BitcoinNet (%d)", uint32(n))
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import "testing"
// TestServiceFlagStringer tests the stringized output for service flag types.
func TestServiceFlagStringer(t *testing.T) {
tests := []struct {
in ServiceFlag
want string
}{
{0, "0x0"},
{SFNodeNetwork, "SFNodeNetwork"},
{SFNodeGetUTXO, "SFNodeGetUTXO"},
{SFNodeBloom, "SFNodeBloom"},
{SFNodeWitness, "SFNodeWitness"},
{0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|SFNodeBloom|SFNodeWitness|0xfffffff0"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.String()
if result != test.want {
t.Errorf("String #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
// TestBitcoinNetStringer tests the stringized output for bitcoin net types.
func TestBitcoinNetStringer(t *testing.T) {
tests := []struct {
in BitcoinNet
want string
}{
{MainNet, "MainNet"},
{TestNet, "TestNet"},
{TestNet3, "TestNet3"},
{SimNet, "SimNet"},
{0xffffffff, "Unknown BitcoinNet (4294967295)"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.String()
if result != test.want {
t.Errorf("String #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
Binary file not shown.