Merge pull request #2073 from Roasbeef/wire-opts

wire: only borrow/return binaryFreeList buffers at the message level
This commit is contained in:
Olaoluwa Osuntokun 2023-12-28 18:52:53 -08:00 committed by GitHub
commit 16684f6cbc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1289 additions and 370 deletions

View file

@ -8,6 +8,7 @@ import (
"bytes"
"compress/bzip2"
"fmt"
"io"
"io/ioutil"
"net"
"os"
@ -63,38 +64,48 @@ var genesisCoinbaseTx = MsgTx{
// BenchmarkWriteVarInt1 performs a benchmark on how long it takes to write
// a single byte variable length integer.
func BenchmarkWriteVarInt1(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 1)
WriteVarInt(io.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) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 65535)
WriteVarInt(io.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) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 4294967295)
WriteVarInt(io.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) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
WriteVarInt(ioutil.Discard, 0, 18446744073709551615)
WriteVarInt(io.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) {
b.ReportAllocs()
buf := []byte{0x01}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
@ -106,6 +117,8 @@ func BenchmarkReadVarInt1(b *testing.B) {
// BenchmarkReadVarInt3 performs a benchmark on how long it takes to read
// a three byte variable length integer.
func BenchmarkReadVarInt3(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x0fd, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
@ -117,6 +130,8 @@ func BenchmarkReadVarInt3(b *testing.B) {
// BenchmarkReadVarInt5 performs a benchmark on how long it takes to read
// a five byte variable length integer.
func BenchmarkReadVarInt5(b *testing.B) {
b.ReportAllocs()
buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
@ -128,6 +143,8 @@ func BenchmarkReadVarInt5(b *testing.B) {
// BenchmarkReadVarInt9 performs a benchmark on how long it takes to read
// a nine byte variable length integer.
func BenchmarkReadVarInt9(b *testing.B) {
b.ReportAllocs()
buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
@ -136,9 +153,119 @@ func BenchmarkReadVarInt9(b *testing.B) {
}
}
// BenchmarkWriteVarIntBuf1 performs a benchmark on how long it takes to write
// a single byte variable length integer.
func BenchmarkWriteVarIntBuf1(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
for i := 0; i < b.N; i++ {
WriteVarIntBuf(io.Discard, 0, 1, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkWriteVarIntBuf3 performs a benchmark on how long it takes to write
// a three byte variable length integer.
func BenchmarkWriteVarIntBuf3(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
for i := 0; i < b.N; i++ {
WriteVarIntBuf(io.Discard, 0, 65535, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkWriteVarIntBuf5 performs a benchmark on how long it takes to write
// a five byte variable length integer.
func BenchmarkWriteVarIntBuf5(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
for i := 0; i < b.N; i++ {
WriteVarIntBuf(io.Discard, 0, 4294967295, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkWriteVarIntBuf9 performs a benchmark on how long it takes to write
// a nine byte variable length integer.
func BenchmarkWriteVarIntBuf9(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
for i := 0; i < b.N; i++ {
WriteVarIntBuf(io.Discard, 0, 18446744073709551615, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkReadVarIntBuf1 performs a benchmark on how long it takes to read
// a single byte variable length integer.
func BenchmarkReadVarIntBuf1(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{0x01}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarIntBuf(r, 0, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkReadVarIntBuf3 performs a benchmark on how long it takes to read
// a three byte variable length integer.
func BenchmarkReadVarIntBuf3(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{0x0fd, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarIntBuf(r, 0, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkReadVarIntBuf5 performs a benchmark on how long it takes to read
// a five byte variable length integer.
func BenchmarkReadVarIntBuf5(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarIntBuf(r, 0, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkReadVarIntBuf9 performs a benchmark on how long it takes to read
// a nine byte variable length integer.
func BenchmarkReadVarIntBuf9(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadVarIntBuf(r, 0, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkReadVarStr4 performs a benchmark on how long it takes to read a
// four byte variable length string.
func BenchmarkReadVarStr4(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x04, 't', 'e', 's', 't'}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
@ -150,6 +277,8 @@ func BenchmarkReadVarStr4(b *testing.B) {
// BenchmarkReadVarStr10 performs a benchmark on how long it takes to read a
// ten byte variable length string.
func BenchmarkReadVarStr10(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
@ -161,22 +290,83 @@ func BenchmarkReadVarStr10(b *testing.B) {
// BenchmarkWriteVarStr4 performs a benchmark on how long it takes to write a
// four byte variable length string.
func BenchmarkWriteVarStr4(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
WriteVarString(ioutil.Discard, 0, "test")
WriteVarString(io.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) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
WriteVarString(ioutil.Discard, 0, "test012345")
WriteVarString(io.Discard, 0, "test012345")
}
}
// BenchmarkReadVarStrBuf4 performs a benchmark on how long it takes to read a
// four byte variable length string.
func BenchmarkReadVarStrBuf4(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{0x04, 't', 'e', 's', 't'}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readVarStringBuf(r, 0, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkReadVarStrBuf10 performs a benchmark on how long it takes to read a
// ten byte variable length string.
func BenchmarkReadVarStrBuf10(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'}
r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readVarStringBuf(r, 0, buf)
}
binarySerializer.Return(buffer)
}
// BenchmarkWriteVarStrBuf4 performs a benchmark on how long it takes to write a
// four byte variable length string.
func BenchmarkWriteVarStrBuf4(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
for i := 0; i < b.N; i++ {
writeVarStringBuf(io.Discard, 0, "test", buf)
}
binarySerializer.Return(buf)
}
// BenchmarkWriteVarStrBuf10 performs a benchmark on how long it takes to write
// a ten byte variable length string.
func BenchmarkWriteVarStrBuf10(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
for i := 0; i < b.N; i++ {
writeVarStringBuf(io.Discard, 0, "test012345", buf)
}
binarySerializer.Return(buf)
}
// BenchmarkReadOutPoint performs a benchmark on how long it takes to read a
// transaction output point.
func BenchmarkReadOutPoint(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -188,25 +378,46 @@ func BenchmarkReadOutPoint(b *testing.B) {
var op OutPoint
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readOutPoint(r, 0, 0, &op)
readOutPointBuf(r, 0, 0, &op, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkWriteOutPoint performs a benchmark on how long it takes to write a
// transaction output point.
func BenchmarkWriteOutPoint(b *testing.B) {
b.ReportAllocs()
op := &OutPoint{
Hash: chainhash.Hash{},
Index: 0,
}
for i := 0; i < b.N; i++ {
WriteOutPoint(ioutil.Discard, 0, 0, op)
WriteOutPoint(io.Discard, 0, 0, op)
}
}
// BenchmarkWriteOutPointBuf performs a benchmark on how long it takes to write a
// transaction output point.
func BenchmarkWriteOutPointBuf(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
op := &OutPoint{
Hash: chainhash.Hash{},
Index: 0,
}
for i := 0; i < b.N; i++ {
writeOutPointBuf(io.Discard, 0, 0, op, buf)
}
binarySerializer.Return(buf)
}
// BenchmarkReadTxOut performs a benchmark on how long it takes to read a
// transaction output.
func BenchmarkReadTxOut(b *testing.B) {
b.ReportAllocs()
buf := []byte{
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
@ -227,22 +438,74 @@ func BenchmarkReadTxOut(b *testing.B) {
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
ReadTxOut(r, 0, 0, &txOut)
scriptPool.Return(txOut.PkScript)
}
}
// BenchmarkReadTxOutBuf performs a benchmark on how long it takes to read a
// transaction output.
func BenchmarkReadTxOutBuf(b *testing.B) {
b.ReportAllocs()
scriptBuffer := scriptPool.Borrow()
sbuf := scriptBuffer[:]
buffer := binarySerializer.Borrow()
buf := []byte{
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
0xee, // 65-byte signature
0xac, // OP_CHECKSIG
}
r := bytes.NewReader(buf)
var txOut TxOut
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readTxOutBuf(r, 0, 0, &txOut, buffer, sbuf)
}
binarySerializer.Return(buffer)
scriptPool.Return(scriptBuffer)
}
// BenchmarkWriteTxOut performs a benchmark on how long it takes to write
// a transaction output.
func BenchmarkWriteTxOut(b *testing.B) {
b.ReportAllocs()
txOut := blockOne.Transactions[0].TxOut[0]
for i := 0; i < b.N; i++ {
WriteTxOut(ioutil.Discard, 0, 0, txOut)
WriteTxOut(io.Discard, 0, 0, txOut)
}
}
// BenchmarkWriteTxOutBuf performs a benchmark on how long it takes to write
// a transaction output.
func BenchmarkWriteTxOutBuf(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
txOut := blockOne.Transactions[0].TxOut[0]
for i := 0; i < b.N; i++ {
WriteTxOutBuf(io.Discard, 0, 0, txOut, buf)
}
binarySerializer.Return(buf)
}
// BenchmarkReadTxIn performs a benchmark on how long it takes to read a
// transaction input.
func BenchmarkReadTxIn(b *testing.B) {
b.ReportAllocs()
scriptBuffer := scriptPool.Borrow()
sbuf := scriptBuffer[:]
buffer := binarySerializer.Borrow()
buf := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -257,18 +520,23 @@ func BenchmarkReadTxIn(b *testing.B) {
var txIn TxIn
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readTxIn(r, 0, 0, &txIn)
scriptPool.Return(txIn.SignatureScript)
readTxInBuf(r, 0, 0, &txIn, buffer, sbuf)
}
binarySerializer.Return(buffer)
scriptPool.Return(scriptBuffer)
}
// BenchmarkWriteTxIn performs a benchmark on how long it takes to write
// a transaction input.
func BenchmarkWriteTxIn(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
txIn := blockOne.Transactions[0].TxIn[0]
for i := 0; i < b.N; i++ {
writeTxIn(ioutil.Discard, 0, 0, txIn)
writeTxInBuf(io.Discard, 0, 0, txIn, buf)
}
binarySerializer.Return(buf)
}
// BenchmarkDeserializeTx performs a benchmark on how long it takes to
@ -302,6 +570,9 @@ func BenchmarkDeserializeTxSmall(b *testing.B) {
0x00, 0x00, 0x00, 0x00, // Lock time
}
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf)
var tx MsgTx
for i := 0; i < b.N; i++ {
@ -313,6 +584,7 @@ func BenchmarkDeserializeTxSmall(b *testing.B) {
// BenchmarkDeserializeTxLarge performs a benchmark on how long it takes to
// deserialize a very large transaction.
func BenchmarkDeserializeTxLarge(b *testing.B) {
// tx bb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e08
// from the main block chain.
fi, err := os.Open("testdata/megatx.bin.bz2")
@ -325,6 +597,9 @@ func BenchmarkDeserializeTxLarge(b *testing.B) {
b.Fatalf("Failed to read transaction data: %v", err)
}
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf)
var tx MsgTx
for i := 0; i < b.N; i++ {
@ -333,19 +608,132 @@ func BenchmarkDeserializeTxLarge(b *testing.B) {
}
}
func BenchmarkDeserializeBlock(b *testing.B) {
buf, err := os.ReadFile(
"testdata/block-00000000000000000021868c2cefc52a480d173c849412fe81c4e5ab806f94ab.blk",
)
if err != nil {
b.Fatalf("Failed to read block data: %v", err)
}
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf)
var block MsgBlock
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
block.Deserialize(r)
}
}
func BenchmarkSerializeBlock(b *testing.B) {
buf, err := os.ReadFile(
"testdata/block-00000000000000000021868c2cefc52a480d173c849412fe81c4e5ab806f94ab.blk",
)
if err != nil {
b.Fatalf("Failed to read block data: %v", err)
}
var block MsgBlock
err = block.Deserialize(bytes.NewReader(buf))
if err != nil {
panic(err.Error())
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
block.Serialize(io.Discard)
}
}
// BenchmarkSerializeTx performs a benchmark on how long it takes to serialize
// a transaction.
func BenchmarkSerializeTx(b *testing.B) {
b.ReportAllocs()
tx := blockOne.Transactions[0]
for i := 0; i < b.N; i++ {
tx.Serialize(ioutil.Discard)
tx.Serialize(io.Discard)
}
}
// BenchmarkSerializeTxSmall performs a benchmark on how long it takes to
// serialize a transaction.
func BenchmarkSerializeTxSmall(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
tx.Deserialize(bytes.NewReader(buf))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tx.Serialize(io.Discard)
}
}
// BenchmarkSerializeTxLarge performs a benchmark on how long it takes to
// serialize a transaction.
func BenchmarkSerializeTxLarge(b *testing.B) {
// tx bb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e08
// from the main block chain.
fi, err := os.Open("testdata/megatx.bin.bz2")
if err != nil {
b.Fatalf("Failed to read transaction data: %v", err)
}
defer fi.Close()
buf, err := ioutil.ReadAll(bzip2.NewReader(fi))
if err != nil {
b.Fatalf("Failed to read transaction data: %v", err)
}
var tx MsgTx
tx.Deserialize(bytes.NewReader(buf))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tx.Serialize(io.Discard)
}
}
// BenchmarkReadBlockHeader performs a benchmark on how long it takes to
// deserialize a block header.
func BenchmarkReadBlockHeader(b *testing.B) {
b.ReportAllocs()
buf := []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
@ -369,18 +757,65 @@ func BenchmarkReadBlockHeader(b *testing.B) {
}
}
// BenchmarkReadBlockHeaderBuf performs a benchmark on how long it takes to
// deserialize a block header.
func BenchmarkReadBlockHeaderBuf(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot
0x29, 0xab, 0x5f, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0xf3, 0xe0, 0x01, 0x00, // Nonce
0x00, // TxnCount Varint
}
r := bytes.NewReader(buf)
var header BlockHeader
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readBlockHeaderBuf(r, 0, &header, buffer)
}
binarySerializer.Return(buffer)
}
// BenchmarkWriteBlockHeader performs a benchmark on how long it takes to
// serialize a block header.
func BenchmarkWriteBlockHeader(b *testing.B) {
b.ReportAllocs()
header := blockOne.Header
for i := 0; i < b.N; i++ {
writeBlockHeader(ioutil.Discard, 0, &header)
writeBlockHeader(io.Discard, 0, &header)
}
}
// BenchmarkWriteBlockHeaderBuf performs a benchmark on how long it takes to
// serialize a block header.
func BenchmarkWriteBlockHeaderBuf(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
header := blockOne.Header
for i := 0; i < b.N; i++ {
writeBlockHeaderBuf(io.Discard, 0, &header, buf)
}
binarySerializer.Return(buf)
}
// BenchmarkDecodeGetHeaders performs a benchmark on how long it takes to
// decode a getheaders message with the maximum number of block locator hashes.
func BenchmarkDecodeGetHeaders(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of block locators.
pver := ProtocolVersion
var m MsgGetHeaders
@ -411,6 +846,8 @@ func BenchmarkDecodeGetHeaders(b *testing.B) {
// BenchmarkDecodeHeaders performs a benchmark on how long it takes to
// decode a headers message with the maximum number of headers.
func BenchmarkDecodeHeaders(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of headers.
pver := ProtocolVersion
var m MsgHeaders
@ -441,6 +878,8 @@ func BenchmarkDecodeHeaders(b *testing.B) {
// BenchmarkDecodeGetBlocks performs a benchmark on how long it takes to
// decode a getblocks message with the maximum number of block locator hashes.
func BenchmarkDecodeGetBlocks(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of block locators.
pver := ProtocolVersion
var m MsgGetBlocks
@ -471,6 +910,8 @@ func BenchmarkDecodeGetBlocks(b *testing.B) {
// BenchmarkDecodeAddr performs a benchmark on how long it takes to decode an
// addr message with the maximum number of addresses.
func BenchmarkDecodeAddr(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of addresses.
pver := ProtocolVersion
ip := net.ParseIP("127.0.0.1")
@ -516,6 +957,9 @@ func BenchmarkDecodeInv(b *testing.B) {
}
buf := bb.Bytes()
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf)
var msg MsgInv
b.ResetTimer()
@ -528,6 +972,8 @@ func BenchmarkDecodeInv(b *testing.B) {
// BenchmarkDecodeNotFound performs a benchmark on how long it takes to decode
// a notfound message with the maximum number of entries.
func BenchmarkDecodeNotFound(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of entries.
pver := ProtocolVersion
var m MsgNotFound
@ -558,6 +1004,8 @@ func BenchmarkDecodeNotFound(b *testing.B) {
// BenchmarkDecodeMerkleBlock performs a benchmark on how long it takes to
// decode a reasonably sized merkleblock message.
func BenchmarkDecodeMerkleBlock(b *testing.B) {
b.ReportAllocs()
// Create a message with random data.
pver := ProtocolVersion
var m MsgMerkleBlock
@ -596,6 +1044,8 @@ func BenchmarkDecodeMerkleBlock(b *testing.B) {
// BenchmarkTxHash performs a benchmark on how long it takes to hash a
// transaction.
func BenchmarkTxHash(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
genesisCoinbaseTx.TxHash()
}
@ -604,6 +1054,8 @@ func BenchmarkTxHash(b *testing.B) {
// BenchmarkDoubleHashB performs a benchmark on how long it takes to perform a
// double hash returning a byte slice.
func BenchmarkDoubleHashB(b *testing.B) {
b.ReportAllocs()
var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err)
@ -620,6 +1072,8 @@ func BenchmarkDoubleHashB(b *testing.B) {
// BenchmarkDoubleHashH performs a benchmark on how long it takes to perform
// a double hash returning a chainhash.Hash.
func BenchmarkDoubleHashH(b *testing.B) {
b.ReportAllocs()
var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err)

View file

@ -107,16 +107,109 @@ func NewBlockHeader(version int32, prevHash, merkleRootHash *chainhash.Hash,
// 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.
//
// DEPRECATED: Use readBlockHeaderBuf instead.
func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
return readElements(r, &bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
(*uint32Time)(&bh.Timestamp), &bh.Bits, &bh.Nonce)
buf := binarySerializer.Borrow()
err := readBlockHeaderBuf(r, pver, bh, buf)
binarySerializer.Return(buf)
return err
}
// readBlockHeaderBuf 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.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readBlockHeaderBuf(r io.Reader, pver uint32, bh *BlockHeader,
buf []byte) error {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
bh.Version = int32(littleEndian.Uint32(buf[:4]))
if _, err := io.ReadFull(r, bh.PrevBlock[:]); err != nil {
return err
}
if _, err := io.ReadFull(r, bh.MerkleRoot[:]); err != nil {
return err
}
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
bh.Timestamp = time.Unix(int64(littleEndian.Uint32(buf[:4])), 0)
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
bh.Bits = littleEndian.Uint32(buf[:4])
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
bh.Nonce = littleEndian.Uint32(buf[:4])
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.
//
// DEPRECATED: Use writeBlockHeaderBuf instead.
func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
sec := uint32(bh.Timestamp.Unix())
return writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
sec, bh.Bits, bh.Nonce)
buf := binarySerializer.Borrow()
err := writeBlockHeaderBuf(w, pver, bh, buf)
binarySerializer.Return(buf)
return err
}
// writeBlockHeaderBuf 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.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func writeBlockHeaderBuf(w io.Writer, pver uint32, bh *BlockHeader,
buf []byte) error {
littleEndian.PutUint32(buf[:4], uint32(bh.Version))
if _, err := w.Write(buf[:4]); err != nil {
return err
}
if _, err := w.Write(bh.PrevBlock[:]); err != nil {
return err
}
if _, err := w.Write(bh.MerkleRoot[:]); err != nil {
return err
}
littleEndian.PutUint32(buf[:4], uint32(bh.Timestamp.Unix()))
if _, err := w.Write(buf[:4]); err != nil {
return err
}
littleEndian.PutUint32(buf[:4], bh.Bits)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
littleEndian.PutUint32(buf[:4], bh.Nonce)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
return nil
}

View file

@ -73,12 +73,13 @@ func (l binaryFreeList) Return(buf []byte) {
// free list and returns it as a uint8.
func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
buf := l.Borrow()[:1]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := buf[0]
l.Return(buf)
return rv, nil
}
@ -87,12 +88,13 @@ func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
// the resulting uint16.
func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) {
buf := l.Borrow()[:2]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := byteOrder.Uint16(buf)
l.Return(buf)
return rv, nil
}
@ -101,12 +103,13 @@ func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16,
// the resulting uint32.
func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
buf := l.Borrow()[:4]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := byteOrder.Uint32(buf)
l.Return(buf)
return rv, nil
}
@ -115,12 +118,13 @@ func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32,
// the resulting uint64.
func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) {
buf := l.Borrow()[:8]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err
}
rv := byteOrder.Uint64(buf)
l.Return(buf)
return rv, nil
}
@ -128,9 +132,11 @@ func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64,
// writes the resulting byte to the given writer.
func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
buf := l.Borrow()[:1]
defer l.Return(buf)
buf[0] = val
_, err := w.Write(buf)
l.Return(buf)
return err
}
@ -139,9 +145,11 @@ func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
// writer.
func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error {
buf := l.Borrow()[:2]
defer l.Return(buf)
byteOrder.PutUint16(buf, val)
_, err := w.Write(buf)
l.Return(buf)
return err
}
@ -150,9 +158,11 @@ func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val u
// writer.
func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error {
buf := l.Borrow()[:4]
defer l.Return(buf)
byteOrder.PutUint32(buf, val)
_, err := w.Write(buf)
l.Return(buf)
return err
}
@ -161,9 +171,11 @@ func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val u
// writer.
func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error {
buf := l.Borrow()[:8]
defer l.Return(buf)
byteOrder.PutUint64(buf, val)
_, err := w.Write(buf)
l.Return(buf)
return err
}
@ -474,19 +486,30 @@ func writeElements(w io.Writer, elements ...interface{}) error {
// ReadVarInt reads a variable length integer from r and returns it as a uint64.
func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
discriminant, err := binarySerializer.Uint8(r)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
n, err := ReadVarIntBuf(r, pver, buf)
return n, err
}
// ReadVarIntBuf reads a variable length integer from r using a preallocated
// scratch buffer and returns it as a uint64.
//
// NOTE: buf MUST at least an 8-byte slice.
func ReadVarIntBuf(r io.Reader, pver uint32, buf []byte) (uint64, error) {
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return 0, err
}
discriminant := buf[0]
var rv uint64
switch discriminant {
case 0xff:
sv, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
if _, err := io.ReadFull(r, buf); err != nil {
return 0, err
}
rv = sv
rv = littleEndian.Uint64(buf)
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
@ -497,11 +520,10 @@ func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
}
case 0xfe:
sv, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return 0, err
}
rv = uint64(sv)
rv = uint64(littleEndian.Uint32(buf[:4]))
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
@ -512,11 +534,10 @@ func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
}
case 0xfd:
sv, err := binarySerializer.Uint16(r, littleEndian)
if err != nil {
if _, err := io.ReadFull(r, buf[:2]); err != nil {
return 0, err
}
rv = uint64(sv)
rv = uint64(littleEndian.Uint16(buf[:2]))
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
@ -536,31 +557,46 @@ func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
// WriteVarInt serializes val to w using a variable number of bytes depending
// on its value.
func WriteVarInt(w io.Writer, pver uint32, val uint64) error {
if val < 0xfd {
return binarySerializer.PutUint8(w, uint8(val))
}
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if val <= math.MaxUint16 {
err := binarySerializer.PutUint8(w, 0xfd)
if err != nil {
err := WriteVarIntBuf(w, pver, val, buf)
return err
}
// WriteVarIntBuf serializes val to w using a variable number of bytes depending
// on its value using a preallocated scratch buffer.
//
// NOTE: buf MUST at least an 8-byte slice.
func WriteVarIntBuf(w io.Writer, pver uint32, val uint64, buf []byte) error {
switch {
case val < 0xfd:
buf[0] = uint8(val)
_, err := w.Write(buf[:1])
return err
case val <= math.MaxUint16:
buf[0] = 0xfd
littleEndian.PutUint16(buf[1:3], uint16(val))
_, err := w.Write(buf[:3])
return err
case val <= math.MaxUint32:
buf[0] = 0xfe
littleEndian.PutUint32(buf[1:5], uint32(val))
_, err := w.Write(buf[:5])
return err
default:
buf[0] = 0xff
if _, err := w.Write(buf[:1]); err != nil {
return err
}
return binarySerializer.PutUint16(w, littleEndian, uint16(val))
}
if val <= math.MaxUint32 {
err := binarySerializer.PutUint8(w, 0xfe)
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, uint32(val))
}
err := binarySerializer.PutUint8(w, 0xff)
if err != nil {
littleEndian.PutUint64(buf, val)
_, err := w.Write(buf)
return err
}
return binarySerializer.PutUint64(w, littleEndian, val)
}
// VarIntSerializeSize returns the number of bytes it would take to serialize
@ -593,7 +629,27 @@ func VarIntSerializeSize(val uint64) int {
// maximum block payload size since it helps protect against memory exhaustion
// attacks and forced panics through malformed messages.
func ReadVarString(r io.Reader, pver uint32) (string, error) {
count, err := ReadVarInt(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
str, err := readVarStringBuf(r, pver, buf)
return str, err
}
// readVarStringBuf reads a variable length string from r and returns it as a Go
// string. A variable length string is encoded as a variable length integer
// containing the length of the string followed by the bytes that represent the
// string itself. An error is returned if the length is greater than the
// maximum block payload size since it helps protect against memory exhaustion
// attacks and forced panics through malformed messages.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readVarStringBuf(r io.Reader, pver uint32, buf []byte) (string, error) {
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return "", err
}
@ -607,22 +663,40 @@ func ReadVarString(r io.Reader, pver uint32) (string, error) {
return "", messageError("ReadVarString", str)
}
buf := make([]byte, count)
_, err = io.ReadFull(r, buf)
str := make([]byte, count)
_, err = io.ReadFull(r, str)
if err != nil {
return "", err
}
return string(buf), nil
return string(str), nil
}
// WriteVarString serializes str to w as a variable length integer containing
// the length of the string followed by the bytes that represent the string
// itself.
func WriteVarString(w io.Writer, pver uint32, str string) error {
err := WriteVarInt(w, pver, uint64(len(str)))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeVarStringBuf(w, pver, str, buf)
return err
}
// writeVarStringBuf serializes str to w as a variable length integer containing
// the length of the string followed by the bytes that represent the string
// itself.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func writeVarStringBuf(w io.Writer, pver uint32, str string, buf []byte) error {
err := WriteVarIntBuf(w, pver, uint64(len(str)), buf)
if err != nil {
return err
}
_, err = w.Write([]byte(str))
return err
}
@ -637,7 +711,26 @@ func WriteVarString(w io.Writer, pver uint32, str string) error {
func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
fieldName string) ([]byte, error) {
count, err := ReadVarInt(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
b, err := ReadVarBytesBuf(r, pver, buf, maxAllowed, fieldName)
return b, err
}
// ReadVarBytesBuf 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 exhaustion
// attacks and forced panics through malformed messages. The fieldName
// parameter is only used for the error message so it provides more context in
// the error. If b is non-nil, the provided buffer will be used for serializing
// small values. Otherwise a buffer will be drawn from the binarySerializer's
// pool and return when the method finishes.
func ReadVarBytesBuf(r io.Reader, pver uint32, buf []byte, maxAllowed uint32,
fieldName string) ([]byte, error) {
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return nil, err
}
@ -651,19 +744,33 @@ func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
return nil, messageError("ReadVarBytes", str)
}
b := make([]byte, count)
_, err = io.ReadFull(r, b)
bytes := make([]byte, count)
_, err = io.ReadFull(r, bytes)
if err != nil {
return nil, err
}
return b, nil
return bytes, nil
}
// WriteVarBytes serializes a variable length byte array to w as a varInt
// containing the number of bytes, followed by the bytes themselves.
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := WriteVarBytesBuf(w, pver, bytes, buf)
return err
}
// WriteVarBytesBuf serializes a variable length byte array to w as a varInt
// containing the number of bytes, followed by the bytes themselves. If b is
// non-nil, the provided buffer will be used for serializing small values.
// Otherwise a buffer will be drawn from the binarySerializer's pool and return
// when the method finishes.
func WriteVarBytesBuf(w io.Writer, pver uint32, bytes, buf []byte) error {
slen := uint64(len(bytes))
err := WriteVarInt(w, pver, slen)
err := WriteVarIntBuf(w, pver, slen, buf)
if err != nil {
return err
}

View file

@ -74,13 +74,37 @@ func NewInvVect(typ InvType, hash *chainhash.Hash) *InvVect {
}
}
// readInvVect reads an encoded InvVect from r depending on the protocol
// readInvVectBuf reads an encoded InvVect from r depending on the protocol
// version.
func readInvVect(r io.Reader, pver uint32, iv *InvVect) error {
return readElements(r, &iv.Type, &iv.Hash)
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readInvVectBuf(r io.Reader, pver uint32, iv *InvVect, buf []byte) error {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
iv.Type = InvType(littleEndian.Uint32(buf[:4]))
_, err := io.ReadFull(r, iv.Hash[:])
return err
}
// writeInvVect serializes an InvVect to w depending on the protocol version.
func writeInvVect(w io.Writer, pver uint32, iv *InvVect) error {
return writeElements(w, iv.Type, &iv.Hash)
// writeInvVectBuf serializes an InvVect to w depending on the protocol version.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func writeInvVectBuf(w io.Writer, pver uint32, iv *InvVect, buf []byte) error {
littleEndian.PutUint32(buf[:4], uint32(iv.Type))
if _, err := w.Write(buf[:4]); err != nil {
return err
}
_, err := w.Write(iv.Hash[:])
return err
}

View file

@ -238,10 +238,11 @@ func TestInvVectWire(t *testing.T) {
}
t.Logf("Running %d tests", len(tests))
var b [8]byte
for i, test := range tests {
// Encode to wire format.
var buf bytes.Buffer
err := writeInvVect(&buf, test.pver, &test.in)
err := writeInvVectBuf(&buf, test.pver, &test.in, b[:])
if err != nil {
t.Errorf("writeInvVect #%d error %v", i, err)
continue
@ -255,7 +256,7 @@ func TestInvVectWire(t *testing.T) {
// Decode the message from wire format.
var iv InvVect
rbuf := bytes.NewReader(test.buf)
err = readInvVect(rbuf, test.pver, &iv)
err = readInvVectBuf(rbuf, test.pver, &iv, b[:])
if err != nil {
t.Errorf("readInvVect #%d error %v", i, err)
continue

View file

@ -62,12 +62,15 @@ func (msg *MsgBlock) ClearTransactions() {
// See Deserialize for decoding blocks stored to disk, such as in a database, as
// opposed to decoding blocks from the wire.
func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
err := readBlockHeader(r, pver, &msg.Header)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := readBlockHeaderBuf(r, pver, &msg.Header, buf)
if err != nil {
return err
}
txCount, err := ReadVarInt(r, pver)
txCount, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -81,10 +84,13 @@ func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) er
return messageError("MsgBlock.BtcDecode", str)
}
scriptBuf := scriptPool.Borrow()
defer scriptPool.Return(scriptBuf)
msg.Transactions = make([]*MsgTx, 0, txCount)
for i := uint64(0); i < txCount; i++ {
tx := MsgTx{}
err := tx.BtcDecode(r, pver, enc)
err := tx.btcDecode(r, pver, enc, buf, scriptBuf[:])
if err != nil {
return err
}
@ -129,15 +135,18 @@ func (msg *MsgBlock) DeserializeNoWitness(r io.Reader) error {
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
fullLen := r.Len()
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// 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)
err := readBlockHeaderBuf(r, 0, &msg.Header, buf)
if err != nil {
return nil, err
}
txCount, err := ReadVarInt(r, 0)
txCount, err := ReadVarIntBuf(r, 0, buf)
if err != nil {
return nil, err
}
@ -151,6 +160,9 @@ func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
return nil, messageError("MsgBlock.DeserializeTxLoc", str)
}
scriptBuf := scriptPool.Borrow()
defer scriptPool.Return(scriptBuf)
// Deserialize each transaction while keeping track of its location
// within the byte stream.
msg.Transactions = make([]*MsgTx, 0, txCount)
@ -158,7 +170,7 @@ func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
for i := uint64(0); i < txCount; i++ {
txLocs[i].TxStart = fullLen - r.Len()
tx := MsgTx{}
err := tx.Deserialize(r)
err := tx.btcDecode(r, 0, WitnessEncoding, buf, scriptBuf[:])
if err != nil {
return nil, err
}
@ -174,18 +186,21 @@ func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
// See Serialize for encoding blocks to be stored to disk, such as in a
// database, as opposed to encoding blocks for the wire.
func (msg *MsgBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
err := writeBlockHeader(w, pver, &msg.Header)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeBlockHeaderBuf(w, pver, &msg.Header, buf)
if err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(len(msg.Transactions)))
err = WriteVarIntBuf(w, pver, uint64(len(msg.Transactions)), buf)
if err != nil {
return err
}
for _, tx := range msg.Transactions {
err = tx.BtcEncode(w, pver, enc)
err = tx.btcEncode(w, pver, enc, buf)
if err != nil {
return err
}

View file

@ -52,20 +52,22 @@ func (msg *MsgCFCheckpt) AddCFHeader(header *chainhash.Hash) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Read filter type
err := readElement(r, &msg.FilterType)
if err != nil {
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.FilterType = FilterType(buf[0])
// Read stop hash
err = readElement(r, &msg.StopHash)
if err != nil {
if _, err := io.ReadFull(r, msg.StopHash[:]); err != nil {
return err
}
// Read number of filter headers
count, err := ReadVarInt(r, pver)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -80,7 +82,7 @@ func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding)
msg.FilterHeaders = make([]*chainhash.Hash, count)
for i := uint64(0); i < count; i++ {
var cfh chainhash.Hash
err := readElement(r, &cfh)
_, err := io.ReadFull(r, cfh[:])
if err != nil {
return err
}
@ -93,27 +95,29 @@ func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding)
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Write filter type
err := writeElement(w, msg.FilterType)
if err != nil {
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
// Write stop hash
err = writeElement(w, msg.StopHash)
if err != nil {
if _, err := w.Write(msg.StopHash[:]); err != nil {
return err
}
// Write length of FilterHeaders slice
count := len(msg.FilterHeaders)
err = WriteVarInt(w, pver, uint64(count))
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, cfh := range msg.FilterHeaders {
err := writeElement(w, cfh)
_, err := w.Write(cfh[:])
if err != nil {
return err
}

View file

@ -48,26 +48,27 @@ func (msg *MsgCFHeaders) AddCFHash(hash *chainhash.Hash) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Read filter type
err := readElement(r, &msg.FilterType)
if err != nil {
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.FilterType = FilterType(buf[0])
// Read stop hash
err = readElement(r, &msg.StopHash)
if err != nil {
if _, err := io.ReadFull(r, msg.StopHash[:]); err != nil {
return err
}
// Read prev filter header
err = readElement(r, &msg.PrevFilterHeader)
if err != nil {
if _, err := io.ReadFull(r, msg.PrevFilterHeader[:]); err != nil {
return err
}
// Read number of filter headers
count, err := ReadVarInt(r, pver)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -85,7 +86,7 @@ func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding)
msg.FilterHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
var cfh chainhash.Hash
err := readElement(r, &cfh)
_, err := io.ReadFull(r, cfh[:])
if err != nil {
return err
}
@ -98,25 +99,6 @@ func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding)
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
// Write filter type
err := writeElement(w, msg.FilterType)
if err != nil {
return err
}
// Write stop hash
err = writeElement(w, msg.StopHash)
if err != nil {
return err
}
// Write prev filter header
err = writeElement(w, msg.PrevFilterHeader)
if err != nil {
return err
}
// Limit to max committed headers per message.
count := len(msg.FilterHashes)
if count > MaxCFHeadersPerMsg {
str := fmt.Sprintf("too many committed filter headers for "+
@ -125,13 +107,32 @@ func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding)
return messageError("MsgCFHeaders.BtcEncode", str)
}
err = WriteVarInt(w, pver, uint64(count))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Write filter type
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
// Write stop hash
if _, err := w.Write(msg.StopHash[:]); err != nil {
return err
}
// Write prev filter header
if _, err := w.Write(msg.PrevFilterHeader[:]); err != nil {
return err
}
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, cfh := range msg.FilterHashes {
err := writeElement(w, cfh)
_, err := w.Write(cfh[:])
if err != nil {
return err
}

View file

@ -38,19 +38,22 @@ type MsgCFilter struct {
// This is part of the Message interface implementation.
func (msg *MsgCFilter) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
// Read filter type
err := readElement(r, &msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.FilterType = FilterType(buf[0])
// Read the hash of the filter's block
err = readElement(r, &msg.BlockHash)
if err != nil {
if _, err := io.ReadFull(r, msg.BlockHash[:]); err != nil {
return err
}
// Read filter data
msg.Data, err = ReadVarBytes(r, pver, MaxCFilterDataSize,
var err error
msg.Data, err = ReadVarBytesBuf(r, pver, buf, MaxCFilterDataSize,
"cfilter data")
return err
}
@ -65,17 +68,20 @@ func (msg *MsgCFilter) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) er
return messageError("MsgCFilter.BtcEncode", str)
}
err := writeElement(w, msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
err = writeElement(w, msg.BlockHash)
if err != nil {
if _, err := w.Write(msg.BlockHash[:]); err != nil {
return err
}
return WriteVarBytes(w, pver, msg.Data)
err := WriteVarBytesBuf(w, pver, msg.Data, buf)
return err
}
// Deserialize decodes a filter from r into the receiver using a format that is

View file

@ -51,16 +51,20 @@ func (msg *MsgGetBlocks) AddBlockLocatorHash(hash *chainhash.Hash) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
err := readElement(r, &msg.ProtocolVersion)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.ProtocolVersion = littleEndian.Uint32(buf[:4])
// Read num block locator hashes and limit to max.
count, err := ReadVarIntBuf(r, pver, buf)
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)
@ -73,14 +77,15 @@ func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding
msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
hash := &locatorHashes[i]
err := readElement(r, hash)
_, err := io.ReadFull(r, hash[:])
if err != nil {
return err
}
msg.AddBlockLocatorHash(hash)
}
return readElement(r, &msg.HashStop)
_, err = io.ReadFull(r, msg.HashStop[:])
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
@ -93,24 +98,28 @@ func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding
return messageError("MsgGetBlocks.BtcEncode", str)
}
err := writeElement(w, msg.ProtocolVersion)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
littleEndian.PutUint32(buf[:4], msg.ProtocolVersion)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(count))
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, hash := range msg.BlockLocatorHashes {
err = writeElement(w, hash)
_, err := w.Write(hash[:])
if err != nil {
return err
}
}
return writeElement(w, &msg.HashStop)
_, err = w.Write(msg.HashStop[:])
return err
}
// Command returns the protocol command string for the message. This is part

View file

@ -21,23 +21,31 @@ type MsgGetCFCheckpt struct {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
err := readElement(r, &msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.FilterType = FilterType(buf[0])
return readElement(r, &msg.StopHash)
_, err := io.ReadFull(r, msg.StopHash[:])
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
err := writeElement(w, msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
return writeElement(w, &msg.StopHash)
_, err := w.Write(msg.StopHash[:])
return err
}
// Command returns the protocol command string for the message. This is part

View file

@ -22,33 +22,41 @@ type MsgGetCFHeaders struct {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
err := readElement(r, &msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.FilterType = FilterType(buf[0])
err = readElement(r, &msg.StartHeight)
if err != nil {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.StartHeight = littleEndian.Uint32(buf[:4])
return readElement(r, &msg.StopHash)
_, err := io.ReadFull(r, msg.StopHash[:])
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
err := writeElement(w, msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
err = writeElement(w, &msg.StartHeight)
if err != nil {
littleEndian.PutUint32(buf[:4], msg.StartHeight)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
return writeElement(w, &msg.StopHash)
_, err := w.Write(msg.StopHash[:])
return err
}
// Command returns the protocol command string for the message. This is part

View file

@ -26,33 +26,41 @@ type MsgGetCFilters struct {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetCFilters) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
err := readElement(r, &msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.FilterType = FilterType(buf[0])
err = readElement(r, &msg.StartHeight)
if err != nil {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.StartHeight = littleEndian.Uint32(buf[:4])
return readElement(r, &msg.StopHash)
_, err := io.ReadFull(r, msg.StopHash[:])
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetCFilters) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
err := writeElement(w, msg.FilterType)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
err = writeElement(w, &msg.StartHeight)
if err != nil {
littleEndian.PutUint32(buf[:4], msg.StartHeight)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
return writeElement(w, &msg.StopHash)
_, err := w.Write(msg.StopHash[:])
return err
}
// Command returns the protocol command string for the message. This is part

View file

@ -38,7 +38,10 @@ func (msg *MsgGetData) AddInvVect(iv *InvVect) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -55,7 +58,7 @@ func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding)
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := &invList[i]
err := readInvVect(r, pver, iv)
err := readInvVectBuf(r, pver, iv, buf)
if err != nil {
return err
}
@ -75,13 +78,16 @@ func (msg *MsgGetData) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
return messageError("MsgGetData.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv)
err := writeInvVectBuf(w, pver, iv, buf)
if err != nil {
return err
}

View file

@ -48,16 +48,20 @@ func (msg *MsgGetHeaders) AddBlockLocatorHash(hash *chainhash.Hash) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
err := readElement(r, &msg.ProtocolVersion)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.ProtocolVersion = littleEndian.Uint32(buf[:4])
// Read num block locator hashes and limit to max.
count, err := ReadVarIntBuf(r, pver, buf)
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)
@ -70,14 +74,15 @@ func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncodin
msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
hash := &locatorHashes[i]
err := readElement(r, hash)
_, err := io.ReadFull(r, hash[:])
if err != nil {
return err
}
msg.AddBlockLocatorHash(hash)
}
return readElement(r, &msg.HashStop)
_, err = io.ReadFull(r, msg.HashStop[:])
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
@ -91,24 +96,28 @@ func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncodin
return messageError("MsgGetHeaders.BtcEncode", str)
}
err := writeElement(w, msg.ProtocolVersion)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
littleEndian.PutUint32(buf[:4], msg.ProtocolVersion)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(count))
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, hash := range msg.BlockLocatorHashes {
err := writeElement(w, hash)
_, err := w.Write(hash[:])
if err != nil {
return err
}
}
return writeElement(w, &msg.HashStop)
_, err = w.Write(msg.HashStop[:])
return err
}
// Command returns the protocol command string for the message. This is part

View file

@ -37,7 +37,10 @@ func (msg *MsgHeaders) AddBlockHeader(bh *BlockHeader) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -55,12 +58,12 @@ func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding)
msg.Headers = make([]*BlockHeader, 0, count)
for i := uint64(0); i < count; i++ {
bh := &headers[i]
err := readBlockHeader(r, pver, bh)
err := readBlockHeaderBuf(r, pver, bh, buf)
if err != nil {
return err
}
txCount, err := ReadVarInt(r, pver)
txCount, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -88,13 +91,16 @@ func (msg *MsgHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
return messageError("MsgHeaders.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, bh := range msg.Headers {
err := writeBlockHeader(w, pver, bh)
err := writeBlockHeaderBuf(w, pver, bh, buf)
if err != nil {
return err
}
@ -103,7 +109,7 @@ func (msg *MsgHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
// 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)
err = WriteVarIntBuf(w, pver, 0, buf)
if err != nil {
return err
}

View file

@ -46,7 +46,10 @@ func (msg *MsgInv) AddInvVect(iv *InvVect) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -63,7 +66,7 @@ func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) erro
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := &invList[i]
err := readInvVect(r, pver, iv)
err := readInvVectBuf(r, pver, iv, buf)
if err != nil {
return err
}
@ -83,13 +86,16 @@ func (msg *MsgInv) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) erro
return messageError("MsgInv.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv)
err := writeInvVectBuf(w, pver, iv, buf)
if err != nil {
return err
}

View file

@ -49,18 +49,21 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncodi
return messageError("MsgMerkleBlock.BtcDecode", str)
}
err := readBlockHeader(r, pver, &msg.Header)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := readBlockHeaderBuf(r, pver, &msg.Header, buf)
if err != nil {
return err
}
err = readElement(r, &msg.Transactions)
if err != nil {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.Transactions = littleEndian.Uint32(buf[:4])
// Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -76,14 +79,14 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncodi
msg.Hashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
hash := &hashes[i]
err := readElement(r, hash)
_, err := io.ReadFull(r, hash[:])
if err != nil {
return err
}
msg.AddTxHash(hash)
}
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock,
msg.Flags, err = ReadVarBytesBuf(r, pver, buf, maxFlagsPerMerkleBlock,
"merkle block flags size")
return err
}
@ -111,28 +114,32 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncodi
return messageError("MsgMerkleBlock.BtcDecode", str)
}
err := writeBlockHeader(w, pver, &msg.Header)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeBlockHeaderBuf(w, pver, &msg.Header, buf)
if err != nil {
return err
}
err = writeElement(w, msg.Transactions)
if err != nil {
littleEndian.PutUint32(buf[:4], msg.Transactions)
if _, err := w.Write(buf[:4]); err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(numHashes))
err = WriteVarIntBuf(w, pver, uint64(numHashes), buf)
if err != nil {
return err
}
for _, hash := range msg.Hashes {
err = writeElement(w, hash)
_, err := w.Write(hash[:])
if err != nil {
return err
}
}
return WriteVarBytes(w, pver, msg.Flags)
err = WriteVarBytesBuf(w, pver, msg.Flags, buf)
return err
}
// Command returns the protocol command string for the message. This is part

View file

@ -35,7 +35,10 @@ func (msg *MsgNotFound) AddInvVect(iv *InvVect) error {
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -52,7 +55,7 @@ func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding)
msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ {
iv := &invList[i]
err := readInvVect(r, pver, iv)
err := readInvVectBuf(r, pver, iv, buf)
if err != nil {
return err
}
@ -72,13 +75,16 @@ func (msg *MsgNotFound) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
return messageError("MsgNotFound.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
return err
}
for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv)
err := writeInvVectBuf(w, pver, iv, buf)
if err != nil {
return err
}

View file

@ -32,10 +32,11 @@ func (msg *MsgPing) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) err
// 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)
nonce, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
msg.Nonce = nonce
}
return nil
@ -48,7 +49,7 @@ func (msg *MsgPing) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) err
// 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)
err := binarySerializer.PutUint64(w, littleEndian, msg.Nonce)
if err != nil {
return err
}

View file

@ -31,7 +31,13 @@ func (msg *MsgPong) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) err
return messageError("MsgPong.BtcDecode", str)
}
return readElement(r, &msg.Nonce)
nonce, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
msg.Nonce = nonce
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
@ -45,7 +51,7 @@ func (msg *MsgPong) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) err
return messageError("MsgPong.BtcEncode", str)
}
return writeElement(w, msg.Nonce)
return binarySerializer.PutUint64(w, littleEndian, msg.Nonce)
}
// Command returns the protocol command string for the message. This is part

View file

@ -81,21 +81,24 @@ func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) e
}
// Command that was rejected.
cmd, err := ReadVarString(r, pver)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
cmd, err := readVarStringBuf(r, pver, buf)
if err != nil {
return err
}
msg.Cmd = cmd
// Code indicating why the command was rejected.
err = readElement(r, &msg.Code)
if err != nil {
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.Code = RejectCode(buf[0])
// Human readable string with specific details (over and above the
// reject code above) about why the command was rejected.
reason, err := ReadVarString(r, pver)
reason, err := readVarStringBuf(r, pver, buf)
if err != nil {
return err
}
@ -104,7 +107,7 @@ func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) e
// 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)
_, err := io.ReadFull(r, msg.Hash[:])
if err != nil {
return err
}
@ -123,20 +126,23 @@ func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) e
}
// Command that was rejected.
err := WriteVarString(w, pver, msg.Cmd)
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeVarStringBuf(w, pver, msg.Cmd, buf)
if err != nil {
return err
}
// Code indicating why the command was rejected.
err = writeElement(w, msg.Code)
if err != nil {
buf[0] = byte(msg.Code)
if _, err := w.Write(buf[:1]); 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)
err = writeVarStringBuf(w, pver, msg.Reason, buf)
if err != nil {
return err
}
@ -144,7 +150,7 @@ func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) e
// 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)
_, err := w.Write(msg.Hash[:])
if err != nil {
return err
}

View file

@ -93,7 +93,7 @@ const (
// scripts per transaction being simultaneously deserialized by 125
// peers. Thus, the peak usage of the free list is 12,500 * 512 =
// 6,400,000 bytes.
freeListMaxItems = 12500
freeListMaxItems = 125
// maxWitnessItemsPerInput is the maximum number of witness items to
// be read for the witness data for a single TxIn. This number is
@ -146,6 +146,10 @@ const (
WitnessFlag TxFlag = 0x01
)
const scriptSlabSize = 1 << 22
type scriptSlab [scriptSlabSize]byte
// scriptFreeList defines a free list of byte slices (up to the maximum number
// defined by the freeListMaxItems constant) that have a cap according to the
// freeListMaxScriptSize constant. It is used to provide temporary buffers for
@ -154,7 +158,7 @@ const (
//
// The caller can obtain a buffer from the free list by calling the Borrow
// function and should return it via the Return function when done using it.
type scriptFreeList chan []byte
type scriptFreeList chan *scriptSlab
// Borrow returns a byte slice from the free list with a length according the
// provided size. A new buffer is allocated if there are any items available.
@ -163,18 +167,14 @@ type scriptFreeList chan []byte
// a new buffer of the appropriate size is allocated and returned. It is safe
// to attempt to return said buffer via the Return function as it will be
// ignored and allowed to go the garbage collector.
func (c scriptFreeList) Borrow(size uint64) []byte {
if size > freeListMaxScriptSize {
return make([]byte, size)
}
var buf []byte
func (c scriptFreeList) Borrow() *scriptSlab {
var buf *scriptSlab
select {
case buf = <-c:
default:
buf = make([]byte, freeListMaxScriptSize)
buf = new(scriptSlab)
}
return buf[:size]
return buf
}
// Return puts the provided byte slice back on the free list when it has a cap
@ -182,13 +182,7 @@ func (c scriptFreeList) Borrow(size uint64) []byte {
// the Borrow function. Any slices that are not of the appropriate size, such
// as those whose size is greater than the largest allowed free list item size
// are simply ignored so they can go to the garbage collector.
func (c scriptFreeList) Return(buf []byte) {
// Ignore any buffers returned that aren't the expected size for the
// free list.
if cap(buf) != freeListMaxScriptSize {
return
}
func (c scriptFreeList) Return(buf *scriptSlab) {
// Return the buffer to the free list when it's not full. Otherwise let
// it be garbage collected.
select {
@ -201,7 +195,7 @@ func (c scriptFreeList) Return(buf []byte) {
// Create the concurrent safe free list to use for script deserialization. As
// previously described, this free list is maintained to significantly reduce
// the number of allocations.
var scriptPool scriptFreeList = make(chan []byte, freeListMaxItems)
var scriptPool = make(scriptFreeList, freeListMaxItems)
// OutPoint defines a bitcoin data type that is used to track previous
// transaction outputs.
@ -452,13 +446,25 @@ func (msg *MsgTx) Copy() *MsgTx {
// 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, enc MessageEncoding) error {
version, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
sbuf := scriptPool.Borrow()
defer scriptPool.Return(sbuf)
err := msg.btcDecode(r, pver, enc, buf, sbuf[:])
return err
}
func (msg *MsgTx) btcDecode(r io.Reader, pver uint32, enc MessageEncoding,
buf, sbuf []byte) error {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.Version = int32(version)
msg.Version = int32(littleEndian.Uint32(buf[:4]))
count, err := ReadVarInt(r, pver)
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -482,7 +488,7 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// With the Segregated Witness specific fields decoded, we can
// now read in the actual txin count.
count, err = ReadVarInt(r, pver)
count, err = ReadVarIntBuf(r, pver, buf)
if err != nil {
return err
}
@ -498,35 +504,6 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
return messageError("MsgTx.BtcDecode", str)
}
// returnScriptBuffers is a closure that returns any script buffers that
// were borrowed from the pool when there are any deserialization
// errors. This is only valid to call before the final step which
// replaces the scripts with the location in a contiguous buffer and
// returns them.
returnScriptBuffers := func() {
for _, txIn := range msg.TxIn {
if txIn == nil {
continue
}
if txIn.SignatureScript != nil {
scriptPool.Return(txIn.SignatureScript)
}
for _, witnessElem := range txIn.Witness {
if witnessElem != nil {
scriptPool.Return(witnessElem)
}
}
}
for _, txOut := range msg.TxOut {
if txOut == nil || txOut.PkScript == nil {
continue
}
scriptPool.Return(txOut.PkScript)
}
}
// Deserialize the inputs.
var totalScriptSize uint64
txIns := make([]TxIn, count)
@ -536,17 +513,16 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// and needs to be returned to the pool on error.
ti := &txIns[i]
msg.TxIn[i] = ti
err = readTxIn(r, pver, msg.Version, ti)
err = readTxInBuf(r, pver, msg.Version, ti, buf, sbuf)
if err != nil {
returnScriptBuffers()
return err
}
totalScriptSize += uint64(len(ti.SignatureScript))
sbuf = sbuf[len(ti.SignatureScript):]
}
count, err = ReadVarInt(r, pver)
count, err = ReadVarIntBuf(r, pver, buf)
if err != nil {
returnScriptBuffers()
return err
}
@ -554,7 +530,6 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxOutPerMessage) {
returnScriptBuffers()
str := fmt.Sprintf("too many output transactions to fit into "+
"max message size [count %d, max %d]", count,
maxTxOutPerMessage)
@ -569,12 +544,12 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// and needs to be returned to the pool on error.
to := &txOuts[i]
msg.TxOut[i] = to
err = ReadTxOut(r, pver, msg.Version, to)
err = readTxOutBuf(r, pver, msg.Version, to, buf, sbuf)
if err != nil {
returnScriptBuffers()
return err
}
totalScriptSize += uint64(len(to.PkScript))
sbuf = sbuf[len(to.PkScript):]
}
// If the transaction's flag byte isn't 0x00 at this point, then one or
@ -584,16 +559,14 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// For each input, the witness is encoded as a stack
// with one or more items. Therefore, we first read a
// varint which encodes the number of stack items.
witCount, err := ReadVarInt(r, pver)
witCount, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
returnScriptBuffers()
return err
}
// Prevent a possible memory exhaustion attack by
// limiting the witCount value to a sane upper bound.
if witCount > maxWitnessItemsPerInput {
returnScriptBuffers()
str := fmt.Sprintf("too many witness items to fit "+
"into max message size [count %d, max %d]",
witCount, maxWitnessItemsPerInput)
@ -605,23 +578,23 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// item itself.
txin.Witness = make([][]byte, witCount)
for j := uint64(0); j < witCount; j++ {
txin.Witness[j], err = readScript(
r, pver, maxWitnessItemSize, "script witness item",
txin.Witness[j], err = readScriptBuf(
r, pver, buf, sbuf, maxWitnessItemSize,
"script witness item",
)
if err != nil {
returnScriptBuffers()
return err
}
totalScriptSize += uint64(len(txin.Witness[j]))
sbuf = sbuf[len(txin.Witness[j]):]
}
}
}
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian)
if err != nil {
returnScriptBuffers()
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
msg.LockTime = littleEndian.Uint32(buf[:4])
// Create a single allocation to house all of the scripts and set each
// input signature script and output public key script to the
@ -652,9 +625,6 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
msg.TxIn[i].SignatureScript = scripts[offset:end:end]
offset += scriptSize
// Return the temporary script buffer to the pool.
scriptPool.Return(signatureScript)
for j := 0; j < len(msg.TxIn[i].Witness); j++ {
// Copy each item within the witness stack for this
// input into the contiguous buffer at the appropriate
@ -668,10 +638,6 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
end := offset + witnessElemSize
msg.TxIn[i].Witness[j] = scripts[offset:end:end]
offset += witnessElemSize
// Return the temporary buffer used for the witness stack
// item to the pool.
scriptPool.Return(witnessElem)
}
}
for i := 0; i < len(msg.TxOut); i++ {
@ -686,9 +652,6 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
end := offset + scriptSize
msg.TxOut[i].PkScript = scripts[offset:end:end]
offset += scriptSize
// Return the temporary script buffer to the pool.
scriptPool.Return(pkScript)
}
return nil
@ -724,8 +687,18 @@ func (msg *MsgTx) DeserializeNoWitness(r io.Reader) error {
// 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, enc MessageEncoding) error {
err := binarySerializer.PutUint32(w, littleEndian, uint32(msg.Version))
if err != nil {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := msg.btcEncode(w, pver, enc, buf)
return err
}
func (msg *MsgTx) btcEncode(w io.Writer, pver uint32, enc MessageEncoding,
buf []byte) error {
littleEndian.PutUint32(buf[:4], uint32(msg.Version))
if _, err := w.Write(buf[:4]); err != nil {
return err
}
@ -745,26 +718,26 @@ func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error
}
count := uint64(len(msg.TxIn))
err = WriteVarInt(w, pver, count)
err := WriteVarIntBuf(w, pver, count, buf)
if err != nil {
return err
}
for _, ti := range msg.TxIn {
err = writeTxIn(w, pver, msg.Version, ti)
err = writeTxInBuf(w, pver, msg.Version, ti, buf)
if err != nil {
return err
}
}
count = uint64(len(msg.TxOut))
err = WriteVarInt(w, pver, count)
err = WriteVarIntBuf(w, pver, count, buf)
if err != nil {
return err
}
for _, to := range msg.TxOut {
err = WriteTxOut(w, pver, msg.Version, to)
err = WriteTxOutBuf(w, pver, msg.Version, to, buf)
if err != nil {
return err
}
@ -775,14 +748,16 @@ func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error
// within the transaction.
if doWitness {
for _, ti := range msg.TxIn {
err = writeTxWitness(w, pver, msg.Version, ti.Witness)
err = writeTxWitnessBuf(w, pver, msg.Version, ti.Witness, buf)
if err != nil {
return err
}
}
}
return binarySerializer.PutUint32(w, littleEndian, msg.LockTime)
littleEndian.PutUint32(buf[:4], msg.LockTime)
_, err = w.Write(buf[:4])
return err
}
// HasWitness returns false if none of the inputs within the transaction
@ -939,26 +914,58 @@ func NewMsgTx(version int32) *MsgTx {
}
}
// readOutPoint reads the next sequence of bytes from r as an OutPoint.
func readOutPoint(r io.Reader, pver uint32, version int32, op *OutPoint) error {
// readOutPointBuf reads the next sequence of bytes from r as an OutPoint.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readOutPointBuf(r io.Reader, pver uint32, version int32, op *OutPoint,
buf []byte) error {
_, err := io.ReadFull(r, op.Hash[:])
if err != nil {
return err
}
op.Index, err = binarySerializer.Uint32(r, littleEndian)
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
op.Index = littleEndian.Uint32(buf[:4])
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 {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeOutPointBuf(w, pver, version, op, buf)
return err
}
// WriteOutPoint encodes op to the bitcoin protocol encoding for an OutPoint
// writeOutPointBuf encodes op to the bitcoin protocol encoding for an OutPoint
// to w.
func WriteOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error {
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func writeOutPointBuf(w io.Writer, pver uint32, version int32, op *OutPoint,
buf []byte) error {
_, err := w.Write(op.Hash[:])
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, op.Index)
littleEndian.PutUint32(buf[:4], op.Index)
_, err = w.Write(buf[:4])
return err
}
// readScript reads a variable length byte array that represents a transaction
@ -968,8 +975,16 @@ func WriteOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error
// memory exhaustion attacks and forced panics through malformed messages. The
// fieldName parameter is only used for the error message so it provides more
// context in the error.
func readScript(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error) {
count, err := ReadVarInt(r, pver)
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readScriptBuf(r io.Reader, pver uint32, buf, s []byte,
maxAllowed uint32, fieldName string) ([]byte, error) {
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
return nil, err
}
@ -983,58 +998,96 @@ func readScript(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) (
return nil, messageError("readScript", str)
}
b := scriptPool.Borrow(count)
_, err = io.ReadFull(r, b)
_, err = io.ReadFull(r, s[:count])
if err != nil {
scriptPool.Return(b)
return nil, err
}
return b, nil
return s[:count], nil
}
// readTxIn reads the next sequence of bytes from r as a transaction input
// readTxInBuf 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 {
err := readOutPoint(r, pver, version, &ti.PreviousOutPoint)
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readTxInBuf(r io.Reader, pver uint32, version int32, ti *TxIn,
buf, s []byte) error {
err := readOutPointBuf(r, pver, version, &ti.PreviousOutPoint, buf)
if err != nil {
return err
}
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
ti.SignatureScript, err = readScriptBuf(r, pver, buf, s, MaxMessagePayload,
"transaction input signature script")
if err != nil {
return err
}
return readElement(r, &ti.Sequence)
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
ti.Sequence = littleEndian.Uint32(buf[:4])
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)
// writeTxInBuf encodes ti to the bitcoin protocol encoding for a transaction
// input (TxIn) to w. If b is non-nil, the provided buffer will be used for
// serializing small values. Otherwise a buffer will be drawn from the
// binarySerializer's pool and return when the method finishes.
func writeTxInBuf(w io.Writer, pver uint32, version int32, ti *TxIn,
buf []byte) error {
err := writeOutPointBuf(w, pver, version, &ti.PreviousOutPoint, buf)
if err != nil {
return err
}
err = WriteVarBytes(w, pver, ti.SignatureScript)
err = WriteVarBytesBuf(w, pver, ti.SignatureScript, buf)
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, ti.Sequence)
littleEndian.PutUint32(buf[:4], ti.Sequence)
_, err = w.Write(buf[:4])
return err
}
// 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 {
err := readElement(r, &to.Value)
var s scriptSlab
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := readTxOutBuf(r, pver, version, to, buf, s[:])
return err
}
// readTxOutBuf reads the next sequence of bytes from r as a transaction output
// (TxOut). If b is non-nil, the provided buffer will be used for serializing
// small values. Otherwise a buffer will be drawn from the binarySerializer's
// pool and return when the method finishes.
func readTxOutBuf(r io.Reader, pver uint32, version int32, to *TxOut,
buf, s []byte) error {
_, err := io.ReadFull(r, buf)
if err != nil {
return err
}
to.Value = int64(littleEndian.Uint64(buf))
to.PkScript, err = readScript(r, pver, MaxMessagePayload,
"transaction output public key script")
to.PkScript, err = readScriptBuf(
r, pver, buf, s, MaxMessagePayload,
"transaction output public key script",
)
return err
}
@ -1044,26 +1097,49 @@ func ReadTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
// NOTE: This function is exported in order to allow txscript to compute the
// new sighashes for witness transactions (BIP0143).
func WriteTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error {
err := binarySerializer.PutUint64(w, littleEndian, uint64(to.Value))
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := WriteTxOutBuf(w, pver, version, to, buf)
return err
}
// WriteTxOutBuf encodes to into the bitcoin protocol encoding for a transaction
// output (TxOut) to w. If b is non-nil, the provided buffer will be used for
// serializing small values. Otherwise a buffer will be drawn from the
// binarySerializer's pool and return when the method finishes.
//
// NOTE: This function is exported in order to allow txscript to compute the
// new sighashes for witness transactions (BIP0143).
func WriteTxOutBuf(w io.Writer, pver uint32, version int32, to *TxOut,
buf []byte) error {
littleEndian.PutUint64(buf, uint64(to.Value))
_, err := w.Write(buf)
if err != nil {
return err
}
return WriteVarBytes(w, pver, to.PkScript)
return WriteVarBytesBuf(w, pver, to.PkScript, buf)
}
// writeTxWitness encodes the bitcoin protocol encoding for a transaction
// input's witness into to w.
func writeTxWitness(w io.Writer, pver uint32, version int32, wit [][]byte) error {
err := WriteVarInt(w, pver, uint64(len(wit)))
// writeTxWitnessBuf encodes the bitcoin protocol encoding for a transaction
// input's witness into to w. If b is non-nil, the provided buffer will be used
// for serializing small values. Otherwise a buffer will be drawn from the
// binarySerializer's pool and return when the method finishes.
func writeTxWitnessBuf(w io.Writer, pver uint32, version int32, wit [][]byte,
buf []byte) error {
err := WriteVarIntBuf(w, pver, uint64(len(wit)), buf)
if err != nil {
return err
}
for _, item := range wit {
err = WriteVarBytes(w, pver, item)
err = WriteVarBytesBuf(w, pver, item, buf)
if err != nil {
return err
}
}
return nil
}

View file

@ -5,7 +5,6 @@
package wire
import (
"encoding/binary"
"io"
"net"
"time"
@ -89,31 +88,60 @@ func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
// version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp.
func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error {
var ip [16]byte
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := readNetAddressBuf(r, pver, na, ts, buf)
return err
}
// readNetAddressBuf 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.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func readNetAddressBuf(r io.Reader, pver uint32, na *NetAddress, ts bool,
buf []byte) error {
var (
timestamp time.Time
services ServiceFlag
ip [16]byte
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 {
err := readElement(r, (*uint32Time)(&na.Timestamp))
if err != nil {
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return err
}
timestamp = time.Unix(int64(littleEndian.Uint32(buf[:4])), 0)
}
err := readElements(r, &na.Services, &ip)
if err != nil {
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
services = ServiceFlag(littleEndian.Uint64(buf))
if _, err := io.ReadFull(r, ip[:]); err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
port, err := binarySerializer.Uint16(r, bigEndian)
if err != nil {
if _, err := io.ReadFull(r, buf[:2]); err != nil {
return err
}
port = bigEndian.Uint16(buf[:2])
*na = NetAddress{
Timestamp: na.Timestamp,
Services: na.Services,
Timestamp: timestamp,
Services: services,
IP: net.IP(ip[:]),
Port: port,
}
@ -124,26 +152,50 @@ func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error {
// 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 {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeNetAddressBuf(w, pver, na, ts, buf)
return err
}
// writeNetAddressBuf 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.
//
// If b is non-nil, the provided buffer will be used for serializing small
// values. Otherwise a buffer will be drawn from the binarySerializer's pool
// and return when the method finishes.
//
// NOTE: b MUST either be nil or at least an 8-byte slice.
func writeNetAddressBuf(w io.Writer, pver uint32, na *NetAddress, ts bool, buf []byte) 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 {
littleEndian.PutUint32(buf[:4], uint32(na.Timestamp.Unix()))
if _, err := w.Write(buf[:4]); err != nil {
return err
}
}
littleEndian.PutUint64(buf, uint64(na.Services))
if _, err := w.Write(buf); 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 {
if _, err := w.Write(ip[:]); err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
return binary.Write(w, bigEndian, na.Port)
bigEndian.PutUint16(buf[:2], na.Port)
_, err := w.Write(buf[:2])
return err
}