Merge btcwire repo into wire directory.

This commit is contained in:
Dave Collins 2015-02-05 12:39:50 -06:00
commit cf6fc57f27
64 changed files with 15191 additions and 0 deletions

125
wire/README.md Normal file
View file

@ -0,0 +1,125 @@
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)
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.
## Documentation
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
(http://godoc.org/github.com/btcsuite/btcd/wire)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/btcsuite/btcd/wire
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/btcsuite/btcd/wire
## Installation
```bash
$ go get 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 Conformal. 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.

394
wire/bench_test.go Normal file
View file

@ -0,0 +1,394 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"io/ioutil"
"testing"
"time"
)
// 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: ShaHash{},
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,
}
// blockOne is the first block in the mainnet block chain.
var blockOne = MsgBlock{
Header: BlockHeader{
Version: 1,
PrevBlock: ShaHash([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: ShaHash([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: ShaHash{},
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,
},
},
}
// 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}
for i := 0; i < b.N; i++ {
readVarInt(bytes.NewReader(buf), 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}
for i := 0; i < b.N; i++ {
readVarInt(bytes.NewReader(buf), 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}
for i := 0; i < b.N; i++ {
readVarInt(bytes.NewReader(buf), 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}
for i := 0; i < b.N; i++ {
readVarInt(bytes.NewReader(buf), 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'}
for i := 0; i < b.N; i++ {
readVarString(bytes.NewReader(buf), 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'}
for i := 0; i < b.N; i++ {
readVarString(bytes.NewReader(buf), 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
}
var op OutPoint
for i := 0; i < b.N; i++ {
readOutPoint(bytes.NewReader(buf), 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: ShaHash{},
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
}
var txOut TxOut
for i := 0; i < b.N; i++ {
readTxOut(bytes.NewReader(buf), 0, 0, &txOut)
}
}
// 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
}
var txIn TxIn
for i := 0; i < b.N; i++ {
readTxIn(bytes.NewReader(buf), 0, 0, &txIn)
}
}
// 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 transaction.
func BenchmarkDeserializeTx(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
}
var tx MsgTx
for i := 0; i < b.N; i++ {
tx.Deserialize(bytes.NewReader(buf))
}
}
// 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
}
var header BlockHeader
for i := 0; i < b.N; i++ {
readBlockHeader(bytes.NewReader(buf), 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)
}
}
// BenchmarkTxSha performs a benchmark on how long it takes to hash a
// transaction.
func BenchmarkTxSha(b *testing.B) {
for i := 0; i < b.N; i++ {
genesisCoinbaseTx.TxSha()
}
}

131
wire/blockheader.go Normal file
View file

@ -0,0 +1,131 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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"
)
// BlockVersion is the current latest supported block version.
const BlockVersion = 2
// Version 4 bytes + Timestamp 4 bytes + Bits 4 bytes + Nonce 4 bytes +
// PrevBlock and MerkleRoot hashes.
const MaxBlockHeaderPayload = 16 + (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 ShaHash
// Merkle tree reference to hash of all transactions for the block.
MerkleRoot ShaHash
// 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
// BlockSha computes the block identifier hash for the given block header.
func (h *BlockHeader) BlockSha() (ShaHash, error) {
// Encode the header and run 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. Also, SetBytes can't fail here due to the
// fact DoubleSha256 always returns a []byte of the right size
// regardless of input.
var buf bytes.Buffer
var sha ShaHash
_ = writeBlockHeader(&buf, 0, h)
_ = sha.SetBytes(DoubleSha256(buf.Bytes()[0:blockHeaderLen]))
// Even though this function can't currently fail, it still returns
// a potential error to help future proof the API should a failure
// become possible.
return sha, nil
}
// 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 previous block
// hash, merkle root hash, difficulty bits, and nonce used to generate the
// block with defaults for the remaining fields.
func NewBlockHeader(prevHash *ShaHash, merkleRootHash *ShaHash, bits uint32,
nonce uint32) *BlockHeader {
// Limit the timestamp to one second precision since the protocol
// doesn't support better.
return &BlockHeader{
Version: BlockVersion,
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 {
var sec uint32
err := readElements(r, &bh.Version, &bh.PrevBlock, &bh.MerkleRoot, &sec,
&bh.Bits, &bh.Nonce)
if err != nil {
return err
}
bh.Timestamp = time.Unix(int64(sec), 0)
return nil
}
// 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())
err := writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
sec, bh.Bits, bh.Nonce)
if err != nil {
return err
}
return nil
}

231
wire/blockheader_test.go Normal file
View file

@ -0,0 +1,231 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestBlockHeader tests the BlockHeader API.
func TestBlockHeader(t *testing.T) {
nonce64, err := wire.RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
nonce := uint32(nonce64)
hash := mainNetGenesisHash
merkleHash := mainNetGenesisMerkleRoot
bits := uint32(0x1d00ffff)
bh := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce)
// Ensure we get the same data back out.
if !bh.PrevBlock.IsEqual(&hash) {
t.Errorf("NewBlockHeader: wrong prev hash - got %v, want %v",
spew.Sprint(bh.PrevBlock), spew.Sprint(hash))
}
if !bh.MerkleRoot.IsEqual(&merkleHash) {
t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v",
spew.Sprint(bh.MerkleRoot), spew.Sprint(merkleHash))
}
if bh.Bits != bits {
t.Errorf("NewBlockHeader: wrong bits - got %v, want %v",
bh.Bits, bits)
}
if bh.Nonce != nonce {
t.Errorf("NewBlockHeader: wrong nonce - got %v, want %v",
bh.Nonce, nonce)
}
}
// TestBlockHeaderWire tests the BlockHeader wire encode and decode for various
// protocol versions.
func TestBlockHeaderWire(t *testing.T) {
nonce := uint32(123123) // 0x1e0f3
// baseBlockHdr is used in the various tests as a baseline BlockHeader.
bits := uint32(0x1d00ffff)
baseBlockHdr := &wire.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 *wire.BlockHeader // Data to encode
out *wire.BlockHeader // Expected decoded data
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion.
{
baseBlockHdr,
baseBlockHdr,
baseBlockHdrEncoded,
wire.MultipleAddressVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := wire.TstWriteBlockHeader(&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
}
// Decode the block header from wire format.
var bh wire.BlockHeader
rbuf := bytes.NewReader(test.buf)
err = wire.TstReadBlockHeader(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
}
}
}
// 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 := &wire.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 *wire.BlockHeader // Data to encode
out *wire.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 wire.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
}
}
}

531
wire/common.go Normal file
View file

@ -0,0 +1,531 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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"
"github.com/btcsuite/fastsha256"
)
// Maximum payload size for a variable length integer.
const MaxVarIntPayload = 9
// 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 {
var scratch [8]byte
// Attempt to read the element based on the concrete type via fast
// type assertions first.
switch e := element.(type) {
case *int32:
b := scratch[0:4]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = int32(binary.LittleEndian.Uint32(b))
return nil
case *uint32:
b := scratch[0:4]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = binary.LittleEndian.Uint32(b)
return nil
case *int64:
b := scratch[0:8]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = int64(binary.LittleEndian.Uint64(b))
return nil
case *uint64:
b := scratch[0:8]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = binary.LittleEndian.Uint64(b)
return nil
case *bool:
b := scratch[0:1]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
if b[0] == 0x00 {
*e = false
} else {
*e = true
}
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 *ShaHash:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil
case *ServiceFlag:
b := scratch[0:8]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = ServiceFlag(binary.LittleEndian.Uint64(b))
return nil
case *InvType:
b := scratch[0:4]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = InvType(binary.LittleEndian.Uint32(b))
return nil
case *BitcoinNet:
b := scratch[0:4]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = BitcoinNet(binary.LittleEndian.Uint32(b))
return nil
case *BloomUpdateType:
b := scratch[0:1]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = BloomUpdateType(b[0])
return nil
case *RejectCode:
b := scratch[0:1]
_, err := io.ReadFull(r, b)
if err != nil {
return err
}
*e = RejectCode(b[0])
return nil
}
// Fall back to the slower binary.Read if a fast path was not available
// above.
return binary.Read(r, binary.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 {
var scratch [8]byte
// Attempt to write the element based on the concrete type via fast
// type assertions first.
switch e := element.(type) {
case int32:
b := scratch[0:4]
binary.LittleEndian.PutUint32(b, uint32(e))
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case uint32:
b := scratch[0:4]
binary.LittleEndian.PutUint32(b, e)
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case int64:
b := scratch[0:8]
binary.LittleEndian.PutUint64(b, uint64(e))
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case uint64:
b := scratch[0:8]
binary.LittleEndian.PutUint64(b, e)
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case bool:
b := scratch[0:1]
if e == true {
b[0] = 0x01
} else {
b[0] = 0x00
}
_, err := w.Write(b)
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 *ShaHash:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil
case ServiceFlag:
b := scratch[0:8]
binary.LittleEndian.PutUint64(b, uint64(e))
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case InvType:
b := scratch[0:4]
binary.LittleEndian.PutUint32(b, uint32(e))
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case BitcoinNet:
b := scratch[0:4]
binary.LittleEndian.PutUint32(b, uint32(e))
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case BloomUpdateType:
b := scratch[0:1]
b[0] = uint8(e)
_, err := w.Write(b)
if err != nil {
return err
}
return nil
case RejectCode:
b := scratch[0:1]
b[0] = uint8(e)
_, err := w.Write(b)
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, binary.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) {
var b [8]byte
_, err := io.ReadFull(r, b[0:1])
if err != nil {
return 0, err
}
var rv uint64
discriminant := uint8(b[0])
switch discriminant {
case 0xff:
_, err := io.ReadFull(r, b[:])
if err != nil {
return 0, err
}
rv = binary.LittleEndian.Uint64(b[:])
case 0xfe:
_, err := io.ReadFull(r, b[0:4])
if err != nil {
return 0, err
}
rv = uint64(binary.LittleEndian.Uint32(b[:]))
case 0xfd:
_, err := io.ReadFull(r, b[0:2])
if err != nil {
return 0, err
}
rv = uint64(binary.LittleEndian.Uint16(b[:]))
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 {
_, err := w.Write([]byte{uint8(val)})
return err
}
if val <= math.MaxUint16 {
var buf [3]byte
buf[0] = 0xfd
binary.LittleEndian.PutUint16(buf[1:], uint16(val))
_, err := w.Write(buf[:])
return err
}
if val <= math.MaxUint32 {
var buf [5]byte
buf[0] = 0xfe
binary.LittleEndian.PutUint32(buf[1:], uint32(val))
_, err := w.Write(buf[:])
return err
}
var buf [9]byte
buf[0] = 0xff
binary.LittleEndian.PutUint64(buf[1:], val)
_, err := w.Write(buf[:])
return err
}
// 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 varString is encoded as a varInt containing the length of the
// string, and the bytes that represent the string itself. An error is returned
// if the length is greater than the maximum block payload size, since it would
// not be possible to put a varString of that size into a block anyways and it
// also 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 varInt 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))
if err != nil {
return err
}
return nil
}
// 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
}
// writeVarInt 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)
if err != nil {
return err
}
return nil
}
// 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) {
var b [8]byte
_, err := io.ReadFull(r, b[:])
if err != nil {
return 0, err
}
return binary.BigEndian.Uint64(b[:]), nil
}
// RandomUint64 returns a cryptographically random uint64 value.
func RandomUint64() (uint64, error) {
return randomUint64(rand.Reader)
}
// DoubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes.
func DoubleSha256(b []byte) []byte {
hasher := fastsha256.New()
hasher.Write(b)
sum := hasher.Sum(nil)
hasher.Reset()
hasher.Write(sum)
return hasher.Sum(nil)
}

703
wire/common_test.go Normal file
View file

@ -0,0 +1,703 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"fmt"
"io"
"reflect"
"strings"
"testing"
"github.com/btcsuite/btcd/wire"
"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 = wire.ShaHash([wire.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 = wire.ShaHash([wire.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},
},
{
[wire.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,
},
},
{
(*wire.ShaHash)(&[wire.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,
},
},
{
wire.ServiceFlag(wire.SFNodeNetwork),
[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
{
wire.InvType(wire.InvTypeTx),
[]byte{0x01, 0x00, 0x00, 0x00},
},
{
wire.BitcoinNet(wire.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 := wire.TstWriteElement(&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 = wire.TstReadElement(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},
{
[wire.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,
},
{
(*wire.ShaHash)(&[wire.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,
},
{wire.ServiceFlag(wire.SFNodeNetwork), 0, io.ErrShortWrite, io.EOF},
{wire.InvType(wire.InvTypeTx), 0, io.ErrShortWrite, io.EOF},
{wire.BitcoinNet(wire.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 := wire.TstWriteElement(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 = wire.TstReadElement(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 := wire.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 := wire.TstWriteVarInt(&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 := wire.TstReadVarInt(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 := wire.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 := wire.TstWriteVarInt(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 = wire.TstReadVarInt(r, test.pver)
if err != test.readErr {
t.Errorf("readVarInt #%d wrong error got: %v, want: %v",
i, err, test.readErr)
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 := wire.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 := wire.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 := wire.TstWriteVarString(&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 := wire.TstReadVarString(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 := wire.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 := wire.TstWriteVarString(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 = wire.TstReadVarString(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 := wire.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, &wire.MessageError{}},
{[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
pver, &wire.MessageError{}},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
_, err := wire.TstReadVarString(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 := wire.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 := wire.TstWriteVarBytes(&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 := wire.TstReadVarBytes(rbuf, test.pver,
wire.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 := wire.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 := wire.TstWriteVarBytes(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 = wire.TstReadVarBytes(r, test.pver,
wire.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 := wire.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, &wire.MessageError{}},
{[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
pver, &wire.MessageError{}},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
rbuf := bytes.NewReader(test.buf)
_, err := wire.TstReadVarBytes(rbuf, test.pver,
wire.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 := wire.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 := wire.TstRandomUint64(fr)
if err != io.ErrUnexpectedEOF {
t.Errorf("TestRandomUint64Fails: Error not expected value of %v [%v]",
io.ErrShortBuffer, err)
}
if nonce != 0 {
t.Errorf("TestRandomUint64Fails: nonce is not 0 [%v]", nonce)
}
}

159
wire/doc.go Normal file
View file

@ -0,0 +1,159 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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://en.bitcoin.it/wiki/BIP_0014)
BIP0031 (https://en.bitcoin.it/wiki/BIP_0031)
BIP0035 (https://en.bitcoin.it/wiki/BIP_0035)
BIP0037 (https://en.bitcoin.it/wiki/BIP_0037)
*/
package wire

34
wire/error.go Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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}
}

62
wire/fakeconn_test.go Normal file
View file

@ -0,0 +1,62 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"net"
"time"
)
// fakeConn implements the net.Conn interface and is used to test functions
// which work with a net.Conn without having to actually make any real
// connections.
type fakeConn struct {
localAddr net.Addr
remoteAddr net.Addr
}
// Read doesn't do anything. It just satisfies the net.Conn interface.
func (c *fakeConn) Read(b []byte) (n int, err error) {
return 0, nil
}
// Write doesn't do anything. It just satisfies the net.Conn interface.
func (c *fakeConn) Write(b []byte) (n int, err error) {
return 0, nil
}
// Close doesn't do anything. It just satisfies the net.Conn interface.
func (c *fakeConn) Close() error {
return nil
}
// LocalAddr returns the localAddr field of the fake connection and satisfies
// the net.Conn interface.
func (c *fakeConn) LocalAddr() net.Addr {
return c.localAddr
}
// RemoteAddr returns the remoteAddr field of the fake connection and satisfies
// the net.Conn interface.
func (c *fakeConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
// SetDeadline doesn't do anything. It just satisfies the net.Conn interface.
func (c *fakeConn) SetDeadline(t time.Time) error {
return nil
}
// SetReadDeadline doesn't do anything. It just satisfies the net.Conn
// interface.
func (c *fakeConn) SetReadDeadline(t time.Time) error {
return nil
}
// SetWriteDeadline doesn't do anything. It just satisfies the net.Conn
// interface.
func (c *fakeConn) SetWriteDeadline(t time.Time) error {
return nil
}

60
wire/fakemessage_test.go Normal file
View file

@ -0,0 +1,60 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"io"
"github.com/btcsuite/btcd/wire"
)
// fakeMessage implements the wire.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) 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) error {
if msg.forceEncodeErr {
err := &wire.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
// wire.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 wire.Message interface.
func (msg *fakeMessage) MaxPayloadLength(pver uint32) uint32 {
lenp := uint32(len(msg.payload))
if msg.forceLenErr {
return lenp - 1
}
return lenp
}

77
wire/fixedIO_test.go Normal file
View file

@ -0,0 +1,77 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
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 alreayd 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, 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, max)
if buf != nil {
copy(b[:], buf)
}
iobuf := bytes.NewBuffer(b)
fr := fixedReader{b, 0, iobuf}
return &fr
}

130
wire/internal_test.go Normal file
View file

@ -0,0 +1,130 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
This test file is part of the wire package rather than than the wire_test
package so it can bridge access to the internals to properly test cases which
are either not possible or can't reliably be tested via the public interface.
The functions are only exported while the tests are being run.
*/
package wire
import (
"io"
)
const (
// MaxTxPerBlock makes the internal maxTxPerBlock constant available to
// the test package.
MaxTxPerBlock = maxTxPerBlock
// MaxFlagsPerMerkleBlock makes the internal maxFlagsPerMerkleBlock
// constant available to the test package.
MaxFlagsPerMerkleBlock = maxFlagsPerMerkleBlock
// MaxCountSetCancel makes the internal maxCountSetCancel constant
// available to the test package.
MaxCountSetCancel = maxCountSetCancel
// MaxCountSetSubVer makes the internal maxCountSetSubVer constant
// available to the test package.
MaxCountSetSubVer = maxCountSetSubVer
)
// TstRandomUint64 makes the internal randomUint64 function available to the
// test package.
func TstRandomUint64(r io.Reader) (uint64, error) {
return randomUint64(r)
}
// TstReadElement makes the internal readElement function available to the
// test package.
func TstReadElement(r io.Reader, element interface{}) error {
return readElement(r, element)
}
// TstWriteElement makes the internal writeElement function available to the
// test package.
func TstWriteElement(w io.Writer, element interface{}) error {
return writeElement(w, element)
}
// TstReadVarInt makes the internal readVarInt function available to the
// test package.
func TstReadVarInt(r io.Reader, pver uint32) (uint64, error) {
return readVarInt(r, pver)
}
// TstWriteVarInt makes the internal writeVarInt function available to the
// test package.
func TstWriteVarInt(w io.Writer, pver uint32, val uint64) error {
return writeVarInt(w, pver, val)
}
// TstReadVarString makes the internal readVarString function available to the
// test package.
func TstReadVarString(r io.Reader, pver uint32) (string, error) {
return readVarString(r, pver)
}
// TstWriteVarString makes the internal writeVarString function available to the
// test package.
func TstWriteVarString(w io.Writer, pver uint32, str string) error {
return writeVarString(w, pver, str)
}
// TstReadVarBytes makes the internal readVarBytes function available to the
// test package.
func TstReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error) {
return readVarBytes(r, pver, maxAllowed, fieldName)
}
// TstWriteVarBytes makes the internal writeVarBytes function available to the
// test package.
func TstWriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
return writeVarBytes(w, pver, bytes)
}
// TstReadNetAddress makes the internal readNetAddress function available to
// the test package.
func TstReadNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error {
return readNetAddress(r, pver, na, ts)
}
// TstWriteNetAddress makes the internal writeNetAddress function available to
// the test package.
func TstWriteNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error {
return writeNetAddress(w, pver, na, ts)
}
// TstMaxNetAddressPayload makes the internal maxNetAddressPayload function
// available to the test package.
func TstMaxNetAddressPayload(pver uint32) uint32 {
return maxNetAddressPayload(pver)
}
// TstReadInvVect makes the internal readInvVect function available to the test
// package.
func TstReadInvVect(r io.Reader, pver uint32, iv *InvVect) error {
return readInvVect(r, pver, iv)
}
// TstWriteInvVect makes the internal writeInvVect function available to the
// test package.
func TstWriteInvVect(w io.Writer, pver uint32, iv *InvVect) error {
return writeInvVect(w, pver, iv)
}
// TstReadBlockHeader makes the internal readBlockHeader function available to
// the test package.
func TstReadBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
return readBlockHeader(r, pver, bh)
}
// TstWriteBlockHeader makes the internal writeBlockHeader function available to
// the test package.
func TstWriteBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
return writeBlockHeader(w, pver, bh)
}

82
wire/invvect.go Normal file
View file

@ -0,0 +1,82 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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 (
// 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 + HashSize
)
// 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
)
// 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",
}
// 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 ShaHash // Hash of the data
}
// NewInvVect returns a new InvVect using the provided type and hash.
func NewInvVect(typ InvType, hash *ShaHash) *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 {
err := readElements(r, &iv.Type, &iv.Hash)
if err != nil {
return err
}
return nil
}
// writeInvVect serializes an InvVect to w depending on the protocol version.
func writeInvVect(w io.Writer, pver uint32, iv *InvVect) error {
err := writeElements(w, iv.Type, &iv.Hash)
if err != nil {
return err
}
return nil
}

269
wire/invvect_test.go Normal file
View file

@ -0,0 +1,269 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestInvVectStringer tests the stringized output for inventory vector types.
func TestInvTypeStringer(t *testing.T) {
tests := []struct {
in wire.InvType
want string
}{
{wire.InvTypeError, "ERROR"},
{wire.InvTypeTx, "MSG_TX"},
{wire.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 := wire.InvTypeBlock
hash := wire.ShaHash{}
// Ensure we get the same payload and signature back out.
iv := wire.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 := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// errInvVect is an inventory vector with an error.
errInvVect := wire.InvVect{
Type: wire.InvTypeError,
Hash: wire.ShaHash{},
}
// 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 := wire.InvVect{
Type: wire.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 := wire.InvVect{
Type: wire.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 wire.InvVect // NetAddress to encode
out wire.InvVect // Expected decoded NetAddress
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
wire.ProtocolVersion,
},
// Latest protocol version tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
wire.ProtocolVersion,
},
// Latest protocol version block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion error inventory vector.
{
errInvVect,
errInvVect,
errInvVectEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion tx inventory vector.
{
txInvVect,
txInvVect,
txInvVectEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion block inventory vector.
{
blockInvVect,
blockInvVect,
blockInvVectEncoded,
wire.MultipleAddressVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := wire.TstWriteInvVect(&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 wire.InvVect
rbuf := bytes.NewReader(test.buf)
err = wire.TstReadInvVect(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
}
}
}

369
wire/message.go Normal file
View file

@ -0,0 +1,369 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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"
)
// 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"
)
// 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) error
BtcEncode(io.Writer, uint32) 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{}
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) {
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)
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[:], DoubleSha256(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())
if err != nil {
totalBytes += n
return totalBytes, err
}
totalBytes += n
// Write payload.
n, err = w.Write(payload)
if err != nil {
totalBytes += n
return totalBytes, err
}
totalBytes += n
return totalBytes, nil
}
// 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
}
// 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) {
totalBytes := 0
n, hdr, err := readMessageHeader(r)
if err != nil {
totalBytes += n
return totalBytes, nil, nil, err
}
totalBytes += n
// 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)
if err != nil {
totalBytes += n
return totalBytes, nil, nil, err
}
totalBytes += n
// Test checksum.
checksum := DoubleSha256(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)
if err != nil {
return totalBytes, nil, nil, err
}
return totalBytes, msg, payload, nil
}
// 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
}

452
wire/message_test.go Normal file
View file

@ -0,0 +1,452 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"encoding/binary"
"io"
"net"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"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 wire.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 := wire.ProtocolVersion
// Create the various types of messages to test.
// MsgVersion.
addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}
you, err := wire.NewNetAddress(addrYou, wire.SFNodeNetwork)
if err != nil {
t.Errorf("NewNetAddress: %v", err)
}
you.Timestamp = time.Time{} // Version message has zero value timestamp.
addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
me, err := wire.NewNetAddress(addrMe, wire.SFNodeNetwork)
if err != nil {
t.Errorf("NewNetAddress: %v", err)
}
me.Timestamp = time.Time{} // Version message has zero value timestamp.
msgVersion := wire.NewMsgVersion(me, you, 123123, 0)
msgVerack := wire.NewMsgVerAck()
msgGetAddr := wire.NewMsgGetAddr()
msgAddr := wire.NewMsgAddr()
msgGetBlocks := wire.NewMsgGetBlocks(&wire.ShaHash{})
msgBlock := &blockOne
msgInv := wire.NewMsgInv()
msgGetData := wire.NewMsgGetData()
msgNotFound := wire.NewMsgNotFound()
msgTx := wire.NewMsgTx()
msgPing := wire.NewMsgPing(123123)
msgPong := wire.NewMsgPong(123123)
msgGetHeaders := wire.NewMsgGetHeaders()
msgHeaders := wire.NewMsgHeaders()
msgAlert := wire.NewMsgAlert([]byte("payload"), []byte("signature"))
msgMemPool := wire.NewMsgMemPool()
msgFilterAdd := wire.NewMsgFilterAdd([]byte{0x01})
msgFilterClear := wire.NewMsgFilterClear()
msgFilterLoad := wire.NewMsgFilterLoad([]byte{0x01}, 10, 0, wire.BloomUpdateNone)
bh := wire.NewBlockHeader(&wire.ShaHash{}, &wire.ShaHash{}, 0, 0)
msgMerkleBlock := wire.NewMsgMerkleBlock(bh)
msgReject := wire.NewMsgReject("block", wire.RejectDuplicate, "duplicate block")
tests := []struct {
in wire.Message // Value to encode
out wire.Message // Expected decoded value
pver uint32 // Protocol version for wire encoding
btcnet wire.BitcoinNet // Network to use for wire encoding
bytes int // Expected num bytes read/written
}{
{msgVersion, msgVersion, pver, wire.MainNet, 125},
{msgVerack, msgVerack, pver, wire.MainNet, 24},
{msgGetAddr, msgGetAddr, pver, wire.MainNet, 24},
{msgAddr, msgAddr, pver, wire.MainNet, 25},
{msgGetBlocks, msgGetBlocks, pver, wire.MainNet, 61},
{msgBlock, msgBlock, pver, wire.MainNet, 239},
{msgInv, msgInv, pver, wire.MainNet, 25},
{msgGetData, msgGetData, pver, wire.MainNet, 25},
{msgNotFound, msgNotFound, pver, wire.MainNet, 25},
{msgTx, msgTx, pver, wire.MainNet, 34},
{msgPing, msgPing, pver, wire.MainNet, 32},
{msgPong, msgPong, pver, wire.MainNet, 32},
{msgGetHeaders, msgGetHeaders, pver, wire.MainNet, 61},
{msgHeaders, msgHeaders, pver, wire.MainNet, 25},
{msgAlert, msgAlert, pver, wire.MainNet, 42},
{msgMemPool, msgMemPool, pver, wire.MainNet, 24},
{msgFilterAdd, msgFilterAdd, pver, wire.MainNet, 26},
{msgFilterClear, msgFilterClear, pver, wire.MainNet, 24},
{msgFilterLoad, msgFilterLoad, pver, wire.MainNet, 35},
{msgMerkleBlock, msgMerkleBlock, pver, wire.MainNet, 110},
{msgReject, msgReject, pver, wire.MainNet, 79},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
nw, err := wire.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 := wire.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 := wire.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 := wire.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 := wire.ProtocolVersion
btcnet := wire.MainNet
// Ensure message errors are as expected with no function specified.
wantErr := "something bad happened"
testErr := wire.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 = wire.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(wire.TestNet3, "", 0, 0)
// Wire encoded bytes for a message that exceeds max overall message
// length.
mpl := uint32(wire.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 wire.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),
&wire.MessageError{},
24,
},
// Exceed max overall message payload length.
{
exceedMaxPayloadBytes,
pver,
btcnet,
len(exceedMaxPayloadBytes),
&wire.MessageError{},
24,
},
// Invalid UTF-8 command.
{
badCommandBytes,
pver,
btcnet,
len(badCommandBytes),
&wire.MessageError{},
24,
},
// Valid, but unsupported command.
{
unsupportedCommandBytes,
pver,
btcnet,
len(unsupportedCommandBytes),
&wire.MessageError{},
24,
},
// Exceed max allowed payload for a message of a specific type.
{
exceedTypePayloadBytes,
pver,
btcnet,
len(exceedTypePayloadBytes),
&wire.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),
&wire.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),
&wire.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 := wire.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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 := wire.ProtocolVersion
btcnet := wire.MainNet
wireErr := &wire.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, wire.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 wire.Message // Message to encode
pver uint32 // Protocol version for wire encoding
btcnet wire.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 := wire.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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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
}
}
}
}

142
wire/msgaddr.go Normal file
View file

@ -0,0 +1,142 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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)
}
msg.AddrList = make([]*NetAddress, 0, count)
for i := uint64(0); i < count; i++ {
na := NetAddress{}
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) 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),
}
}

321
wire/msgaddr_test.go Normal file
View file

@ -0,0 +1,321 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"net"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestAddr tests the MsgAddr API.
func TestAddr(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "addr"
msg := wire.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, err := wire.NewNetAddress(tcpAddr, wire.SFNodeNetwork)
if err != nil {
t.Errorf("NewNetAddress: %v", err)
}
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 < wire.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 = wire.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 = wire.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)
}
return
}
// TestAddrWire tests the MsgAddr wire encode and decode for various numbers
// of addreses and protocol versions.
func TestAddrWire(t *testing.T) {
// A couple of NetAddresses to use for testing.
na := &wire.NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: wire.SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
na2 := &wire.NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: wire.SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8334,
}
// Empty address message.
noAddr := wire.NewMsgAddr()
noAddrEncoded := []byte{
0x00, // Varint for number of addresses
}
// Address message with multiple addresses.
multiAddr := wire.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 *wire.MsgAddr // Message to encode
out *wire.MsgAddr // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no addresses.
{
noAddr,
noAddr,
noAddrEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple addresses.
{
multiAddr,
multiAddr,
multiAddrEncoded,
wire.ProtocolVersion,
},
// Protocol version MultipleAddressVersion-1 with no addresses.
{
noAddr,
noAddr,
noAddrEncoded,
wire.MultipleAddressVersion - 1,
},
}
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)
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 wire.MsgAddr
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
pverMA := wire.MultipleAddressVersion
wireErr := &wire.MessageError{}
// A couple of NetAddresses to use for testing.
na := &wire.NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: wire.SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
na2 := &wire.NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: wire.SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8334,
}
// Address message with multiple addresses.
baseAddr := wire.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 := wire.NewMsgAddr()
for i := 0; i < wire.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 *wire.MsgAddr // 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 addresses count
{baseAddr, baseAddrEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in address list.
{baseAddr, baseAddrEncoded, pver, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxAddr, maxAddrEncoded, pver, 3, wireErr, wireErr},
// Force error with greater than max inventory vectors for
// protocol versions before multiple addresses were allowed.
{maxAddr, maxAddrEncoded, pverMA - 1, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgAddr
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

422
wire/msgalert.go Normal file
View file

@ -0,0 +1,422 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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 < int(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 < int(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
}
err = writeVarString(w, pver, alert.Reserved)
if err != nil {
return err
}
return nil
}
// 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)
if err != nil {
return err
}
return nil
}
// 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) 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")
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 *MsgAlert) BtcEncode(w io.Writer, pver uint32) 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
}
err = writeVarBytes(w, pver, msg.Signature)
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 *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,
}
}

467
wire/msgalert_test.go Normal file
View file

@ -0,0 +1,467 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestMsgAlert tests the MsgAlert API.
func TestMsgAlert(t *testing.T) {
pver := wire.ProtocolVersion
serializedpayload := []byte("some message")
signature := []byte("some sig")
// Ensure we get the same payload and signature back out.
msg := wire.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)
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(wire.Alert)
buf = *new(bytes.Buffer)
err = msg.BtcEncode(&buf, pver)
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 := wire.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 *wire.MsgAlert // Message to encode
out *wire.MsgAlert // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion.
{
baseMsgAlert,
baseMsgAlert,
baseMsgAlertEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgAlert
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
baseMsgAlert := wire.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 *wire.MsgAlert // 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 payload length.
{baseMsgAlert, baseMsgAlertEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in payload.
{baseMsgAlert, baseMsgAlertEncoded, pver, 1, io.ErrShortWrite, io.EOF},
// Force error in signature length.
{baseMsgAlert, baseMsgAlertEncoded, pver, 13, io.ErrShortWrite, io.EOF},
// Force error in signature.
{baseMsgAlert, baseMsgAlertEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgAlert
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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)
if _, ok := err.(*wire.MessageError); !ok {
t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T",
err, wire.MessageError{})
}
// Test Payload Serialize error
// overflow the max number of elements in SetCancel
baseMsgAlert.Payload = new(wire.Alert)
baseMsgAlert.Payload.SetCancel = make([]int32, wire.MaxCountSetCancel+1)
buf := *new(bytes.Buffer)
err = baseMsgAlert.BtcEncode(&buf, pver)
if _, ok := err.(*wire.MessageError); !ok {
t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T",
err, wire.MessageError{})
}
// overflow the max number of elements in SetSubVer
baseMsgAlert.Payload = new(wire.Alert)
baseMsgAlert.Payload.SetSubVer = make([]string, wire.MaxCountSetSubVer+1)
buf = *new(bytes.Buffer)
err = baseMsgAlert.BtcEncode(&buf, pver)
if _, ok := err.(*wire.MessageError); !ok {
t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T",
err, wire.MessageError{})
}
}
// TestAlert tests serialization and deserialization
// of the payload to Alert
func TestAlert(t *testing.T) {
pver := wire.ProtocolVersion
alert := wire.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 := wire.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 := wire.ProtocolVersion
baseAlert := wire.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 *wire.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 wire.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 wire.Alert
r := bytes.NewReader(badAlertEncoded)
err := alert.Deserialize(r, pver)
if _, ok := err.(*wire.MessageError); !ok {
t.Errorf("Alert.Deserialize wrong error got: %T, want: %T",
err, wire.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.(*wire.MessageError); !ok {
t.Errorf("Alert.Deserialize wrong error got: %T, want: %T",
err, wire.MessageError{})
}
}

250
wire/msgblock.go Normal file
View file

@ -0,0 +1,250 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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"
)
// 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.
const MaxBlockPayload = 1000000 // Not actually 1MB which would be 1024 * 1024
// 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) 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)
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.
return msg.BtcDecode(r, 0)
}
// 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) 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)
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.
return msg.BtcEncode(w, 0)
}
// SerializeSize returns the number of bytes it would take to serialize the
// the block.
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
}
// 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
}
// BlockSha computes the block identifier hash for this block.
func (msg *MsgBlock) BlockSha() (ShaHash, error) {
return msg.Header.BlockSha()
}
// TxShas returns a slice of hashes of all of transactions in this block.
func (msg *MsgBlock) TxShas() ([]ShaHash, error) {
shaList := make([]ShaHash, 0, len(msg.Transactions))
for _, tx := range msg.Transactions {
// Ignore error here since TxSha can't fail in the current
// implementation except due to run-time panics.
sha, _ := tx.TxSha()
shaList = append(shaList, sha)
}
return shaList, 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),
}
}

584
wire/msgblock_test.go Normal file
View file

@ -0,0 +1,584 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestBlock tests the MsgBlock API.
func TestBlock(t *testing.T) {
pver := wire.ProtocolVersion
// Block 1 header.
prevHash := &blockOne.Header.PrevBlock
merkleHash := &blockOne.Header.MerkleRoot
bits := blockOne.Header.Bits
nonce := blockOne.Header.Nonce
bh := wire.NewBlockHeader(prevHash, merkleHash, bits, nonce)
// Ensure the command is expected value.
wantCmd := "block"
msg := wire.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(1000000)
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)
}
return
}
// TestBlockTxShas tests the ability to generate a slice of all transaction
// hashes from a block accurately.
func TestBlockTxShas(t *testing.T) {
// Block 1, transaction 1 hash.
hashStr := "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"
wantHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
return
}
wantShas := []wire.ShaHash{*wantHash}
shas, err := blockOne.TxShas()
if err != nil {
t.Errorf("TxShas: %v", err)
}
if !reflect.DeepEqual(shas, wantShas) {
t.Errorf("TxShas: wrong transaction hashes - got %v, want %v",
spew.Sdump(shas), spew.Sdump(wantShas))
}
}
// TestBlockSha tests the ability to generate the hash of a block accurately.
func TestBlockSha(t *testing.T) {
// Block 1 hash.
hashStr := "839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"
wantHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Ensure the hash produced is expected.
blockHash, err := blockOne.BlockSha()
if err != nil {
t.Errorf("BlockSha: %v", err)
}
if !blockHash.IsEqual(wantHash) {
t.Errorf("BlockSha: 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 *wire.MsgBlock // Message to encode
out *wire.MsgBlock // Expected decoded message
buf []byte // Wire encoding
txLocs []wire.TxLoc // Expected transaction locations
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
wire.BIP0035Version,
},
// Protocol version BIP0031Version.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion.
{
&blockOne,
&blockOne,
blockOneBytes,
blockOneTxLocs,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgBlock
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 *wire.MsgBlock // 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.
{&blockOne, blockOneBytes, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in prev block hash.
{&blockOne, blockOneBytes, pver, 4, io.ErrShortWrite, io.EOF},
// Force error in merkle root.
{&blockOne, blockOneBytes, pver, 36, io.ErrShortWrite, io.EOF},
// Force error in timestamp.
{&blockOne, blockOneBytes, pver, 68, io.ErrShortWrite, io.EOF},
// Force error in difficulty bits.
{&blockOne, blockOneBytes, pver, 72, io.ErrShortWrite, io.EOF},
// Force error in header nonce.
{&blockOne, blockOneBytes, pver, 76, io.ErrShortWrite, io.EOF},
// Force error in transaction count.
{&blockOne, blockOneBytes, pver, 80, io.ErrShortWrite, io.EOF},
// Force error in transactions.
{&blockOne, blockOneBytes, pver, 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)
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 wire.MsgBlock
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
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 *wire.MsgBlock // Message to encode
out *wire.MsgBlock // Expected decoded message
buf []byte // Serialized data
txLocs []wire.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 wire.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 wire.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 *wire.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 wire.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 wire.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
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, &wire.MessageError{},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
var msg wire.MsgBlock
r := bytes.NewReader(test.buf)
err := msg.BtcDecode(r, test.pver)
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 := wire.NewMsgBlock(&blockOne.Header)
tests := []struct {
in *wire.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
}
}
}
var blockOne = wire.MsgBlock{
Header: wire.BlockHeader{
Version: 1,
PrevBlock: wire.ShaHash([wire.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: wire.ShaHash([wire.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: []*wire.MsgTx{
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash{},
Index: 0xffffffff,
},
SignatureScript: []byte{
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04,
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.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 = []wire.TxLoc{
{TxStart: 81, TxLen: 134},
}

90
wire/msgfilteradd.go Normal file
View file

@ -0,0 +1,90 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// 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) 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")
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 *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32) 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)
}
err := writeVarBytes(w, pver, msg.Data)
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 *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,
}
}

189
wire/msgfilteradd_test.go Normal file
View file

@ -0,0 +1,189 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
)
// TestFilterAddLatest tests the MsgFilterAdd API against the latest protocol
// version.
func TestFilterAddLatest(t *testing.T) {
pver := wire.ProtocolVersion
data := []byte{0x01, 0x02}
msg := wire.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)
if err != nil {
t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
var readmsg wire.MsgFilterAdd
err = readmsg.BtcDecode(&buf, pver)
if err != nil {
t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err)
}
return
}
// 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 := wire.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, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
var readmsg wire.MsgFilterAdd
err = readmsg.BtcDecode(&buf, wire.BIP0031Version)
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 := wire.NewMsgFilterAdd(data)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
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, wire.ProtocolVersion)
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 := wire.ProtocolVersion
pverNoFilterAdd := wire.BIP0037Version - 1
wireErr := &wire.MessageError{}
baseData := []byte{0x01, 0x02, 0x03, 0x04}
baseFilterAdd := wire.NewMsgFilterAdd(baseData)
baseFilterAddEncoded := append([]byte{0x04}, baseData...)
tests := []struct {
in *wire.MsgFilterAdd // 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 data size.
{
baseFilterAdd, baseFilterAddEncoded, pver, 0,
io.ErrShortWrite, io.EOF,
},
// Force error in data.
{
baseFilterAdd, baseFilterAddEncoded, pver, 1,
io.ErrShortWrite, io.EOF,
},
// Force error due to unsupported protocol version.
{
baseFilterAdd, baseFilterAddEncoded, pverNoFilterAdd, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgFilterAdd
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

59
wire/msgfilterclear.go Normal file
View file

@ -0,0 +1,59 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// 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) 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) 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{}
}

197
wire/msgfilterclear_test.go Normal file
View file

@ -0,0 +1,197 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestFilterCLearLatest tests the MsgFilterClear API against the latest
// protocol version.
func TestFilterClearLatest(t *testing.T) {
pver := wire.ProtocolVersion
msg := wire.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)
}
return
}
// TestFilterClearCrossProtocol tests the MsgFilterClear API when encoding with
// the latest protocol version and decoding with BIP0031Version.
func TestFilterClearCrossProtocol(t *testing.T) {
msg := wire.NewMsgFilterClear()
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of MsgFilterClear failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
var readmsg wire.MsgFilterClear
err = readmsg.BtcDecode(&buf, wire.BIP0031Version)
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 := wire.NewMsgFilterClear()
msgFilterClearEncoded := []byte{}
tests := []struct {
in *wire.MsgFilterClear // Message to encode
out *wire.MsgFilterClear // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
msgFilterClear,
msgFilterClear,
msgFilterClearEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0037Version + 1.
{
msgFilterClear,
msgFilterClear,
msgFilterClearEncoded,
wire.BIP0037Version + 1,
},
// Protocol version BIP0037Version.
{
msgFilterClear,
msgFilterClear,
msgFilterClearEncoded,
wire.BIP0037Version,
},
}
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)
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 wire.MsgFilterClear
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.BIP0037Version - 1
wireErr := &wire.MessageError{}
baseFilterClear := wire.NewMsgFilterClear()
baseFilterClearEncoded := []byte{}
tests := []struct {
in *wire.MsgFilterClear // 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 due to unsupported protocol version.
{
baseFilterClear, baseFilterClearEncoded,
pverNoFilterClear, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgFilterClear
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

141
wire/msgfilterload.go Normal file
View file

@ -0,0 +1,141 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// 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) 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) 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
}
err = writeElements(w, msg.HashFuncs, msg.Tweak, msg.Flags)
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 *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,
}
}

230
wire/msgfilterload_test.go Normal file
View file

@ -0,0 +1,230 @@
// Copyright (c) 2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
)
// TestFilterCLearLatest tests the MsgFilterLoad API against the latest protocol
// version.
func TestFilterLoadLatest(t *testing.T) {
pver := wire.ProtocolVersion
data := []byte{0x01, 0x02}
msg := wire.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)
if err != nil {
t.Errorf("encode of MsgFilterLoad failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := wire.MsgFilterLoad{}
err = readmsg.BtcDecode(&buf, pver)
if err != nil {
t.Errorf("decode of MsgFilterLoad failed [%v] err <%v>", buf, err)
}
return
}
// 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 := wire.NewMsgFilterLoad(data, 10, 0, 0)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg,
err)
}
// Decode with old protocol version.
var readmsg wire.MsgFilterLoad
err = readmsg.BtcDecode(&buf, wire.BIP0031Version)
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 := wire.NewMsgFilterLoad(data, 10, 0, 0)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
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, wire.ProtocolVersion)
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 := wire.NewMsgFilterLoad(data, 61, 0, 0)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
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, wire.ProtocolVersion)
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 := wire.ProtocolVersion
pverNoFilterLoad := wire.BIP0037Version - 1
wireErr := &wire.MessageError{}
baseFilter := []byte{0x01, 0x02, 0x03, 0x04}
baseFilterLoad := wire.NewMsgFilterLoad(baseFilter, 10, 0,
wire.BloomUpdateNone)
baseFilterLoadEncoded := append([]byte{0x04}, baseFilter...)
baseFilterLoadEncoded = append(baseFilterLoadEncoded,
0x00, 0x00, 0x00, 0x0a, // HashFuncs
0x00, 0x00, 0x00, 0x00, // Tweak
0x00) // Flags
tests := []struct {
in *wire.MsgFilterLoad // 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 filter size.
{
baseFilterLoad, baseFilterLoadEncoded, pver, 0,
io.ErrShortWrite, io.EOF,
},
// Force error in filter.
{
baseFilterLoad, baseFilterLoadEncoded, pver, 1,
io.ErrShortWrite, io.EOF,
},
// Force error in hash funcs.
{
baseFilterLoad, baseFilterLoadEncoded, pver, 5,
io.ErrShortWrite, io.EOF,
},
// Force error in tweak.
{
baseFilterLoad, baseFilterLoadEncoded, pver, 9,
io.ErrShortWrite, io.EOF,
},
// Force error in flags.
{
baseFilterLoad, baseFilterLoadEncoded, pver, 13,
io.ErrShortWrite, io.EOF,
},
// Force error due to unsupported protocol version.
{
baseFilterLoad, baseFilterLoadEncoded, pverNoFilterLoad,
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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgFilterLoad
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

47
wire/msggetaddr.go Normal file
View file

@ -0,0 +1,47 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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) 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{}
}

123
wire/msggetaddr_test.go Normal file
View file

@ -0,0 +1,123 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestGetAddr tests the MsgGetAddr API.
func TestGetAddr(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "getaddr"
msg := wire.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)
}
return
}
// TestGetAddrWire tests the MsgGetAddr wire encode and decode for various
// protocol versions.
func TestGetAddrWire(t *testing.T) {
msgGetAddr := wire.NewMsgGetAddr()
msgGetAddrEncoded := []byte{}
tests := []struct {
in *wire.MsgGetAddr // Message to encode
out *wire.MsgGetAddr // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion.
{
msgGetAddr,
msgGetAddr,
msgGetAddrEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgGetAddr
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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
}
}
}

144
wire/msggetblocks.go Normal file
View file

@ -0,0 +1,144 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// 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 []*ShaHash
HashStop ShaHash
}
// AddBlockLocatorHash adds a new block locator hash to the message.
func (msg *MsgGetBlocks) AddBlockLocatorHash(hash *ShaHash) 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) 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)
}
msg.BlockLocatorHashes = make([]*ShaHash, 0, count)
for i := uint64(0); i < count; i++ {
sha := ShaHash{}
err := readElement(r, &sha)
if err != nil {
return err
}
msg.AddBlockLocatorHash(&sha)
}
err = readElement(r, &msg.HashStop)
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 *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32) 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
}
}
err = writeElement(w, &msg.HashStop)
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 *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 * HashSize) + 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 *ShaHash) *MsgGetBlocks {
return &MsgGetBlocks{
ProtocolVersion: ProtocolVersion,
BlockLocatorHashes: make([]*ShaHash, 0, MaxBlockLocatorsPerMsg),
HashStop: *hashStop,
}
}

390
wire/msggetblocks_test.go Normal file
View file

@ -0,0 +1,390 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestGetBlocks tests the MsgGetBlocks API.
func TestGetBlocks(t *testing.T) {
pver := wire.ProtocolVersion
// Block 99500 hash.
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
locatorHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Ensure we get the same data back out.
msg := wire.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 < wire.MaxBlockLocatorsPerMsg; i++ {
err = msg.AddBlockLocatorHash(locatorHash)
}
if err == nil {
t.Errorf("AddBlockLocatorHash: expected error on too many " +
"block locator hashes not received")
}
return
}
// 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 := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// MsgGetBlocks message with no block locators or stop hash.
noLocators := wire.NewMsgGetBlocks(&wire.ShaHash{})
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 := wire.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 *wire.MsgGetBlocks // Message to encode
out *wire.MsgGetBlocks // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Versionwith multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgGetBlocks
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := &wire.MessageError{}
// Block 99499 hash.
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
hashLocator, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// MsgGetBlocks message with multiple block locators and a stop hash.
baseGetBlocks := wire.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 := wire.NewMsgGetBlocks(hashStop)
for i := 0; i < wire.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 *wire.MsgGetBlocks // 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 protocol version.
{baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in block locator hash count.
{baseGetBlocks, baseGetBlocksEncoded, pver, 4, io.ErrShortWrite, io.EOF},
// Force error in block locator hashes.
{baseGetBlocks, baseGetBlocksEncoded, pver, 5, io.ErrShortWrite, io.EOF},
// Force error in stop hash.
{baseGetBlocks, baseGetBlocksEncoded, pver, 69, io.ErrShortWrite, io.EOF},
// Force error with greater than max block locator hashes.
{maxGetBlocks, maxGetBlocksEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgGetBlocks
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

130
wire/msggetdata.go Normal file
View file

@ -0,0 +1,130 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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)
}
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := InvVect{}
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) 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),
}
}

331
wire/msggetdata_test.go Normal file
View file

@ -0,0 +1,331 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestGetData tests the MsgGetData API.
func TestGetData(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "getdata"
msg := wire.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 := wire.ShaHash{}
iv := wire.NewInvVect(wire.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 < wire.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 = wire.NewMsgGetDataSizeHint(wire.MaxInvPerMsg + 1)
wantCap := wire.MaxInvPerMsg
if cap(msg.InvList) != wantCap {
t.Errorf("NewMsgGetDataSizeHint: wrong cap for size hint - "+
"got %v, want %v", cap(msg.InvList), wantCap)
}
return
}
// 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 := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Transation 1 of Block 203707 hash.
hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
txHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
iv2 := wire.NewInvVect(wire.InvTypeTx, txHash)
// Empty MsgGetData message.
NoInv := wire.NewMsgGetData()
NoInvEncoded := []byte{
0x00, // Varint for number of inventory vectors
}
// MsgGetData message with multiple inventory vectors.
MultiInv := wire.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 *wire.MsgGetData // Message to encode
out *wire.MsgGetData // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgGetData
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
wireErr := &wire.MessageError{}
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
// Base message used to induce errors.
baseGetData := wire.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 := wire.NewMsgGetData()
for i := 0; i < wire.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 *wire.MsgGetData // 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 inventory vector count
{baseGetData, baseGetDataEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in inventory list.
{baseGetData, baseGetDataEncoded, pver, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxGetData, maxGetDataEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgGetData
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

139
wire/msggetheaders.go Normal file
View file

@ -0,0 +1,139 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// 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 []*ShaHash
HashStop ShaHash
}
// AddBlockLocatorHash adds a new block locator hash to the message.
func (msg *MsgGetHeaders) AddBlockLocatorHash(hash *ShaHash) 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) 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)
}
msg.BlockLocatorHashes = make([]*ShaHash, 0, count)
for i := uint64(0); i < count; i++ {
sha := ShaHash{}
err := readElement(r, &sha)
if err != nil {
return err
}
msg.AddBlockLocatorHash(&sha)
}
err = readElement(r, &msg.HashStop)
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 *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32) 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 _, sha := range msg.BlockLocatorHashes {
err := writeElement(w, sha)
if err != nil {
return err
}
}
err = writeElement(w, &msg.HashStop)
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 *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 * HashSize) + 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([]*ShaHash, 0, MaxBlockLocatorsPerMsg),
}
}

381
wire/msggetheaders_test.go Normal file
View file

@ -0,0 +1,381 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestGetHeaders tests the MsgGetHeader API.
func TestGetHeaders(t *testing.T) {
pver := wire.ProtocolVersion
// Block 99500 hash.
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
locatorHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Ensure the command is expected value.
wantCmd := "getheaders"
msg := wire.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 < wire.MaxBlockLocatorsPerMsg; i++ {
err = msg.AddBlockLocatorHash(locatorHash)
}
if err == nil {
t.Errorf("AddBlockLocatorHash: expected error on too many " +
"block locator hashes not received")
}
return
}
// 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 := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// MsgGetHeaders message with no block locators or stop hash.
noLocators := wire.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 := wire.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 *wire.MsgGetHeaders // Message to encode
out *wire.MsgGetHeaders // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Versionwith multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion with no block locators.
{
noLocators,
noLocators,
noLocatorsEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion multiple block locators.
{
multiLocators,
multiLocators,
multiLocatorsEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgGetHeaders
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := &wire.MessageError{}
// Block 99499 hash.
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
hashLocator, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 99500 hash.
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
hashLocator2, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Block 100000 hash.
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStop, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// MsgGetHeaders message with multiple block locators and a stop hash.
baseGetHeaders := wire.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 := wire.NewMsgGetHeaders()
for i := 0; i < wire.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 *wire.MsgGetHeaders // 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 protocol version.
{baseGetHeaders, baseGetHeadersEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in block locator hash count.
{baseGetHeaders, baseGetHeadersEncoded, pver, 4, io.ErrShortWrite, io.EOF},
// Force error in block locator hashes.
{baseGetHeaders, baseGetHeadersEncoded, pver, 5, io.ErrShortWrite, io.EOF},
// Force error in stop hash.
{baseGetHeaders, baseGetHeadersEncoded, pver, 69, io.ErrShortWrite, io.EOF},
// Force error with greater than max block locator hashes.
{maxGetHeaders, maxGetHeadersEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgGetHeaders
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

134
wire/msgheaders.go Normal file
View file

@ -0,0 +1,134 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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)
}
msg.Headers = make([]*BlockHeader, 0, count)
for i := uint64(0); i < count; i++ {
bh := BlockHeader{}
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) 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),
}
}

350
wire/msgheaders_test.go Normal file
View file

@ -0,0 +1,350 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"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 := wire.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 < wire.MaxBlockHeadersPerMsg+1; i++ {
err = msg.AddBlockHeader(bh)
}
if reflect.TypeOf(err) != reflect.TypeOf(&wire.MessageError{}) {
t.Errorf("AddBlockHeader: expected error on too many headers " +
"not received")
}
return
}
// 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 := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce)
bh.Version = blockOne.Header.Version
bh.Timestamp = blockOne.Header.Timestamp
// Empty headers message.
noHeaders := wire.NewMsgHeaders()
noHeadersEncoded := []byte{
0x00, // Varint for number of headers
}
// Headers message with one header.
oneHeader := wire.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 *wire.MsgHeaders // Message to encode
out *wire.MsgHeaders // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion with no headers.
{
noHeaders,
noHeaders,
noHeadersEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion with one header.
{
oneHeader,
oneHeader,
oneHeaderEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgHeaders
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
wireErr := &wire.MessageError{}
hash := mainNetGenesisHash
merkleHash := blockOne.Header.MerkleRoot
bits := uint32(0x1d00ffff)
nonce := uint32(0x9962e301)
bh := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce)
bh.Version = blockOne.Header.Version
bh.Timestamp = blockOne.Header.Timestamp
// Headers message with one header.
oneHeader := wire.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 := wire.NewMsgHeaders()
for i := 0; i < wire.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 := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce)
bhTrans.Version = blockOne.Header.Version
bhTrans.Timestamp = blockOne.Header.Timestamp
transHeader := wire.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 *wire.MsgHeaders // 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 header count.
{oneHeader, oneHeaderEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in block header.
{oneHeader, oneHeaderEncoded, pver, 5, io.ErrShortWrite, io.EOF},
// Force error with greater than max headers.
{maxHeaders, maxHeadersEncoded, pver, 3, wireErr, wireErr},
// Force error with number of transactions.
{transHeader, transHeaderEncoded, pver, 81, io.ErrShortWrite, io.EOF},
// Force error with included transactions.
{transHeader, transHeaderEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgHeaders
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

138
wire/msginv.go Normal file
View file

@ -0,0 +1,138 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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)
}
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := InvVect{}
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) 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),
}
}

332
wire/msginv_test.go Normal file
View file

@ -0,0 +1,332 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestInv tests the MsgInv API.
func TestInv(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "inv"
msg := wire.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 := wire.ShaHash{}
iv := wire.NewInvVect(wire.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 < wire.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 = wire.NewMsgInvSizeHint(wire.MaxInvPerMsg + 1)
wantCap := wire.MaxInvPerMsg
if cap(msg.InvList) != wantCap {
t.Errorf("NewMsgInvSizeHint: wrong cap for size hint - "+
"got %v, want %v", cap(msg.InvList), wantCap)
}
return
}
// 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 := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Transation 1 of Block 203707 hash.
hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
txHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
iv2 := wire.NewInvVect(wire.InvTypeTx, txHash)
// Empty inv message.
NoInv := wire.NewMsgInv()
NoInvEncoded := []byte{
0x00, // Varint for number of inventory vectors
}
// Inv message with multiple inventory vectors.
MultiInv := wire.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 *wire.MsgInv // Message to encode
out *wire.MsgInv // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgInv
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
wireErr := &wire.MessageError{}
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
// Base inv message used to induce errors.
baseInv := wire.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 := wire.NewMsgInv()
for i := 0; i < wire.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 *wire.MsgInv // 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 inventory vector count
{baseInv, baseInvEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in inventory list.
{baseInv, baseInvEncoded, pver, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxInv, maxInvEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgInv
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

60
wire/msgmempool.go Normal file
View file

@ -0,0 +1,60 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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) 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{}
}

66
wire/msgmempool_test.go Normal file
View file

@ -0,0 +1,66 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"testing"
"github.com/btcsuite/btcd/wire"
)
func TestMemPool(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "mempool"
msg := wire.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)
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 := wire.BIP0035Version - 1
err = msg.BtcEncode(&buf, oldPver)
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 := wire.NewMsgMemPool()
err = readmsg.BtcDecode(&buf, pver)
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)
if err == nil {
s := "decode of MsgMemPool passed for old protocol version %v err <%v>"
t.Errorf(s, msg, err)
}
return
}

163
wire/msgmerkleblock.go Normal file
View file

@ -0,0 +1,163 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// 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 []*ShaHash
Flags []byte
}
// AddTxHash adds a new transaction hash to the message.
func (msg *MsgMerkleBlock) AddTxHash(hash *ShaHash) 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) 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)
}
msg.Hashes = make([]*ShaHash, 0, count)
for i := uint64(0); i < count; i++ {
var sha ShaHash
err := readElement(r, &sha)
if err != nil {
return err
}
msg.AddTxHash(&sha)
}
msg.Flags, err = readVarBytes(r, pver, maxFlagsPerMerkleBlock,
"merkle block flags size")
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 *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) 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
}
}
err = writeVarBytes(w, pver, msg.Flags)
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 *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([]*ShaHash, 0),
Flags: make([]byte, 0),
}
}

426
wire/msgmerkleblock_test.go Normal file
View file

@ -0,0 +1,426 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"crypto/rand"
"io"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestMerkleBlock tests the MsgMerkleBlock API.
func TestMerkleBlock(t *testing.T) {
pver := wire.ProtocolVersion
// Block 1 header.
prevHash := &blockOne.Header.PrevBlock
merkleHash := &blockOne.Header.MerkleRoot
bits := blockOne.Header.Bits
nonce := blockOne.Header.Nonce
bh := wire.NewBlockHeader(prevHash, merkleHash, bits, nonce)
// Ensure the command is expected value.
wantCmd := "merkleblock"
msg := wire.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(1000000)
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 < wire.MaxTxPerBlock; i++ {
rand.Read(data)
hash, err := wire.NewShaHash(data)
if err != nil {
t.Errorf("NewShaHash 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 := wire.NewShaHash(data)
if err != nil {
t.Errorf("NewShaHash 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)
if err != nil {
t.Errorf("encode of MsgMerkleBlock failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := wire.MsgMerkleBlock{}
err = readmsg.BtcDecode(&buf, pver)
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)
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, wire.MaxFlagsPerMerkleBlock+1)
err = msg.BtcEncode(&buf, pver)
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 := wire.NewBlockHeader(prevHash, merkleHash, bits, nonce)
msg := wire.NewMsgMerkleBlock(bh)
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg,
err)
}
// Decode with old protocol version.
var readmsg wire.MsgFilterLoad
err = readmsg.BtcDecode(&buf, wire.BIP0031Version)
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 *wire.MsgMerkleBlock // Message to encode
out *wire.MsgMerkleBlock // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
&merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes,
wire.ProtocolVersion,
},
// Protocol version BIP0037Version.
{
&merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes,
wire.BIP0037Version,
},
}
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)
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 wire.MsgMerkleBlock
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.BIP0037Version - 1
wireErr := &wire.MessageError{}
tests := []struct {
in *wire.MsgMerkleBlock // 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.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 0,
io.ErrShortWrite, io.EOF,
},
// Force error in prev block hash.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 4,
io.ErrShortWrite, io.EOF,
},
// Force error in merkle root.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 36,
io.ErrShortWrite, io.EOF,
},
// Force error in timestamp.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 68,
io.ErrShortWrite, io.EOF,
},
// Force error in difficulty bits.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 72,
io.ErrShortWrite, io.EOF,
},
// Force error in header nonce.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 76,
io.ErrShortWrite, io.EOF,
},
// Force error in transaction count.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 80,
io.ErrShortWrite, io.EOF,
},
// Force error in num hashes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 84,
io.ErrShortWrite, io.EOF,
},
// Force error in hashes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 85,
io.ErrShortWrite, io.EOF,
},
// Force error in num flag bytes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 117,
io.ErrShortWrite, io.EOF,
},
// Force error in flag bytes.
{
&merkleBlockOne, merkleBlockOneBytes, pver, 118,
io.ErrShortWrite, io.EOF,
},
// Force error due to unsupported protocol version.
{
&merkleBlockOne, merkleBlockOneBytes, pverNoMerkleBlock,
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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgMerkleBlock
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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
wire.TstWriteVarInt(&buf, pver, wire.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()
wire.TstWriteVarInt(&buf, pver, wire.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
err error // Expected error
}{
// Block that claims to have more than max allowed hashes.
{exceedMaxHashes, pver, &wire.MessageError{}},
// Block that claims to have more than max allowed flag bytes.
{exceedMaxFlagBytes, pver, &wire.MessageError{}},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
var msg wire.MsgMerkleBlock
r := bytes.NewReader(test.buf)
err := msg.BtcDecode(r, test.pver)
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 = wire.MsgMerkleBlock{
Header: wire.BlockHeader{
Version: 1,
PrevBlock: wire.ShaHash([wire.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: wire.ShaHash([wire.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: []*wire.ShaHash{
(*wire.ShaHash)(&[wire.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
}

107
wire/msgnotfound.go Normal file
View file

@ -0,0 +1,107 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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)
}
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := InvVect{}
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) 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),
}
}

321
wire/msgnotfound_test.go Normal file
View file

@ -0,0 +1,321 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestNotFound tests the MsgNotFound API.
func TestNotFound(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "notfound"
msg := wire.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 := wire.ShaHash{}
iv := wire.NewInvVect(wire.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 < wire.MaxInvPerMsg; i++ {
err = msg.AddInvVect(iv)
}
if err == nil {
t.Errorf("AddInvVect: expected error on too many inventory " +
"vectors not received")
}
return
}
// 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 := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Transation 1 of Block 203707 hash.
hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
txHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
iv2 := wire.NewInvVect(wire.InvTypeTx, txHash)
// Empty notfound message.
NoInv := wire.NewMsgNotFound()
NoInvEncoded := []byte{
0x00, // Varint for number of inventory vectors
}
// NotFound message with multiple inventory vectors.
MultiInv := wire.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 *wire.MsgNotFound // Message to encode
out *wire.MsgNotFound // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion no inv vectors.
{
NoInv,
NoInv,
NoInvEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion with multiple inv vectors.
{
MultiInv,
MultiInv,
MultiInvEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgNotFound
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
wireErr := &wire.MessageError{}
// Block 203707 hash.
hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
blockHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
// Base message used to induce errors.
baseNotFound := wire.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 := wire.NewMsgNotFound()
for i := 0; i < wire.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 *wire.MsgNotFound // 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 inventory vector count
{baseNotFound, baseNotFoundEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in inventory list.
{baseNotFound, baseNotFoundEncoded, pver, 1, io.ErrShortWrite, io.EOF},
// Force error with greater than max inventory vectors.
{maxNotFound, maxNotFoundEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgNotFound
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

87
wire/msgping.go Normal file
View file

@ -0,0 +1,87 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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) 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,
}
}

243
wire/msgping_test.go Normal file
View file

@ -0,0 +1,243 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestPing tests the MsgPing API against the latest protocol version.
func TestPing(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure we get the same nonce back out.
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
msg := wire.NewMsgPing(nonce)
if msg.Nonce != nonce {
t.Errorf("NewMsgPing: wrong nonce - got %v, want %v",
msg.Nonce, nonce)
}
// Ensure the command is expected value.
wantCmd := "ping"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgPing: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(8)
maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
return
}
// 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 := wire.BIP0031Version
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
msg := wire.NewMsgPing(nonce)
if msg.Nonce != nonce {
t.Errorf("NewMsgPing: wrong nonce - got %v, want %v",
msg.Nonce, nonce)
}
// Ensure max payload is expected value for old protocol version.
wantPayload := uint32(0)
maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
// Test encode with old protocol version.
var buf bytes.Buffer
err = msg.BtcEncode(&buf, pver)
if err != nil {
t.Errorf("encode of MsgPing failed %v err <%v>", msg, err)
}
// Test decode with old protocol version.
readmsg := wire.NewMsgPing(0)
err = readmsg.BtcDecode(&buf, pver)
if err != nil {
t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err)
}
// Since this protocol version doesn't support the nonce, make sure
// it didn't get encoded and decoded back out.
if msg.Nonce == readmsg.Nonce {
t.Errorf("Should not get same nonce for protocol version %d", pver)
}
return
}
// TestPingCrossProtocol tests the MsgPing API when encoding with the latest
// protocol version and decoding with BIP0031Version.
func TestPingCrossProtocol(t *testing.T) {
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("RandomUint64: Error generating nonce: %v", err)
}
msg := wire.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, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of MsgPing failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
readmsg := wire.NewMsgPing(0)
err = readmsg.BtcDecode(&buf, wire.BIP0031Version)
if err != nil {
t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err)
}
// Since one of the protocol versions doesn't support the nonce, make
// sure it didn't get encoded and decoded back out.
if msg.Nonce == readmsg.Nonce {
t.Error("Should not get same nonce for cross protocol")
}
}
// TestPingWire tests the MsgPing wire encode and decode for various protocol
// versions.
func TestPingWire(t *testing.T) {
tests := []struct {
in wire.MsgPing // Message to encode
out wire.MsgPing // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
wire.MsgPing{Nonce: 123123}, // 0x1e0f3
wire.MsgPing{Nonce: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
wire.ProtocolVersion,
},
// Protocol version BIP0031Version+1
{
wire.MsgPing{Nonce: 456456}, // 0x6f708
wire.MsgPing{Nonce: 456456}, // 0x6f708
[]byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
wire.BIP0031Version + 1,
},
// Protocol version BIP0031Version
{
wire.MsgPing{Nonce: 789789}, // 0xc0d1d
wire.MsgPing{Nonce: 0}, // No nonce for pver
[]byte{}, // No nonce for pver
wire.BIP0031Version,
},
}
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)
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 wire.MsgPing
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
tests := []struct {
in *wire.MsgPing // 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.
{
&wire.MsgPing{Nonce: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00},
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 := test.in.BtcEncode(w, test.pver)
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 wire.MsgPing
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}

88
wire/msgpong.go Normal file
View file

@ -0,0 +1,88 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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)
}
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 *MsgPong) BtcEncode(w io.Writer, pver uint32) 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)
}
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 *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,
}
}

276
wire/msgpong_test.go Normal file
View file

@ -0,0 +1,276 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestPongLatest tests the MsgPong API against the latest protocol version.
func TestPongLatest(t *testing.T) {
pver := wire.ProtocolVersion
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("RandomUint64: error generating nonce: %v", err)
}
msg := wire.NewMsgPong(nonce)
if msg.Nonce != nonce {
t.Errorf("NewMsgPong: wrong nonce - got %v, want %v",
msg.Nonce, nonce)
}
// Ensure the command is expected value.
wantCmd := "pong"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgPong: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(8)
maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
// Test encode with latest protocol version.
var buf bytes.Buffer
err = msg.BtcEncode(&buf, pver)
if err != nil {
t.Errorf("encode of MsgPong failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readmsg := wire.NewMsgPong(0)
err = readmsg.BtcDecode(&buf, pver)
if err != nil {
t.Errorf("decode of MsgPong failed [%v] err <%v>", buf, err)
}
// Ensure nonce is the same.
if msg.Nonce != readmsg.Nonce {
t.Errorf("Should get same nonce for protocol version %d", pver)
}
return
}
// 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 := wire.BIP0031Version
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("Error generating nonce: %v", err)
}
msg := wire.NewMsgPong(nonce)
if msg.Nonce != nonce {
t.Errorf("Should get same nonce back out.")
}
// Ensure max payload is expected value for old protocol version.
size := msg.MaxPayloadLength(pver)
if size != 0 {
t.Errorf("Max length should be 0 for pong protocol version %d.",
pver)
}
// Test encode with old protocol version.
var buf bytes.Buffer
err = msg.BtcEncode(&buf, pver)
if err == nil {
t.Errorf("encode of MsgPong succeeded when it shouldn't have %v",
msg)
}
// Test decode with old protocol version.
readmsg := wire.NewMsgPong(0)
err = readmsg.BtcDecode(&buf, pver)
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)
}
return
}
// TestPongCrossProtocol tests the MsgPong API when encoding with the latest
// protocol version and decoding with BIP0031Version.
func TestPongCrossProtocol(t *testing.T) {
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("Error generating nonce: %v", err)
}
msg := wire.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, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of MsgPong failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
readmsg := wire.NewMsgPong(0)
err = readmsg.BtcDecode(&buf, wire.BIP0031Version)
if err == nil {
t.Errorf("encode of MsgPong succeeded when it shouldn't have %v",
msg)
}
// Since one of the protocol versions doesn't support the pong message,
// make sure the nonce didn't get encoded and decoded back out.
if msg.Nonce == readmsg.Nonce {
t.Error("Should not get same nonce for cross protocol")
}
}
// TestPongWire tests the MsgPong wire encode and decode for various protocol
// versions.
func TestPongWire(t *testing.T) {
tests := []struct {
in wire.MsgPong // Message to encode
out wire.MsgPong // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
wire.MsgPong{Nonce: 123123}, // 0x1e0f3
wire.MsgPong{Nonce: 123123}, // 0x1e0f3
[]byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
wire.ProtocolVersion,
},
// Protocol version BIP0031Version+1
{
wire.MsgPong{Nonce: 456456}, // 0x6f708
wire.MsgPong{Nonce: 456456}, // 0x6f708
[]byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
wire.BIP0031Version + 1,
},
}
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)
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 wire.MsgPong
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
pverNoPong := wire.BIP0031Version
wireErr := &wire.MessageError{}
basePong := wire.NewMsgPong(123123) // 0x1e0f3
basePongEncoded := []byte{
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
}
tests := []struct {
in *wire.MsgPong // Value to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
max int // Max size of fixed buffer to induce errors
writeErr error // Expected write error
readErr error // Expected read error
}{
// Latest protocol version with intentional read/write errors.
// Force error in nonce.
{basePong, basePongEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error due to unsupported protocol version.
{basePong, basePongEncoded, pverNoPong, 4, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgPong
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

184
wire/msgreject.go Normal file
View file

@ -0,0 +1,184 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
)
// 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 ShaHash
}
// 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) 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) 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,
}
}

385
wire/msgreject_test.go Normal file
View file

@ -0,0 +1,385 @@
// Copyright (c) 2014-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestRejectCodeStringer tests the stringized output for the reject code type.
func TestRejectCodeStringer(t *testing.T) {
tests := []struct {
in wire.RejectCode
want string
}{
{wire.RejectMalformed, "REJECT_MALFORMED"},
{wire.RejectInvalid, "REJECT_INVALID"},
{wire.RejectObsolete, "REJECT_OBSOLETE"},
{wire.RejectDuplicate, "REJECT_DUPLICATE"},
{wire.RejectNonstandard, "REJECT_NONSTANDARD"},
{wire.RejectDust, "REJECT_DUST"},
{wire.RejectInsufficientFee, "REJECT_INSUFFICIENTFEE"},
{wire.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 := wire.ProtocolVersion
// Create reject message data.
rejCommand := (&wire.MsgBlock{}).Command()
rejCode := wire.RejectDuplicate
rejReason := "duplicate block"
rejHash := mainNetGenesisHash
// Ensure we get the correct data back out.
msg := wire.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(wire.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)
if err != nil {
t.Errorf("encode of MsgReject failed %v err <%v>", msg, err)
}
// Test decode with latest protocol version.
readMsg := wire.MsgReject{}
err = readMsg.BtcDecode(&buf, pver)
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 := wire.RejectVersion - 1
// Create reject message data.
rejCommand := (&wire.MsgBlock{}).Command()
rejCode := wire.RejectDuplicate
rejReason := "duplicate block"
rejHash := mainNetGenesisHash
msg := wire.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)
if err == nil {
t.Errorf("encode of MsgReject succeeded when it shouldn't "+
"have %v", msg)
}
// // Test decode with old protocol version.
readMsg := wire.MsgReject{}
err = readMsg.BtcDecode(&buf, pver)
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 := (&wire.MsgBlock{}).Command()
rejCode := wire.RejectDuplicate
rejReason := "duplicate block"
rejHash := mainNetGenesisHash
msg := wire.NewMsgReject(rejCommand, rejCode, rejReason)
msg.Hash = rejHash
// Encode with latest protocol version.
var buf bytes.Buffer
err := msg.BtcEncode(&buf, wire.ProtocolVersion)
if err != nil {
t.Errorf("encode of MsgReject failed %v err <%v>", msg, err)
}
// Decode with old protocol version.
readMsg := wire.MsgReject{}
err = readMsg.BtcDecode(&buf, wire.RejectVersion-1)
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 wire.MsgReject // Message to encode
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version rejected command version (no hash).
{
wire.MsgReject{
Cmd: "version",
Code: wire.RejectDuplicate,
Reason: "duplicate version",
},
[]byte{
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version"
0x12, // wire.RejectDuplicate
0x11, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, // "duplicate version"
},
wire.ProtocolVersion,
},
// Latest protocol version rejected command block (has hash).
{
wire.MsgReject{
Cmd: "block",
Code: wire.RejectDuplicate,
Reason: "duplicate block",
Hash: mainNetGenesisHash,
},
[]byte{
0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "block"
0x12, // wire.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
},
wire.ProtocolVersion,
},
}
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)
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 wire.MsgReject
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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 := wire.ProtocolVersion
pverNoReject := wire.RejectVersion - 1
wireErr := &wire.MessageError{}
baseReject := wire.NewMsgReject("block", wire.RejectDuplicate,
"duplicate block")
baseReject.Hash = mainNetGenesisHash
baseRejectEncoded := []byte{
0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "block"
0x12, // wire.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 *wire.MsgReject // 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 reject command.
{baseReject, baseRejectEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in reject code.
{baseReject, baseRejectEncoded, pver, 6, io.ErrShortWrite, io.EOF},
// Force error in reject reason.
{baseReject, baseRejectEncoded, pver, 7, io.ErrShortWrite, io.EOF},
// Force error in reject hash.
{baseReject, baseRejectEncoded, pver, 23, io.ErrShortWrite, io.EOF},
// Force error due to unsupported protocol version.
{baseReject, baseRejectEncoded, pverNoReject, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgReject
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.MessageError); !ok {
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}

562
wire/msgtx.go Normal file
View file

@ -0,0 +1,562 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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"
"fmt"
"io"
"strconv"
)
const (
// TxVersion is the current latest supported transaction version.
TxVersion = 1
// MaxTxInSequenceNum is the maximum sequence number the sequence field
// of a transaction input can be.
MaxTxInSequenceNum uint32 = 0xffffffff
// MaxPrevOutIndex is the maximum index the index field of a previous
// outpoint can be.
MaxPrevOutIndex uint32 = 0xffffffff
)
// defaultTxInOutAlloc is the default size used for the backing array for
// transaction inputs and outputs. The array will dynamically grow as needed,
// but this figure is intended to provide enough space for the number of
// inputs and outputs in a typical transaction without needing to grow the
// backing array multiple times.
const defaultTxInOutAlloc = 15
const (
// minTxInPayload is the minimum payload size for a transaction input.
// PreviousOutPoint.Hash + PreviousOutPoint.Index 4 bytes + Varint for
// SignatureScript length 1 byte + Sequence 4 bytes.
minTxInPayload = 9 + HashSize
// maxTxInPerMessage is the maximum number of transactions inputs that
// a transaction which fits into a message could possibly have.
maxTxInPerMessage = (MaxMessagePayload / minTxInPayload) + 1
// minTxOutPayload is the minimum payload size for a transaction output.
// Value 8 bytes + Varint for PkScript length 1 byte.
minTxOutPayload = 9
// maxTxOutPerMessage is the maximum number of transactions outputs that
// a transaction which fits into a message could possibly have.
maxTxOutPerMessage = (MaxMessagePayload / minTxOutPayload) + 1
// minTxPayload is the minimum payload size for a transaction. Note
// that any realistically usable transaction must have at least one
// input or output, but that is a rule enforced at a higher layer, so
// it is intentionally not included here.
// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint
// number of transaction outputs 1 byte + LockTime 4 bytes + min input
// payload + min output payload.
minTxPayload = 10
)
// OutPoint defines a bitcoin data type that is used to track previous
// transaction outputs.
type OutPoint struct {
Hash ShaHash
Index uint32
}
// NewOutPoint returns a new bitcoin transaction outpoint point with the
// provided hash and index.
func NewOutPoint(hash *ShaHash, index uint32) *OutPoint {
return &OutPoint{
Hash: *hash,
Index: index,
}
}
// String returns the OutPoint in the human-readable form "hash:index".
func (o OutPoint) String() string {
// Allocate enough for hash string, colon, and 10 digits. Although
// at the time of writing, the number of digits can be no greater than
// the length of the decimal representation of maxTxOutPerMessage, the
// maximum message payload may increase in the future and this
// optimization may go unnoticed, so allocate space for 10 decimal
// digits, which will fit any uint32.
buf := make([]byte, 2*HashSize+1, 2*HashSize+1+10)
copy(buf, o.Hash.String())
buf[2*HashSize] = ':'
buf = strconv.AppendUint(buf, uint64(o.Index), 10)
return string(buf)
}
// TxIn defines a bitcoin transaction input.
type TxIn struct {
PreviousOutPoint OutPoint
SignatureScript []byte
Sequence uint32
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction input.
func (t *TxIn) SerializeSize() int {
// Outpoint Hash 32 bytes + Outpoint Index 4 bytes + Sequence 4 bytes +
// serialized varint size for the length of SignatureScript +
// SignatureScript bytes.
return 40 + VarIntSerializeSize(uint64(len(t.SignatureScript))) +
len(t.SignatureScript)
}
// NewTxIn returns a new bitcoin transaction input with the provided
// previous outpoint point and signature script with a default sequence of
// MaxTxInSequenceNum.
func NewTxIn(prevOut *OutPoint, signatureScript []byte) *TxIn {
return &TxIn{
PreviousOutPoint: *prevOut,
SignatureScript: signatureScript,
Sequence: MaxTxInSequenceNum,
}
}
// TxOut defines a bitcoin transaction output.
type TxOut struct {
Value int64
PkScript []byte
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction output.
func (t *TxOut) SerializeSize() int {
// Value 8 bytes + serialized varint size for the length of PkScript +
// PkScript bytes.
return 8 + VarIntSerializeSize(uint64(len(t.PkScript))) + len(t.PkScript)
}
// NewTxOut returns a new bitcoin transaction output with the provided
// transaction value and public key script.
func NewTxOut(value int64, pkScript []byte) *TxOut {
return &TxOut{
Value: value,
PkScript: pkScript,
}
}
// MsgTx implements the Message interface and represents a bitcoin tx message.
// It is used to deliver transaction information in response to a getdata
// message (MsgGetData) for a given transaction.
//
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
// inputs and outputs.
type MsgTx struct {
Version int32
TxIn []*TxIn
TxOut []*TxOut
LockTime uint32
}
// AddTxIn adds a transaction input to the message.
func (msg *MsgTx) AddTxIn(ti *TxIn) {
msg.TxIn = append(msg.TxIn, ti)
}
// AddTxOut adds a transaction output to the message.
func (msg *MsgTx) AddTxOut(to *TxOut) {
msg.TxOut = append(msg.TxOut, to)
}
// TxSha generates the ShaHash name for the transaction.
func (msg *MsgTx) TxSha() (ShaHash, error) {
// Encode the transaction and calculate double sha256 on the result.
// Ignore the error returns since the only way the encode could fail
// is being out of memory or due to nil pointers, both of which would
// cause a run-time panic. Also, SetBytes can't fail here due to the
// fact DoubleSha256 always returns a []byte of the right size
// regardless of input.
buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize()))
_ = msg.Serialize(buf)
var sha ShaHash
_ = sha.SetBytes(DoubleSha256(buf.Bytes()))
// Even though this function can't currently fail, it still returns
// a potential error to help future proof the API should a failure
// become possible.
return sha, nil
}
// Copy creates a deep copy of a transaction so that the original does not get
// modified when the copy is manipulated.
func (msg *MsgTx) Copy() *MsgTx {
// Create new tx and start by copying primitive values and making space
// for the transaction inputs and outputs.
newTx := MsgTx{
Version: msg.Version,
TxIn: make([]*TxIn, 0, len(msg.TxIn)),
TxOut: make([]*TxOut, 0, len(msg.TxOut)),
LockTime: msg.LockTime,
}
// Deep copy the old TxIn data.
for _, oldTxIn := range msg.TxIn {
// Deep copy the old previous outpoint.
oldOutPoint := oldTxIn.PreviousOutPoint
newOutPoint := OutPoint{}
newOutPoint.Hash.SetBytes(oldOutPoint.Hash[:])
newOutPoint.Index = oldOutPoint.Index
// Deep copy the old signature script.
var newScript []byte
oldScript := oldTxIn.SignatureScript
oldScriptLen := len(oldScript)
if oldScriptLen > 0 {
newScript = make([]byte, oldScriptLen, oldScriptLen)
copy(newScript, oldScript[:oldScriptLen])
}
// Create new txIn with the deep copied data and append it to
// new Tx.
newTxIn := TxIn{
PreviousOutPoint: newOutPoint,
SignatureScript: newScript,
Sequence: oldTxIn.Sequence,
}
newTx.TxIn = append(newTx.TxIn, &newTxIn)
}
// Deep copy the old TxOut data.
for _, oldTxOut := range msg.TxOut {
// Deep copy the old PkScript
var newScript []byte
oldScript := oldTxOut.PkScript
oldScriptLen := len(oldScript)
if oldScriptLen > 0 {
newScript = make([]byte, oldScriptLen, oldScriptLen)
copy(newScript, oldScript[:oldScriptLen])
}
// Create new txOut with the deep copied data and append it to
// new Tx.
newTxOut := TxOut{
Value: oldTxOut.Value,
PkScript: newScript,
}
newTx.TxOut = append(newTx.TxOut, &newTxOut)
}
return &newTx
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
// See Deserialize for decoding transactions stored to disk, such as in a
// database, as opposed to decoding transactions from the wire.
func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
var buf [4]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return err
}
msg.Version = int32(binary.LittleEndian.Uint32(buf[:]))
count, err := readVarInt(r, pver)
if err != nil {
return err
}
// Prevent more input transactions than could possibly fit into a
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxInPerMessage) {
str := fmt.Sprintf("too many input transactions to fit into "+
"max message size [count %d, max %d]", count,
maxTxInPerMessage)
return messageError("MsgTx.BtcDecode", str)
}
msg.TxIn = make([]*TxIn, count)
for i := uint64(0); i < count; i++ {
ti := TxIn{}
err = readTxIn(r, pver, msg.Version, &ti)
if err != nil {
return err
}
msg.TxIn[i] = &ti
}
count, err = readVarInt(r, pver)
if err != nil {
return err
}
// Prevent more output transactions than could possibly fit into a
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxOutPerMessage) {
str := fmt.Sprintf("too many output transactions to fit into "+
"max message size [count %d, max %d]", count,
maxTxOutPerMessage)
return messageError("MsgTx.BtcDecode", str)
}
msg.TxOut = make([]*TxOut, count)
for i := uint64(0); i < count; i++ {
to := TxOut{}
err = readTxOut(r, pver, msg.Version, &to)
if err != nil {
return err
}
msg.TxOut[i] = &to
}
_, err = io.ReadFull(r, buf[:])
if err != nil {
return err
}
msg.LockTime = binary.LittleEndian.Uint32(buf[:])
return nil
}
// Deserialize decodes a transaction 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 transaction. 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 transaction at all. As of the time this comment was written, the
// encoded transaction 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 *MsgTx) 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.
return msg.BtcDecode(r, 0)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
// See Serialize for encoding transactions to be stored to disk, such as in a
// database, as opposed to encoding transactions for the wire.
func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32) error {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], uint32(msg.Version))
_, err := w.Write(buf[:])
if err != nil {
return err
}
count := uint64(len(msg.TxIn))
err = writeVarInt(w, pver, count)
if err != nil {
return err
}
for _, ti := range msg.TxIn {
err = writeTxIn(w, pver, msg.Version, ti)
if err != nil {
return err
}
}
count = uint64(len(msg.TxOut))
err = writeVarInt(w, pver, count)
if err != nil {
return err
}
for _, to := range msg.TxOut {
err = writeTxOut(w, pver, msg.Version, to)
if err != nil {
return err
}
}
binary.LittleEndian.PutUint32(buf[:], msg.LockTime)
_, err = w.Write(buf[:])
if err != nil {
return err
}
return nil
}
// Serialize encodes the transaction to w using a format that suitable for
// long-term storage such as a database while respecting the Version field in
// the transaction. This function differs from BtcEncode in that BtcEncode
// encodes the transaction 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 transaction at all. As of the time this comment was written, the
// encoded transaction 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 *MsgTx) 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.
return msg.BtcEncode(w, 0)
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction.
func (msg *MsgTx) SerializeSize() int {
// Version 4 bytes + LockTime 4 bytes + Serialized varint size for the
// number of transaction inputs and outputs.
n := 8 + VarIntSerializeSize(uint64(len(msg.TxIn))) +
VarIntSerializeSize(uint64(len(msg.TxOut)))
for _, txIn := range msg.TxIn {
n += txIn.SerializeSize()
}
for _, txOut := range msg.TxOut {
n += txOut.SerializeSize()
}
return n
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgTx) Command() string {
return CmdTx
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
return MaxBlockPayload
}
// NewMsgTx returns a new bitcoin tx message that conforms to the Message
// interface. The return instance has a default version of TxVersion and there
// are no transaction inputs or outputs. Also, the lock time is set to zero
// to indicate the transaction is valid immediately as opposed to some time in
// future.
func NewMsgTx() *MsgTx {
return &MsgTx{
Version: TxVersion,
TxIn: make([]*TxIn, 0, defaultTxInOutAlloc),
TxOut: make([]*TxOut, 0, defaultTxInOutAlloc),
}
}
// readOutPoint reads the next sequence of bytes from r as an OutPoint.
func readOutPoint(r io.Reader, pver uint32, version int32, op *OutPoint) error {
_, err := io.ReadFull(r, op.Hash[:])
if err != nil {
return err
}
var buf [4]byte
_, err = io.ReadFull(r, buf[:])
if err != nil {
return err
}
op.Index = binary.LittleEndian.Uint32(buf[:])
return nil
}
// writeOutPoint encodes op to the bitcoin protocol encoding for an OutPoint
// to w.
func writeOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error {
_, err := w.Write(op.Hash[:])
if err != nil {
return err
}
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], op.Index)
_, err = w.Write(buf[:])
if err != nil {
return err
}
return nil
}
// readTxIn reads the next sequence of bytes from r as a transaction input
// (TxIn).
func readTxIn(r io.Reader, pver uint32, version int32, ti *TxIn) error {
var op OutPoint
err := readOutPoint(r, pver, version, &op)
if err != nil {
return err
}
ti.PreviousOutPoint = op
ti.SignatureScript, err = readVarBytes(r, pver, MaxMessagePayload,
"transaction input signature script")
if err != nil {
return err
}
var buf [4]byte
_, err = io.ReadFull(r, buf[:])
if err != nil {
return err
}
ti.Sequence = binary.LittleEndian.Uint32(buf[:])
return nil
}
// writeTxIn encodes ti to the bitcoin protocol encoding for a transaction
// input (TxIn) to w.
func writeTxIn(w io.Writer, pver uint32, version int32, ti *TxIn) error {
err := writeOutPoint(w, pver, version, &ti.PreviousOutPoint)
if err != nil {
return err
}
err = writeVarBytes(w, pver, ti.SignatureScript)
if err != nil {
return err
}
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], ti.Sequence)
_, err = w.Write(buf[:])
if err != nil {
return err
}
return nil
}
// readTxOut reads the next sequence of bytes from r as a transaction output
// (TxOut).
func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
var buf [8]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return err
}
to.Value = int64(binary.LittleEndian.Uint64(buf[:]))
to.PkScript, err = readVarBytes(r, pver, MaxMessagePayload,
"transaction output public key script")
if err != nil {
return err
}
return nil
}
// writeTxOut encodes to into the bitcoin protocol encoding for a transaction
// output (TxOut) to w.
func writeTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(to.Value))
_, err := w.Write(buf[:])
if err != nil {
return err
}
err = writeVarBytes(w, pver, to.PkScript)
if err != nil {
return err
}
return nil
}

692
wire/msgtx_test.go Normal file
View file

@ -0,0 +1,692 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"fmt"
"io"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestTx tests the MsgTx API.
func TestTx(t *testing.T) {
pver := wire.ProtocolVersion
// Block 100000 hash.
hashStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Ensure the command is expected value.
wantCmd := "tx"
msg := wire.NewMsgTx()
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(1000 * 1000)
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 transaction output point data back out.
// NOTE: This is a block hash and made up index, but we're only
// testing package functionality.
prevOutIndex := uint32(1)
prevOut := wire.NewOutPoint(hash, prevOutIndex)
if !prevOut.Hash.IsEqual(hash) {
t.Errorf("NewOutPoint: wrong hash - got %v, want %v",
spew.Sprint(&prevOut.Hash), spew.Sprint(hash))
}
if prevOut.Index != prevOutIndex {
t.Errorf("NewOutPoint: wrong index - got %v, want %v",
prevOut.Index, prevOutIndex)
}
prevOutStr := fmt.Sprintf("%s:%d", hash.String(), prevOutIndex)
if s := prevOut.String(); s != prevOutStr {
t.Errorf("OutPoint.String: unexpected result - got %v, "+
"want %v", s, prevOutStr)
}
// Ensure we get the same transaction input back out.
sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
txIn := wire.NewTxIn(prevOut, sigScript)
if !reflect.DeepEqual(&txIn.PreviousOutPoint, prevOut) {
t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v",
spew.Sprint(&txIn.PreviousOutPoint),
spew.Sprint(prevOut))
}
if !bytes.Equal(txIn.SignatureScript, sigScript) {
t.Errorf("NewTxIn: wrong signature script - got %v, want %v",
spew.Sdump(txIn.SignatureScript),
spew.Sdump(sigScript))
}
// Ensure we get the same transaction output back out.
txValue := int64(5000000000)
pkScript := []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
}
txOut := wire.NewTxOut(txValue, pkScript)
if txOut.Value != txValue {
t.Errorf("NewTxOut: wrong pk script - got %v, want %v",
txOut.Value, txValue)
}
if !bytes.Equal(txOut.PkScript, pkScript) {
t.Errorf("NewTxOut: wrong pk script - got %v, want %v",
spew.Sdump(txOut.PkScript),
spew.Sdump(pkScript))
}
// Ensure transaction inputs are added properly.
msg.AddTxIn(txIn)
if !reflect.DeepEqual(msg.TxIn[0], txIn) {
t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v",
spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn))
}
// Ensure transaction outputs are added properly.
msg.AddTxOut(txOut)
if !reflect.DeepEqual(msg.TxOut[0], txOut) {
t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v",
spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut))
}
// Ensure the copy produced an identical transaction message.
newMsg := msg.Copy()
if !reflect.DeepEqual(newMsg, msg) {
t.Errorf("Copy: mismatched tx messages - got %v, want %v",
spew.Sdump(newMsg), spew.Sdump(msg))
}
return
}
// TestTxSha tests the ability to generate the hash of a transaction accurately.
func TestTxSha(t *testing.T) {
// Hash of first transaction from block 113875.
hashStr := "f051e59b5e2503ac626d03aaeac8ab7be2d72ba4b7e97119c5852d70d52dcb86"
wantHash, err := wire.NewShaHashFromStr(hashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
return
}
// First transaction from block 113875.
msgTx := wire.NewMsgTx()
txIn := wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash{},
Index: 0xffffffff,
},
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
Sequence: 0xffffffff,
}
txOut := wire.TxOut{
Value: 5000000000,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
}
msgTx.AddTxIn(&txIn)
msgTx.AddTxOut(&txOut)
msgTx.LockTime = 0
// Ensure the hash produced is expected.
txHash, err := msgTx.TxSha()
if err != nil {
t.Errorf("TxSha: %v", err)
}
if !txHash.IsEqual(wantHash) {
t.Errorf("TxSha: wrong hash - got %v, want %v",
spew.Sprint(txHash), spew.Sprint(wantHash))
}
}
// TestTxWire tests the MsgTx wire encode and decode for various numbers
// of transaction inputs and outputs and protocol versions.
func TestTxWire(t *testing.T) {
// Empty tx message.
noTx := wire.NewMsgTx()
noTx.Version = 1
noTxEncoded := []byte{
0x01, 0x00, 0x00, 0x00, // Version
0x00, // Varint for number of input transactions
0x00, // Varint for number of output transactions
0x00, 0x00, 0x00, 0x00, // Lock time
}
tests := []struct {
in *wire.MsgTx // Message to encode
out *wire.MsgTx // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no transactions.
{
noTx,
noTx,
noTxEncoded,
wire.ProtocolVersion,
},
// Latest protocol version with multiple transactions.
{
multiTx,
multiTx,
multiTxEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version with no transactions.
{
noTx,
noTx,
noTxEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0035Version with multiple transactions.
{
multiTx,
multiTx,
multiTxEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version with no transactions.
{
noTx,
noTx,
noTxEncoded,
wire.BIP0031Version,
},
// Protocol version BIP0031Version with multiple transactions.
{
multiTx,
multiTx,
multiTxEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion with no transactions.
{
noTx,
noTx,
noTxEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with multiple transactions.
{
multiTx,
multiTx,
multiTxEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion with no transactions.
{
noTx,
noTx,
noTxEncoded,
wire.MultipleAddressVersion,
},
// Protocol version MultipleAddressVersion with multiple transactions.
{
multiTx,
multiTx,
multiTxEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgTx
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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
}
}
}
// TestTxWireErrors performs negative tests against wire encode and decode
// of MsgTx to confirm error paths work correctly.
func TestTxWireErrors(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 *wire.MsgTx // 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.
{multiTx, multiTxEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in number of transaction inputs.
{multiTx, multiTxEncoded, pver, 4, io.ErrShortWrite, io.EOF},
// Force error in transaction input previous block hash.
{multiTx, multiTxEncoded, pver, 5, io.ErrShortWrite, io.EOF},
// Force error in transaction input previous block hash.
{multiTx, multiTxEncoded, pver, 5, io.ErrShortWrite, io.EOF},
// Force error in transaction input previous block output index.
{multiTx, multiTxEncoded, pver, 37, io.ErrShortWrite, io.EOF},
// Force error in transaction input signature script length.
{multiTx, multiTxEncoded, pver, 41, io.ErrShortWrite, io.EOF},
// Force error in transaction input signature script.
{multiTx, multiTxEncoded, pver, 42, io.ErrShortWrite, io.EOF},
// Force error in transaction input sequence.
{multiTx, multiTxEncoded, pver, 49, io.ErrShortWrite, io.EOF},
// Force error in number of transaction outputs.
{multiTx, multiTxEncoded, pver, 53, io.ErrShortWrite, io.EOF},
// Force error in transaction output value.
{multiTx, multiTxEncoded, pver, 54, io.ErrShortWrite, io.EOF},
// Force error in transaction output pk script length.
{multiTx, multiTxEncoded, pver, 62, io.ErrShortWrite, io.EOF},
// Force error in transaction output pk script.
{multiTx, multiTxEncoded, pver, 63, io.ErrShortWrite, io.EOF},
// Force error in transaction output lock time.
{multiTx, multiTxEncoded, pver, 130, 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)
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 wire.MsgTx
r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver)
if err != test.readErr {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestTxSerialize tests MsgTx serialize and deserialize.
func TestTxSerialize(t *testing.T) {
noTx := wire.NewMsgTx()
noTx.Version = 1
noTxEncoded := []byte{
0x01, 0x00, 0x00, 0x00, // Version
0x00, // Varint for number of input transactions
0x00, // Varint for number of output transactions
0x00, 0x00, 0x00, 0x00, // Lock time
}
tests := []struct {
in *wire.MsgTx // Message to encode
out *wire.MsgTx // Expected decoded message
buf []byte // Serialized data
}{
// No transactions.
{
noTx,
noTx,
noTxEncoded,
},
// Multiple transactions.
{
multiTx,
multiTx,
multiTxEncoded,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
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 transaction.
var tx wire.MsgTx
rbuf := bytes.NewReader(test.buf)
err = tx.Deserialize(rbuf)
if err != nil {
t.Errorf("Deserialize #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&tx, test.out) {
t.Errorf("Deserialize #%d\n got: %s want: %s", i,
spew.Sdump(&tx), spew.Sdump(test.out))
continue
}
}
}
// TestTxSerializeErrors performs negative tests against wire encode and decode
// of MsgTx to confirm error paths work correctly.
func TestTxSerializeErrors(t *testing.T) {
tests := []struct {
in *wire.MsgTx // 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.
{multiTx, multiTxEncoded, 0, io.ErrShortWrite, io.EOF},
// Force error in number of transaction inputs.
{multiTx, multiTxEncoded, 4, io.ErrShortWrite, io.EOF},
// Force error in transaction input previous block hash.
{multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF},
// Force error in transaction input previous block hash.
{multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF},
// Force error in transaction input previous block output index.
{multiTx, multiTxEncoded, 37, io.ErrShortWrite, io.EOF},
// Force error in transaction input signature script length.
{multiTx, multiTxEncoded, 41, io.ErrShortWrite, io.EOF},
// Force error in transaction input signature script.
{multiTx, multiTxEncoded, 42, io.ErrShortWrite, io.EOF},
// Force error in transaction input sequence.
{multiTx, multiTxEncoded, 49, io.ErrShortWrite, io.EOF},
// Force error in number of transaction outputs.
{multiTx, multiTxEncoded, 53, io.ErrShortWrite, io.EOF},
// Force error in transaction output value.
{multiTx, multiTxEncoded, 54, io.ErrShortWrite, io.EOF},
// Force error in transaction output pk script length.
{multiTx, multiTxEncoded, 62, io.ErrShortWrite, io.EOF},
// Force error in transaction output pk script.
{multiTx, multiTxEncoded, 63, io.ErrShortWrite, io.EOF},
// Force error in transaction output lock time.
{multiTx, multiTxEncoded, 130, io.ErrShortWrite, io.EOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
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 transaction.
var tx wire.MsgTx
r := newFixedReader(test.max, test.buf)
err = tx.Deserialize(r)
if err != test.readErr {
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
}
}
// TestTxOverflowErrors performs tests to ensure deserializing transactions
// which are intentionally crafted to use large values for the variable number
// of inputs and outputs are handled properly. This could otherwise potentially
// be used as an attack vector.
func TestTxOverflowErrors(t *testing.T) {
// Use protocol version 70001 and transaction version 1 specifically
// here instead of the latest values because the test data is using
// bytes encoded with those versions.
pver := uint32(70001)
txVer := uint32(1)
tests := []struct {
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
version uint32 // Transaction version
err error // Expected error
}{
// Transaction that claims to have ~uint64(0) inputs.
{
[]byte{
0x00, 0x00, 0x00, 0x01, // Version
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, // Varint for number of input transactions
}, pver, txVer, &wire.MessageError{},
},
// Transaction that claims to have ~uint64(0) outputs.
{
[]byte{
0x00, 0x00, 0x00, 0x01, // Version
0x00, // Varint for number of input transactions
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, // Varint for number of output transactions
}, pver, txVer, &wire.MessageError{},
},
// Transaction that has an input with a signature script that
// claims to have ~uint64(0) length.
{
[]byte{
0x00, 0x00, 0x00, 0x01, // 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
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, // Varint for length of signature script
}, pver, txVer, &wire.MessageError{},
},
// Transaction that has an output with a public key script
// that claims to have ~uint64(0) length.
{
[]byte{
0x00, 0x00, 0x00, 0x01, // 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
0x00, // Varint for length of signature script
0xff, 0xff, 0xff, 0xff, // Sequence
0x01, // Varint for number of output transactions
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Transaction amount
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, // Varint for length of public key script
}, pver, txVer, &wire.MessageError{},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Decode from wire format.
var msg wire.MsgTx
r := bytes.NewReader(test.buf)
err := msg.BtcDecode(r, test.pver)
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
}
// Decode 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
}
}
}
// TestTxSerializeSize performs tests to ensure the serialize size for various
// transactions is accurate.
func TestTxSerializeSize(t *testing.T) {
// Empty tx message.
noTx := wire.NewMsgTx()
noTx.Version = 1
tests := []struct {
in *wire.MsgTx // Tx to encode
size int // Expected serialized size
}{
// No inputs or outpus.
{noTx, 10},
// Transcaction with an input and an output.
{multiTx, 134},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
serializedSize := test.in.SerializeSize()
if serializedSize != test.size {
t.Errorf("MsgTx.SerializeSize: #%d got: %d, want: %d", i,
serializedSize, test.size)
continue
}
}
}
// multiTx is a MsgTx with an input and output and used in various tests.
var multiTx = &wire.MsgTx{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash{},
Index: 0xffffffff,
},
SignatureScript: []byte{
0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62,
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x12a05f200,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
}
// multiTxEncoded is the wire encoded bytes for multiTx using protocol version
// 60002 and is used in the various tests.
var multiTxEncoded = []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, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // 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, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
0x00, 0x00, 0x00, 0x00, // Lock time
}

46
wire/msgverack.go Normal file
View file

@ -0,0 +1,46 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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) 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) 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{}
}

122
wire/msgverack_test.go Normal file
View file

@ -0,0 +1,122 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestVerAck tests the MsgVerAck API.
func TestVerAck(t *testing.T) {
pver := wire.ProtocolVersion
// Ensure the command is expected value.
wantCmd := "verack"
msg := wire.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)
}
return
}
// TestVerAckWire tests the MsgVerAck wire encode and decode for various
// protocol versions.
func TestVerAckWire(t *testing.T) {
msgVerAck := wire.NewMsgVerAck()
msgVerAckEncoded := []byte{}
tests := []struct {
in *wire.MsgVerAck // Message to encode
out *wire.MsgVerAck // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
wire.ProtocolVersion,
},
// Protocol version BIP0035Version.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion.
{
msgVerAck,
msgVerAck,
msgVerAckEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgVerAck
rbuf := bytes.NewReader(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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
}
}
}

296
wire/msgversion.go Normal file
View file

@ -0,0 +1,296 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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"
"net"
"strings"
"time"
)
// MaxUserAgentLen is the maximum allowed length for the user agent field in a
// version message (MsgVersion).
const MaxUserAgentLen = 2000
// DefaultUserAgent for wire in the stack
const DefaultUserAgent = "/btcwire:0.2.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 {
if msg.Services&service == service {
return true
}
return false
}
// 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) error {
buf, ok := r.(*bytes.Buffer)
if !ok {
return fmt.Errorf("MsgVersion.BtcDecode reader is not a " +
"*bytes.Buffer")
}
var sec int64
err := readElements(buf, &msg.ProtocolVersion, &msg.Services, &sec)
if err != nil {
return err
}
msg.Timestamp = time.Unix(sec, 0)
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) 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,
}
}
// NewMsgVersionFromConn is a convenience function that extracts the remote
// and local address from conn and returns a new bitcoin version message that
// conforms to the Message interface. See NewMsgVersion.
func NewMsgVersionFromConn(conn net.Conn, nonce uint64,
lastBlock int32) (*MsgVersion, error) {
// Don't assume any services until we know otherwise.
lna, err := NewNetAddress(conn.LocalAddr(), 0)
if err != nil {
return nil, err
}
// Don't assume any services until we know otherwise.
rna, err := NewNetAddress(conn.RemoteAddr(), 0)
if err != nil {
return nil, err
}
return NewMsgVersion(lna, rna, nonce, lastBlock), nil
}
// 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
}

596
wire/msgversion_test.go Normal file
View file

@ -0,0 +1,596 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"net"
"reflect"
"strings"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)
// TestVersion tests the MsgVersion API.
func TestVersion(t *testing.T) {
pver := wire.ProtocolVersion
// Create version message data.
lastBlock := int32(234234)
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
me, err := wire.NewNetAddress(tcpAddrMe, wire.SFNodeNetwork)
if err != nil {
t.Errorf("NewNetAddress: %v", err)
}
tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}
you, err := wire.NewNetAddress(tcpAddrYou, wire.SFNodeNetwork)
if err != nil {
t.Errorf("NewNetAddress: %v", err)
}
nonce, err := wire.RandomUint64()
if err != nil {
t.Errorf("RandomUint64: error generating nonce: %v", err)
}
// Ensure we get the correct data back out.
msg := wire.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 != wire.DefaultUserAgent {
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
msg.UserAgent, wire.DefaultUserAgent)
}
if msg.LastBlock != lastBlock {
t.Errorf("NewMsgVersion: wrong last block - got %v, want %v",
msg.LastBlock, lastBlock)
}
if msg.DisableRelayTx != false {
t.Errorf("NewMsgVersion: disable relay tx is not false by "+
"default - got %v, want %v", msg.DisableRelayTx, false)
}
msg.AddUserAgent("myclient", "1.2.3", "optional", "comments")
customUserAgent := wire.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",
wire.MaxUserAgentLen-len(customUserAgent)-2+1), "")
if _, ok := err.(*wire.MessageError); !ok {
t.Errorf("AddUserAgent: expected error not received "+
"- got %v, want %T", err, wire.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(wire.SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service is set")
}
// Ensure the command is expected value.
wantCmd := "version"
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgVersion: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
// Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes +
// remote and local net addresses + nonce 8 bytes + length of user agent
// (varInt) + max allowed user agent length + last block 4 bytes +
// relay transactions flag 1 byte.
wantPayload := uint32(2102)
maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
// Ensure adding the full service node flag works.
msg.AddService(wire.SFNodeNetwork)
if msg.Services != wire.SFNodeNetwork {
t.Errorf("AddService: wrong services - got %v, want %v",
msg.Services, wire.SFNodeNetwork)
}
if !msg.HasService(wire.SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service not set")
}
// Use a fake connection.
conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou}
msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != nil {
t.Errorf("NewMsgVersionFromConn: %v", err)
}
// Ensure we get the correct connection data back out.
if !msg.AddrMe.IP.Equal(tcpAddrMe.IP) {
t.Errorf("NewMsgVersionFromConn: wrong me ip - got %v, want %v",
msg.AddrMe.IP, tcpAddrMe.IP)
}
if !msg.AddrYou.IP.Equal(tcpAddrYou.IP) {
t.Errorf("NewMsgVersionFromConn: wrong you ip - got %v, want %v",
msg.AddrYou.IP, tcpAddrYou.IP)
}
// Use a fake connection with local UDP addresses to force a failure.
conn = &fakeConn{
localAddr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333},
remoteAddr: tcpAddrYou,
}
msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != wire.ErrInvalidNetAddr {
t.Errorf("NewMsgVersionFromConn: expected error not received "+
"- got %v, want %v", err, wire.ErrInvalidNetAddr)
}
// Use a fake connection with remote UDP addresses to force a failure.
conn = &fakeConn{
localAddr: tcpAddrMe,
remoteAddr: &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333},
}
msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != wire.ErrInvalidNetAddr {
t.Errorf("NewMsgVersionFromConn: expected error not received "+
"- got %v, want %v", err, wire.ErrInvalidNetAddr)
}
return
}
// 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 *wire.MsgVersion // Message to encode
out *wire.MsgVersion // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
baseVersionBIP0037,
baseVersionBIP0037,
baseVersionBIP0037Encoded,
wire.ProtocolVersion,
},
// Protocol version BIP0037Version with relay transactions field
// true.
{
baseVersionBIP0037,
baseVersionBIP0037,
baseVersionBIP0037Encoded,
wire.BIP0037Version,
},
// Protocol version BIP0037Version with relay transactions field
// false.
{
verRelayTxFalse,
verRelayTxFalse,
verRelayTxFalseEncoded,
wire.BIP0037Version,
},
// Protocol version BIP0035Version.
{
baseVersion,
baseVersion,
baseVersionEncoded,
wire.BIP0035Version,
},
// Protocol version BIP0031Version.
{
baseVersion,
baseVersion,
baseVersionEncoded,
wire.BIP0031Version,
},
// Protocol version NetAddressTimeVersion.
{
baseVersion,
baseVersion,
baseVersionEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version MultipleAddressVersion.
{
baseVersion,
baseVersion,
baseVersionEncoded,
wire.MultipleAddressVersion,
},
}
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)
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 wire.MsgVersion
rbuf := bytes.NewBuffer(test.buf)
err = msg.BtcDecode(rbuf, test.pver)
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)
wireErr := &wire.MessageError{}
// Ensure calling MsgVersion.BtcDecode with a non *bytes.Buffer returns
// error.
fr := newFixedReader(0, []byte{})
if err := baseVersion.BtcDecode(fr, pver); 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", wire.MaxUserAgentLen-8+1) + ":0.0.1/"
exceedUAVer.UserAgent = newUA
// Encode the new UA length as a varint.
var newUAVarIntBuf bytes.Buffer
err := wire.TstWriteVarInt(&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 *wire.MsgVersion // 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 protocol version.
{baseVersion, baseVersionEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in services.
{baseVersion, baseVersionEncoded, pver, 4, io.ErrShortWrite, io.EOF},
// Force error in timestamp.
{baseVersion, baseVersionEncoded, pver, 12, io.ErrShortWrite, io.EOF},
// Force error in remote address.
{baseVersion, baseVersionEncoded, pver, 20, io.ErrShortWrite, io.EOF},
// Force error in local address.
{baseVersion, baseVersionEncoded, pver, 47, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in nonce.
{baseVersion, baseVersionEncoded, pver, 73, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in user agent length.
{baseVersion, baseVersionEncoded, pver, 81, io.ErrShortWrite, io.EOF},
// Force error in user agent.
{baseVersion, baseVersionEncoded, pver, 82, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in last block.
{baseVersion, baseVersionEncoded, pver, 98, io.ErrShortWrite, io.ErrUnexpectedEOF},
// Force error in relay tx - no read error should happen since
// it's optional.
{
baseVersionBIP0037, baseVersionBIP0037Encoded,
wire.BIP0037Version, 101, io.ErrShortWrite, nil,
},
// Force error due to user agent too big
{exceedUAVer, exceedUAVerEncoded, pver, 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)
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 wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 wire.MsgVersion
buf := bytes.NewBuffer(test.buf[0:test.max])
err = msg.BtcDecode(buf, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type wire.MessageError, check
// them for equality.
if _, ok := err.(*wire.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 := wire.MsgVersion{
ProtocolVersion: 60002,
Services: wire.SFNodeNetwork,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST)
AddrYou: wire.NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: wire.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 = wire.NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: wire.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 *wire.MsgVersion // Expected message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
{
&onlyRequiredVersion,
onlyRequiredVersionEncoded,
wire.ProtocolVersion,
},
{
&addrMeVersion,
addrMeVersionEncoded,
wire.ProtocolVersion,
},
{
&nonceVersion,
nonceVersionEncoded,
wire.ProtocolVersion,
},
{
&uaVersion,
uaVersionEncoded,
wire.ProtocolVersion,
},
{
&lastBlockVersion,
lastBlockVersionEncoded,
wire.ProtocolVersion,
},
}
for i, test := range tests {
// Decode the message from wire format.
var msg wire.MsgVersion
rbuf := bytes.NewBuffer(test.buf)
err := msg.BtcDecode(rbuf, test.pver)
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 = &wire.MsgVersion{
ProtocolVersion: 60002,
Services: wire.SFNodeNetwork,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST)
AddrYou: wire.NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: wire.SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8333,
},
AddrMe: wire.NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: wire.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 = &wire.MsgVersion{
ProtocolVersion: 70001,
Services: wire.SFNodeNetwork,
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST)
AddrYou: wire.NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: wire.SFNodeNetwork,
IP: net.ParseIP("192.168.0.1"),
Port: 8333,
},
AddrMe: wire.NetAddress{
Timestamp: time.Time{}, // Zero value -- no timestamp in version
Services: wire.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
}

172
wire/netaddress.go Normal file
View file

@ -0,0 +1,172 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"encoding/binary"
"errors"
"io"
"net"
"time"
)
// ErrInvalidNetAddr describes an error that indicates the caller didn't specify
// a TCP address as required.
var ErrInvalidNetAddr = errors.New("provided net.Addr is not a net.TCPAddr")
// 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 {
if na.Services&service == service {
return true
}
return false
}
// AddService adds service as a supported service by the peer generating the
// message.
func (na *NetAddress) AddService(service ServiceFlag) {
na.Services |= service
}
// SetAddress is a convenience function to set the IP address and port in one
// call.
func (na *NetAddress) SetAddress(ip net.IP, port uint16) {
na.IP = ip
na.Port = port
}
// 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 {
// Limit the timestamp to one second precision since the protocol
// doesn't support better.
na := NetAddress{
Timestamp: time.Unix(time.Now().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.
//
// Note that addr must be a net.TCPAddr. An ErrInvalidNetAddr is returned
// if it is not.
func NewNetAddress(addr net.Addr, services ServiceFlag) (*NetAddress, error) {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return nil, ErrInvalidNetAddr
}
na := NewNetAddressIPPort(tcpAddr.IP, uint16(tcpAddr.Port), services)
return na, nil
}
// 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 timestamp time.Time
var services ServiceFlag
var ip [16]byte
var port uint16
// 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 {
var stamp uint32
err := readElement(r, &stamp)
if err != nil {
return err
}
timestamp = time.Unix(int64(stamp), 0)
}
err := readElements(r, &services, &ip)
if err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
err = binary.Read(r, binary.BigEndian, &port)
if err != nil {
return err
}
na.Timestamp = timestamp
na.Services = services
na.SetAddress(net.IP(ip[:]), 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.
err = binary.Write(w, binary.BigEndian, na.Port)
if err != nil {
return err
}
return nil
}

294
wire/netaddress_test.go Normal file
View file

@ -0,0 +1,294 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"io"
"net"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"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.
tcpAddr := &net.TCPAddr{
IP: ip,
Port: port,
}
na, err := wire.NewNetAddress(tcpAddr, 0)
if err != nil {
t.Errorf("NewNetAddress: %v", err)
}
// 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(wire.SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service is set")
}
// Ensure adding the full service node flag works.
na.AddService(wire.SFNodeNetwork)
if na.Services != wire.SFNodeNetwork {
t.Errorf("AddService: wrong services - got %v, want %v",
na.Services, wire.SFNodeNetwork)
}
if !na.HasService(wire.SFNodeNetwork) {
t.Errorf("HasService: SFNodeNetwork service not set")
}
// Ensure max payload is expected value for latest protocol version.
pver := wire.ProtocolVersion
wantPayload := uint32(30)
maxPayload := wire.TstMaxNetAddressPayload(wire.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 = wire.NetAddressTimeVersion - 1
wantPayload = 26
maxPayload = wire.TstMaxNetAddressPayload(pver)
if maxPayload != wantPayload {
t.Errorf("maxNetAddressPayload: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
// Check for expected failure on wrong address type.
udpAddr := &net.UDPAddr{}
_, err = wire.NewNetAddress(udpAddr, 0)
if err != wire.ErrInvalidNetAddr {
t.Errorf("NewNetAddress: expected error not received - "+
"got %v, want %v", err, wire.ErrInvalidNetAddr)
}
}
// 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 := wire.NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: wire.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 wire.NetAddress // NetAddress to encode
out wire.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,
wire.ProtocolVersion,
},
// Latest protocol version with ts flag.
{
baseNetAddr,
baseNetAddr,
true,
baseNetAddrEncoded,
wire.ProtocolVersion,
},
// Protocol version NetAddressTimeVersion without ts flag.
{
baseNetAddr,
baseNetAddrNoTS,
false,
baseNetAddrNoTSEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion with ts flag.
{
baseNetAddr,
baseNetAddr,
true,
baseNetAddrEncoded,
wire.NetAddressTimeVersion,
},
// Protocol version NetAddressTimeVersion-1 without ts flag.
{
baseNetAddr,
baseNetAddrNoTS,
false,
baseNetAddrNoTSEncoded,
wire.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,
wire.NetAddressTimeVersion - 1,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := wire.TstWriteNetAddress(&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 wire.NetAddress
rbuf := bytes.NewReader(test.buf)
err = wire.TstReadNetAddress(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 := wire.ProtocolVersion
pverNAT := wire.NetAddressTimeVersion - 1
// baseNetAddr is used in the various tests as a baseline NetAddress.
baseNetAddr := wire.NetAddress{
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST
Services: wire.SFNodeNetwork,
IP: net.ParseIP("127.0.0.1"),
Port: 8333,
}
tests := []struct {
in *wire.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 := wire.TstWriteNetAddress(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 wire.NetAddress
r := newFixedReader(test.max, test.buf)
err = wire.TstReadNetAddress(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
}
}
}

118
wire/protocol.go Normal file
View file

@ -0,0 +1,118 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// 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 = 70002
// 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
)
// 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
)
// Map of service flags back to their constant names for pretty printing.
var sfStrings = map[ServiceFlag]string{
SFNodeNetwork: "SFNodeNetwork",
}
// 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, name := range sfStrings {
if f&flag == flag {
s += name + "|"
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))
}

57
wire/protocol_test.go Normal file
View file

@ -0,0 +1,57 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"testing"
"github.com/btcsuite/btcd/wire"
)
// TestServiceFlagStringer tests the stringized output for service flag types.
func TestServiceFlagStringer(t *testing.T) {
tests := []struct {
in wire.ServiceFlag
want string
}{
{0, "0x0"},
{wire.SFNodeNetwork, "SFNodeNetwork"},
{0xffffffff, "SFNodeNetwork|0xfffffffe"},
}
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 wire.BitcoinNet
want string
}{
{wire.MainNet, "MainNet"},
{wire.TestNet, "TestNet"},
{wire.TestNet3, "TestNet3"},
{wire.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
}
}
}

108
wire/shahash.go Normal file
View file

@ -0,0 +1,108 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"encoding/hex"
"fmt"
)
// Size of array used to store sha hashes. See ShaHash.
const HashSize = 32
// MaxHashStringSize is the maximum length of a ShaHash hash string.
const MaxHashStringSize = HashSize * 2
// ErrHashStrSize describes an error that indicates the caller specified a hash
// string that has too many characters.
var ErrHashStrSize = fmt.Errorf("max hash string length is %v bytes", MaxHashStringSize)
// ShaHash is used in several of the bitcoin messages and common structures. It
// typically represents the double sha256 of data.
type ShaHash [HashSize]byte
// String returns the ShaHash as the hexadecimal string of the byte-reversed
// hash.
func (hash ShaHash) String() string {
for i := 0; i < HashSize/2; i++ {
hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
}
return hex.EncodeToString(hash[:])
}
// Bytes returns the bytes which represent the hash as a byte slice.
func (hash *ShaHash) Bytes() []byte {
newHash := make([]byte, HashSize)
copy(newHash, hash[:])
return newHash
}
// SetBytes sets the bytes which represent the hash. An error is returned if
// the number of bytes passed in is not HashSize.
func (hash *ShaHash) SetBytes(newHash []byte) error {
nhlen := len(newHash)
if nhlen != HashSize {
return fmt.Errorf("invalid sha length of %v, want %v", nhlen,
HashSize)
}
copy(hash[:], newHash[0:HashSize])
return nil
}
// IsEqual returns true if target is the same as hash.
func (hash *ShaHash) IsEqual(target *ShaHash) bool {
return bytes.Equal(hash[:], target[:])
}
// NewShaHash returns a new ShaHash from a byte slice. An error is returned if
// the number of bytes passed in is not HashSize.
func NewShaHash(newHash []byte) (*ShaHash, error) {
var sh ShaHash
err := sh.SetBytes(newHash)
if err != nil {
return nil, err
}
return &sh, err
}
// NewShaHashFromStr creates a ShaHash from a hash string. The string should be
// the hexadecimal string of a byte-reversed hash, but any missing characters
// result in zero padding at the end of the ShaHash.
func NewShaHashFromStr(hash string) (*ShaHash, error) {
// Return error if hash string is too long.
if len(hash) > MaxHashStringSize {
return nil, ErrHashStrSize
}
// Hex decoder expects the hash to be a multiple of two.
if len(hash)%2 != 0 {
hash = "0" + hash
}
// Convert string hash to bytes.
buf, err := hex.DecodeString(hash)
if err != nil {
return nil, err
}
// Un-reverse the decoded bytes, copying into in leading bytes of a
// ShaHash. There is no need to explicitly pad the result as any
// missing (when len(buf) < HashSize) bytes from the decoded hex string
// will remain zeros at the end of the ShaHash.
var ret ShaHash
blen := len(buf)
mid := blen / 2
if blen%2 != 0 {
mid++
}
blen--
for i, b := range buf[:mid] {
ret[i], ret[blen-i] = buf[blen-i], b
}
return &ret, nil
}

182
wire/shahash_test.go Normal file
View file

@ -0,0 +1,182 @@
// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire_test
import (
"bytes"
"encoding/hex"
"testing"
"github.com/btcsuite/btcd/wire"
)
// TestShaHash tests the ShaHash API.
func TestShaHash(t *testing.T) {
// Hash of block 234439.
blockHashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef"
blockHash, err := wire.NewShaHashFromStr(blockHashStr)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
}
// Hash of block 234440 as byte slice.
buf := []byte{
0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1,
0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8,
0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f,
0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
hash, err := wire.NewShaHash(buf)
if err != nil {
t.Errorf("NewShaHash: unexpected error %v", err)
}
// Ensure proper size.
if len(hash) != wire.HashSize {
t.Errorf("NewShaHash: hash length mismatch - got: %v, want: %v",
len(hash), wire.HashSize)
}
// Ensure contents match.
if !bytes.Equal(hash[:], buf) {
t.Errorf("NewShaHash: hash contents mismatch - got: %v, want: %v",
hash[:], buf)
}
// Ensure contents of hash of block 234440 don't match 234439.
if hash.IsEqual(blockHash) {
t.Errorf("IsEqual: hash contents should not match - got: %v, want: %v",
hash, blockHash)
}
// Set hash from byte slice and ensure contents match.
err = hash.SetBytes(blockHash.Bytes())
if err != nil {
t.Errorf("SetBytes: %v", err)
}
if !hash.IsEqual(blockHash) {
t.Errorf("IsEqual: hash contents mismatch - got: %v, want: %v",
hash, blockHash)
}
// Invalid size for SetBytes.
err = hash.SetBytes([]byte{0x00})
if err == nil {
t.Errorf("SetBytes: failed to received expected err - got: nil")
}
// Invalid size for NewShaHash.
invalidHash := make([]byte, wire.HashSize+1)
_, err = wire.NewShaHash(invalidHash)
if err == nil {
t.Errorf("NewShaHash: failed to received expected err - got: nil")
}
}
// TestShaHashString tests the stringized output for sha hashes.
func TestShaHashString(t *testing.T) {
// Block 100000 hash.
wantStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hash := wire.ShaHash([wire.HashSize]byte{ // Make go vet happy.
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,
})
hashStr := hash.String()
if hashStr != wantStr {
t.Errorf("String: wrong hash string - got %v, want %v",
hashStr, wantStr)
}
}
// TestNewShaHashFromStr executes tests against the NewShaHashFromStr function.
func TestNewShaHashFromStr(t *testing.T) {
tests := []struct {
in string
want wire.ShaHash
err error
}{
// Genesis hash.
{
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
mainNetGenesisHash,
nil,
},
// Genesis hash with stripped leading zeros.
{
"19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
mainNetGenesisHash,
nil,
},
// Empty string.
{
"",
wire.ShaHash{},
nil,
},
// Single digit hash.
{
"1",
wire.ShaHash([wire.HashSize]byte{ // Make go vet happy.
0x01, 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,
}),
nil,
},
// Block 203707 with stripped leading zeros.
{
"3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc",
wire.ShaHash([wire.HashSize]byte{ // Make go vet happy.
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,
}),
nil,
},
// Hash string that is too long.
{
"01234567890123456789012345678901234567890123456789012345678912345",
wire.ShaHash{},
wire.ErrHashStrSize,
},
// Hash string that is contains non-hex chars.
{
"abcdefg",
wire.ShaHash{},
hex.InvalidByteError('g'),
},
}
unexpectedErrStr := "NewShaHashFromStr #%d failed to detect expected error - got: %v want: %v"
unexpectedResultStr := "NewShaHashFromStr #%d got: %v want: %v"
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result, err := wire.NewShaHashFromStr(test.in)
if err != test.err {
t.Errorf(unexpectedErrStr, i, err, test.err)
continue
} else if err != nil {
// Got expected error. Move on to the next test.
continue
}
if !test.want.IsEqual(result) {
t.Errorf(unexpectedResultStr, i, result, &test.want)
continue
}
}
}