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" "bytes"
"compress/bzip2" "compress/bzip2"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -63,38 +64,48 @@ var genesisCoinbaseTx = MsgTx{
// BenchmarkWriteVarInt1 performs a benchmark on how long it takes to write // BenchmarkWriteVarInt1 performs a benchmark on how long it takes to write
// a single byte variable length integer. // a single byte variable length integer.
func BenchmarkWriteVarInt1(b *testing.B) { func BenchmarkWriteVarInt1(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { 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 // BenchmarkWriteVarInt3 performs a benchmark on how long it takes to write
// a three byte variable length integer. // a three byte variable length integer.
func BenchmarkWriteVarInt3(b *testing.B) { func BenchmarkWriteVarInt3(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { 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 // BenchmarkWriteVarInt5 performs a benchmark on how long it takes to write
// a five byte variable length integer. // a five byte variable length integer.
func BenchmarkWriteVarInt5(b *testing.B) { func BenchmarkWriteVarInt5(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { 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 // BenchmarkWriteVarInt9 performs a benchmark on how long it takes to write
// a nine byte variable length integer. // a nine byte variable length integer.
func BenchmarkWriteVarInt9(b *testing.B) { func BenchmarkWriteVarInt9(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { 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 // BenchmarkReadVarInt1 performs a benchmark on how long it takes to read
// a single byte variable length integer. // a single byte variable length integer.
func BenchmarkReadVarInt1(b *testing.B) { func BenchmarkReadVarInt1(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x01} buf := []byte{0x01}
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ { 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 // BenchmarkReadVarInt3 performs a benchmark on how long it takes to read
// a three byte variable length integer. // a three byte variable length integer.
func BenchmarkReadVarInt3(b *testing.B) { func BenchmarkReadVarInt3(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x0fd, 0xff, 0xff} buf := []byte{0x0fd, 0xff, 0xff}
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ { 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 // BenchmarkReadVarInt5 performs a benchmark on how long it takes to read
// a five byte variable length integer. // a five byte variable length integer.
func BenchmarkReadVarInt5(b *testing.B) { func BenchmarkReadVarInt5(b *testing.B) {
b.ReportAllocs()
buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff} buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ { 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 // BenchmarkReadVarInt9 performs a benchmark on how long it takes to read
// a nine byte variable length integer. // a nine byte variable length integer.
func BenchmarkReadVarInt9(b *testing.B) { func BenchmarkReadVarInt9(b *testing.B) {
b.ReportAllocs()
buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ { 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 // BenchmarkReadVarStr4 performs a benchmark on how long it takes to read a
// four byte variable length string. // four byte variable length string.
func BenchmarkReadVarStr4(b *testing.B) { func BenchmarkReadVarStr4(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x04, 't', 'e', 's', 't'} buf := []byte{0x04, 't', 'e', 's', 't'}
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ { 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 // BenchmarkReadVarStr10 performs a benchmark on how long it takes to read a
// ten byte variable length string. // ten byte variable length string.
func BenchmarkReadVarStr10(b *testing.B) { func BenchmarkReadVarStr10(b *testing.B) {
b.ReportAllocs()
buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'} buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'}
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
for i := 0; i < b.N; i++ { 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 // BenchmarkWriteVarStr4 performs a benchmark on how long it takes to write a
// four byte variable length string. // four byte variable length string.
func BenchmarkWriteVarStr4(b *testing.B) { func BenchmarkWriteVarStr4(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { 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 // BenchmarkWriteVarStr10 performs a benchmark on how long it takes to write a
// ten byte variable length string. // ten byte variable length string.
func BenchmarkWriteVarStr10(b *testing.B) { func BenchmarkWriteVarStr10(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { 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 // BenchmarkReadOutPoint performs a benchmark on how long it takes to read a
// transaction output point. // transaction output point.
func BenchmarkReadOutPoint(b *testing.B) { func BenchmarkReadOutPoint(b *testing.B) {
b.ReportAllocs()
buffer := binarySerializer.Borrow()
buf := []byte{ buf := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -188,25 +378,46 @@ func BenchmarkReadOutPoint(b *testing.B) {
var op OutPoint var op OutPoint
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
r.Seek(0, 0) 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 // BenchmarkWriteOutPoint performs a benchmark on how long it takes to write a
// transaction output point. // transaction output point.
func BenchmarkWriteOutPoint(b *testing.B) { func BenchmarkWriteOutPoint(b *testing.B) {
b.ReportAllocs()
op := &OutPoint{ op := &OutPoint{
Hash: chainhash.Hash{}, Hash: chainhash.Hash{},
Index: 0, Index: 0,
} }
for i := 0; i < b.N; i++ { 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 // BenchmarkReadTxOut performs a benchmark on how long it takes to read a
// transaction output. // transaction output.
func BenchmarkReadTxOut(b *testing.B) { func BenchmarkReadTxOut(b *testing.B) {
b.ReportAllocs()
buf := []byte{ buf := []byte{
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount 0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script 0x43, // Varint for length of pk script
@ -227,22 +438,74 @@ func BenchmarkReadTxOut(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
r.Seek(0, 0) r.Seek(0, 0)
ReadTxOut(r, 0, 0, &txOut) 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 // BenchmarkWriteTxOut performs a benchmark on how long it takes to write
// a transaction output. // a transaction output.
func BenchmarkWriteTxOut(b *testing.B) { func BenchmarkWriteTxOut(b *testing.B) {
b.ReportAllocs()
txOut := blockOne.Transactions[0].TxOut[0] txOut := blockOne.Transactions[0].TxOut[0]
for i := 0; i < b.N; i++ { 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 // BenchmarkReadTxIn performs a benchmark on how long it takes to read a
// transaction input. // transaction input.
func BenchmarkReadTxIn(b *testing.B) { func BenchmarkReadTxIn(b *testing.B) {
b.ReportAllocs()
scriptBuffer := scriptPool.Borrow()
sbuf := scriptBuffer[:]
buffer := binarySerializer.Borrow()
buf := []byte{ buf := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -257,18 +520,23 @@ func BenchmarkReadTxIn(b *testing.B) {
var txIn TxIn var txIn TxIn
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
r.Seek(0, 0) r.Seek(0, 0)
readTxIn(r, 0, 0, &txIn) readTxInBuf(r, 0, 0, &txIn, buffer, sbuf)
scriptPool.Return(txIn.SignatureScript)
} }
binarySerializer.Return(buffer)
scriptPool.Return(scriptBuffer)
} }
// BenchmarkWriteTxIn performs a benchmark on how long it takes to write // BenchmarkWriteTxIn performs a benchmark on how long it takes to write
// a transaction input. // a transaction input.
func BenchmarkWriteTxIn(b *testing.B) { func BenchmarkWriteTxIn(b *testing.B) {
b.ReportAllocs()
buf := binarySerializer.Borrow()
txIn := blockOne.Transactions[0].TxIn[0] txIn := blockOne.Transactions[0].TxIn[0]
for i := 0; i < b.N; i++ { 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 // 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 0x00, 0x00, 0x00, 0x00, // Lock time
} }
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
var tx MsgTx var tx MsgTx
for i := 0; i < b.N; i++ { 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 // BenchmarkDeserializeTxLarge performs a benchmark on how long it takes to
// deserialize a very large transaction. // deserialize a very large transaction.
func BenchmarkDeserializeTxLarge(b *testing.B) { func BenchmarkDeserializeTxLarge(b *testing.B) {
// tx bb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e08 // tx bb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e08
// from the main block chain. // from the main block chain.
fi, err := os.Open("testdata/megatx.bin.bz2") 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.Fatalf("Failed to read transaction data: %v", err)
} }
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
var tx MsgTx var tx MsgTx
for i := 0; i < b.N; i++ { 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 // BenchmarkSerializeTx performs a benchmark on how long it takes to serialize
// a transaction. // a transaction.
func BenchmarkSerializeTx(b *testing.B) { func BenchmarkSerializeTx(b *testing.B) {
b.ReportAllocs()
tx := blockOne.Transactions[0] tx := blockOne.Transactions[0]
for i := 0; i < b.N; i++ { 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 // BenchmarkReadBlockHeader performs a benchmark on how long it takes to
// deserialize a block header. // deserialize a block header.
func BenchmarkReadBlockHeader(b *testing.B) { func BenchmarkReadBlockHeader(b *testing.B) {
b.ReportAllocs()
buf := []byte{ buf := []byte{
0x01, 0x00, 0x00, 0x00, // Version 1 0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 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 // BenchmarkWriteBlockHeader performs a benchmark on how long it takes to
// serialize a block header. // serialize a block header.
func BenchmarkWriteBlockHeader(b *testing.B) { func BenchmarkWriteBlockHeader(b *testing.B) {
b.ReportAllocs()
header := blockOne.Header header := blockOne.Header
for i := 0; i < b.N; i++ { 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 // BenchmarkDecodeGetHeaders performs a benchmark on how long it takes to
// decode a getheaders message with the maximum number of block locator hashes. // decode a getheaders message with the maximum number of block locator hashes.
func BenchmarkDecodeGetHeaders(b *testing.B) { func BenchmarkDecodeGetHeaders(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of block locators. // Create a message with the maximum number of block locators.
pver := ProtocolVersion pver := ProtocolVersion
var m MsgGetHeaders var m MsgGetHeaders
@ -411,6 +846,8 @@ func BenchmarkDecodeGetHeaders(b *testing.B) {
// BenchmarkDecodeHeaders performs a benchmark on how long it takes to // BenchmarkDecodeHeaders performs a benchmark on how long it takes to
// decode a headers message with the maximum number of headers. // decode a headers message with the maximum number of headers.
func BenchmarkDecodeHeaders(b *testing.B) { func BenchmarkDecodeHeaders(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of headers. // Create a message with the maximum number of headers.
pver := ProtocolVersion pver := ProtocolVersion
var m MsgHeaders var m MsgHeaders
@ -441,6 +878,8 @@ func BenchmarkDecodeHeaders(b *testing.B) {
// BenchmarkDecodeGetBlocks performs a benchmark on how long it takes to // BenchmarkDecodeGetBlocks performs a benchmark on how long it takes to
// decode a getblocks message with the maximum number of block locator hashes. // decode a getblocks message with the maximum number of block locator hashes.
func BenchmarkDecodeGetBlocks(b *testing.B) { func BenchmarkDecodeGetBlocks(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of block locators. // Create a message with the maximum number of block locators.
pver := ProtocolVersion pver := ProtocolVersion
var m MsgGetBlocks var m MsgGetBlocks
@ -471,6 +910,8 @@ func BenchmarkDecodeGetBlocks(b *testing.B) {
// BenchmarkDecodeAddr performs a benchmark on how long it takes to decode an // BenchmarkDecodeAddr performs a benchmark on how long it takes to decode an
// addr message with the maximum number of addresses. // addr message with the maximum number of addresses.
func BenchmarkDecodeAddr(b *testing.B) { func BenchmarkDecodeAddr(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of addresses. // Create a message with the maximum number of addresses.
pver := ProtocolVersion pver := ProtocolVersion
ip := net.ParseIP("127.0.0.1") ip := net.ParseIP("127.0.0.1")
@ -516,6 +957,9 @@ func BenchmarkDecodeInv(b *testing.B) {
} }
buf := bb.Bytes() buf := bb.Bytes()
b.ReportAllocs()
b.ResetTimer()
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
var msg MsgInv var msg MsgInv
b.ResetTimer() b.ResetTimer()
@ -528,6 +972,8 @@ func BenchmarkDecodeInv(b *testing.B) {
// BenchmarkDecodeNotFound performs a benchmark on how long it takes to decode // BenchmarkDecodeNotFound performs a benchmark on how long it takes to decode
// a notfound message with the maximum number of entries. // a notfound message with the maximum number of entries.
func BenchmarkDecodeNotFound(b *testing.B) { func BenchmarkDecodeNotFound(b *testing.B) {
b.ReportAllocs()
// Create a message with the maximum number of entries. // Create a message with the maximum number of entries.
pver := ProtocolVersion pver := ProtocolVersion
var m MsgNotFound var m MsgNotFound
@ -558,6 +1004,8 @@ func BenchmarkDecodeNotFound(b *testing.B) {
// BenchmarkDecodeMerkleBlock performs a benchmark on how long it takes to // BenchmarkDecodeMerkleBlock performs a benchmark on how long it takes to
// decode a reasonably sized merkleblock message. // decode a reasonably sized merkleblock message.
func BenchmarkDecodeMerkleBlock(b *testing.B) { func BenchmarkDecodeMerkleBlock(b *testing.B) {
b.ReportAllocs()
// Create a message with random data. // Create a message with random data.
pver := ProtocolVersion pver := ProtocolVersion
var m MsgMerkleBlock var m MsgMerkleBlock
@ -596,6 +1044,8 @@ func BenchmarkDecodeMerkleBlock(b *testing.B) {
// BenchmarkTxHash performs a benchmark on how long it takes to hash a // BenchmarkTxHash performs a benchmark on how long it takes to hash a
// transaction. // transaction.
func BenchmarkTxHash(b *testing.B) { func BenchmarkTxHash(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
genesisCoinbaseTx.TxHash() genesisCoinbaseTx.TxHash()
} }
@ -604,6 +1054,8 @@ func BenchmarkTxHash(b *testing.B) {
// BenchmarkDoubleHashB performs a benchmark on how long it takes to perform a // BenchmarkDoubleHashB performs a benchmark on how long it takes to perform a
// double hash returning a byte slice. // double hash returning a byte slice.
func BenchmarkDoubleHashB(b *testing.B) { func BenchmarkDoubleHashB(b *testing.B) {
b.ReportAllocs()
var buf bytes.Buffer var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil { if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err) 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 // BenchmarkDoubleHashH performs a benchmark on how long it takes to perform
// a double hash returning a chainhash.Hash. // a double hash returning a chainhash.Hash.
func BenchmarkDoubleHashH(b *testing.B) { func BenchmarkDoubleHashH(b *testing.B) {
b.ReportAllocs()
var buf bytes.Buffer var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil { if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err) 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 // 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 block headers stored to disk, such as in a database, as opposed to
// decoding from the wire. // decoding from the wire.
//
// DEPRECATED: Use readBlockHeaderBuf instead.
func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error { func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
return readElements(r, &bh.Version, &bh.PrevBlock, &bh.MerkleRoot, buf := binarySerializer.Borrow()
(*uint32Time)(&bh.Timestamp), &bh.Bits, &bh.Nonce) 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 // 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 // encoding block headers to be stored to disk, such as in a database, as
// opposed to encoding for the wire. // opposed to encoding for the wire.
//
// DEPRECATED: Use writeBlockHeaderBuf instead.
func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error { func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
sec := uint32(bh.Timestamp.Unix()) buf := binarySerializer.Borrow()
return writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot, err := writeBlockHeaderBuf(w, pver, bh, buf)
sec, bh.Bits, bh.Nonce) 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. // free list and returns it as a uint8.
func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) { func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
buf := l.Borrow()[:1] buf := l.Borrow()[:1]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err return 0, err
} }
rv := buf[0] rv := buf[0]
l.Return(buf)
return rv, nil return rv, nil
} }
@ -87,12 +88,13 @@ func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
// the resulting uint16. // the resulting uint16.
func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) { func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) {
buf := l.Borrow()[:2] buf := l.Borrow()[:2]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err return 0, err
} }
rv := byteOrder.Uint16(buf) rv := byteOrder.Uint16(buf)
l.Return(buf)
return rv, nil return rv, nil
} }
@ -101,12 +103,13 @@ func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16,
// the resulting uint32. // the resulting uint32.
func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) { func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
buf := l.Borrow()[:4] buf := l.Borrow()[:4]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err return 0, err
} }
rv := byteOrder.Uint32(buf) rv := byteOrder.Uint32(buf)
l.Return(buf)
return rv, nil return rv, nil
} }
@ -115,12 +118,13 @@ func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32,
// the resulting uint64. // the resulting uint64.
func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) { func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) {
buf := l.Borrow()[:8] buf := l.Borrow()[:8]
defer l.Return(buf)
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
l.Return(buf)
return 0, err return 0, err
} }
rv := byteOrder.Uint64(buf) rv := byteOrder.Uint64(buf)
l.Return(buf)
return rv, nil 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. // writes the resulting byte to the given writer.
func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error { func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
buf := l.Borrow()[:1] buf := l.Borrow()[:1]
defer l.Return(buf)
buf[0] = val buf[0] = val
_, err := w.Write(buf) _, err := w.Write(buf)
l.Return(buf)
return err return err
} }
@ -139,9 +145,11 @@ func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
// writer. // writer.
func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error { func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error {
buf := l.Borrow()[:2] buf := l.Borrow()[:2]
defer l.Return(buf)
byteOrder.PutUint16(buf, val) byteOrder.PutUint16(buf, val)
_, err := w.Write(buf) _, err := w.Write(buf)
l.Return(buf)
return err return err
} }
@ -150,9 +158,11 @@ func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val u
// writer. // writer.
func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error { func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error {
buf := l.Borrow()[:4] buf := l.Borrow()[:4]
defer l.Return(buf)
byteOrder.PutUint32(buf, val) byteOrder.PutUint32(buf, val)
_, err := w.Write(buf) _, err := w.Write(buf)
l.Return(buf)
return err return err
} }
@ -161,9 +171,11 @@ func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val u
// writer. // writer.
func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error { func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error {
buf := l.Borrow()[:8] buf := l.Borrow()[:8]
defer l.Return(buf)
byteOrder.PutUint64(buf, val) byteOrder.PutUint64(buf, val)
_, err := w.Write(buf) _, err := w.Write(buf)
l.Return(buf)
return err 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. // ReadVarInt reads a variable length integer from r and returns it as a uint64.
func ReadVarInt(r io.Reader, pver uint32) (uint64, error) { func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
discriminant, err := binarySerializer.Uint8(r) buf := binarySerializer.Borrow()
if err != nil { 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 return 0, err
} }
discriminant := buf[0]
var rv uint64 var rv uint64
switch discriminant { switch discriminant {
case 0xff: case 0xff:
sv, err := binarySerializer.Uint64(r, littleEndian) if _, err := io.ReadFull(r, buf); err != nil {
if err != nil {
return 0, err return 0, err
} }
rv = sv rv = littleEndian.Uint64(buf)
// The encoding is not canonical if the value could have been // The encoding is not canonical if the value could have been
// encoded using fewer bytes. // encoded using fewer bytes.
@ -497,11 +520,10 @@ func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
} }
case 0xfe: case 0xfe:
sv, err := binarySerializer.Uint32(r, littleEndian) if _, err := io.ReadFull(r, buf[:4]); err != nil {
if err != nil {
return 0, err return 0, err
} }
rv = uint64(sv) rv = uint64(littleEndian.Uint32(buf[:4]))
// The encoding is not canonical if the value could have been // The encoding is not canonical if the value could have been
// encoded using fewer bytes. // encoded using fewer bytes.
@ -512,11 +534,10 @@ func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
} }
case 0xfd: case 0xfd:
sv, err := binarySerializer.Uint16(r, littleEndian) if _, err := io.ReadFull(r, buf[:2]); err != nil {
if err != nil {
return 0, err return 0, err
} }
rv = uint64(sv) rv = uint64(littleEndian.Uint16(buf[:2]))
// The encoding is not canonical if the value could have been // The encoding is not canonical if the value could have been
// encoded using fewer bytes. // 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 // WriteVarInt serializes val to w using a variable number of bytes depending
// on its value. // on its value.
func WriteVarInt(w io.Writer, pver uint32, val uint64) error { func WriteVarInt(w io.Writer, pver uint32, val uint64) error {
if val < 0xfd { buf := binarySerializer.Borrow()
return binarySerializer.PutUint8(w, uint8(val)) defer binarySerializer.Return(buf)
}
if val <= math.MaxUint16 { err := WriteVarIntBuf(w, pver, val, buf)
err := binarySerializer.PutUint8(w, 0xfd) return err
if err != nil { }
// 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 err
} }
return binarySerializer.PutUint16(w, littleEndian, uint16(val))
}
if val <= math.MaxUint32 { littleEndian.PutUint64(buf, val)
err := binarySerializer.PutUint8(w, 0xfe) _, err := w.Write(buf)
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, uint32(val))
}
err := binarySerializer.PutUint8(w, 0xff)
if err != nil {
return err return err
} }
return binarySerializer.PutUint64(w, littleEndian, val)
} }
// VarIntSerializeSize returns the number of bytes it would take to serialize // 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 // maximum block payload size since it helps protect against memory exhaustion
// attacks and forced panics through malformed messages. // attacks and forced panics through malformed messages.
func ReadVarString(r io.Reader, pver uint32) (string, error) { 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 { if err != nil {
return "", err return "", err
} }
@ -607,22 +663,40 @@ func ReadVarString(r io.Reader, pver uint32) (string, error) {
return "", messageError("ReadVarString", str) return "", messageError("ReadVarString", str)
} }
buf := make([]byte, count) str := make([]byte, count)
_, err = io.ReadFull(r, buf) _, err = io.ReadFull(r, str)
if err != nil { if err != nil {
return "", err return "", err
} }
return string(buf), nil return string(str), nil
} }
// WriteVarString serializes str to w as a variable length integer containing // WriteVarString serializes str to w as a variable length integer containing
// the length of the string followed by the bytes that represent the string // the length of the string followed by the bytes that represent the string
// itself. // itself.
func WriteVarString(w io.Writer, pver uint32, str string) error { 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 { if err != nil {
return err return err
} }
_, err = w.Write([]byte(str)) _, err = w.Write([]byte(str))
return err 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, func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
fieldName string) ([]byte, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -651,19 +744,33 @@ func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
return nil, messageError("ReadVarBytes", str) return nil, messageError("ReadVarBytes", str)
} }
b := make([]byte, count) bytes := make([]byte, count)
_, err = io.ReadFull(r, b) _, err = io.ReadFull(r, bytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return b, nil return bytes, nil
} }
// WriteVarBytes serializes a variable length byte array to w as a varInt // WriteVarBytes serializes a variable length byte array to w as a varInt
// containing the number of bytes, followed by the bytes themselves. // containing the number of bytes, followed by the bytes themselves.
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error { 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)) slen := uint64(len(bytes))
err := WriteVarInt(w, pver, slen)
err := WriteVarIntBuf(w, pver, slen, buf)
if err != nil { if err != nil {
return err 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. // 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. // writeInvVectBuf 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) // 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)) t.Logf("Running %d tests", len(tests))
var b [8]byte
for i, test := range tests { for i, test := range tests {
// Encode to wire format. // Encode to wire format.
var buf bytes.Buffer var buf bytes.Buffer
err := writeInvVect(&buf, test.pver, &test.in) err := writeInvVectBuf(&buf, test.pver, &test.in, b[:])
if err != nil { if err != nil {
t.Errorf("writeInvVect #%d error %v", i, err) t.Errorf("writeInvVect #%d error %v", i, err)
continue continue
@ -255,7 +256,7 @@ func TestInvVectWire(t *testing.T) {
// Decode the message from wire format. // Decode the message from wire format.
var iv InvVect var iv InvVect
rbuf := bytes.NewReader(test.buf) rbuf := bytes.NewReader(test.buf)
err = readInvVect(rbuf, test.pver, &iv) err = readInvVectBuf(rbuf, test.pver, &iv, b[:])
if err != nil { if err != nil {
t.Errorf("readInvVect #%d error %v", i, err) t.Errorf("readInvVect #%d error %v", i, err)
continue 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 // See Deserialize for decoding blocks stored to disk, such as in a database, as
// opposed to decoding blocks from the wire. // opposed to decoding blocks from the wire.
func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
txCount, err := ReadVarInt(r, pver) txCount, err := ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
return err return err
} }
@ -81,10 +84,13 @@ func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) er
return messageError("MsgBlock.BtcDecode", str) return messageError("MsgBlock.BtcDecode", str)
} }
scriptBuf := scriptPool.Borrow()
defer scriptPool.Return(scriptBuf)
msg.Transactions = make([]*MsgTx, 0, txCount) msg.Transactions = make([]*MsgTx, 0, txCount)
for i := uint64(0); i < txCount; i++ { for i := uint64(0); i < txCount; i++ {
tx := MsgTx{} tx := MsgTx{}
err := tx.BtcDecode(r, pver, enc) err := tx.btcDecode(r, pver, enc, buf, scriptBuf[:])
if err != nil { if err != nil {
return err return err
} }
@ -129,15 +135,18 @@ func (msg *MsgBlock) DeserializeNoWitness(r io.Reader) error {
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) { func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
fullLen := r.Len() fullLen := r.Len()
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// At the current time, there is no difference between the wire encoding // At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As // at protocol version 0 and the stable long-term storage format. As
// a result, make use of existing wire protocol functions. // 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 { if err != nil {
return nil, err return nil, err
} }
txCount, err := ReadVarInt(r, 0) txCount, err := ReadVarIntBuf(r, 0, buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -151,6 +160,9 @@ func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
return nil, messageError("MsgBlock.DeserializeTxLoc", str) return nil, messageError("MsgBlock.DeserializeTxLoc", str)
} }
scriptBuf := scriptPool.Borrow()
defer scriptPool.Return(scriptBuf)
// Deserialize each transaction while keeping track of its location // Deserialize each transaction while keeping track of its location
// within the byte stream. // within the byte stream.
msg.Transactions = make([]*MsgTx, 0, txCount) 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++ { for i := uint64(0); i < txCount; i++ {
txLocs[i].TxStart = fullLen - r.Len() txLocs[i].TxStart = fullLen - r.Len()
tx := MsgTx{} tx := MsgTx{}
err := tx.Deserialize(r) err := tx.btcDecode(r, 0, WitnessEncoding, buf, scriptBuf[:])
if err != nil { if err != nil {
return nil, err 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 // See Serialize for encoding blocks to be stored to disk, such as in a
// database, as opposed to encoding blocks for the wire. // database, as opposed to encoding blocks for the wire.
func (msg *MsgBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
err = WriteVarInt(w, pver, uint64(len(msg.Transactions))) err = WriteVarIntBuf(w, pver, uint64(len(msg.Transactions)), buf)
if err != nil { if err != nil {
return err return err
} }
for _, tx := range msg.Transactions { for _, tx := range msg.Transactions {
err = tx.BtcEncode(w, pver, enc) err = tx.btcEncode(w, pver, enc, buf)
if err != nil { if err != nil {
return err 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Read filter type // Read filter type
err := readElement(r, &msg.FilterType) if _, err := io.ReadFull(r, buf[:1]); err != nil {
if err != nil {
return err return err
} }
msg.FilterType = FilterType(buf[0])
// Read stop hash // Read stop hash
err = readElement(r, &msg.StopHash) if _, err := io.ReadFull(r, msg.StopHash[:]); err != nil {
if err != nil {
return err return err
} }
// Read number of filter headers // Read number of filter headers
count, err := ReadVarInt(r, pver) count, err := ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
return err return err
} }
@ -80,7 +82,7 @@ func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding)
msg.FilterHeaders = make([]*chainhash.Hash, count) msg.FilterHeaders = make([]*chainhash.Hash, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
var cfh chainhash.Hash var cfh chainhash.Hash
err := readElement(r, &cfh) _, err := io.ReadFull(r, cfh[:])
if err != nil { if err != nil {
return err 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. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Write filter type // Write filter type
err := writeElement(w, msg.FilterType) buf[0] = byte(msg.FilterType)
if err != nil { if _, err := w.Write(buf[:1]); err != nil {
return err return err
} }
// Write stop hash // Write stop hash
err = writeElement(w, msg.StopHash) if _, err := w.Write(msg.StopHash[:]); err != nil {
if err != nil {
return err return err
} }
// Write length of FilterHeaders slice // Write length of FilterHeaders slice
count := len(msg.FilterHeaders) count := len(msg.FilterHeaders)
err = WriteVarInt(w, pver, uint64(count)) err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil { if err != nil {
return err return err
} }
for _, cfh := range msg.FilterHeaders { for _, cfh := range msg.FilterHeaders {
err := writeElement(w, cfh) _, err := w.Write(cfh[:])
if err != nil { if err != nil {
return err 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
// Read filter type // Read filter type
err := readElement(r, &msg.FilterType) if _, err := io.ReadFull(r, buf[:1]); err != nil {
if err != nil {
return err return err
} }
msg.FilterType = FilterType(buf[0])
// Read stop hash // Read stop hash
err = readElement(r, &msg.StopHash) if _, err := io.ReadFull(r, msg.StopHash[:]); err != nil {
if err != nil {
return err return err
} }
// Read prev filter header // Read prev filter header
err = readElement(r, &msg.PrevFilterHeader) if _, err := io.ReadFull(r, msg.PrevFilterHeader[:]); err != nil {
if err != nil {
return err return err
} }
// Read number of filter headers // Read number of filter headers
count, err := ReadVarInt(r, pver) count, err := ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
return err return err
} }
@ -85,7 +86,7 @@ func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding)
msg.FilterHashes = make([]*chainhash.Hash, 0, count) msg.FilterHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
var cfh chainhash.Hash var cfh chainhash.Hash
err := readElement(r, &cfh) _, err := io.ReadFull(r, cfh[:])
if err != nil { if err != nil {
return err 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. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { 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) count := len(msg.FilterHashes)
if count > MaxCFHeadersPerMsg { if count > MaxCFHeadersPerMsg {
str := fmt.Sprintf("too many committed filter headers for "+ 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) 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 { if err != nil {
return err return err
} }
for _, cfh := range msg.FilterHashes { for _, cfh := range msg.FilterHashes {
err := writeElement(w, cfh) _, err := w.Write(cfh[:])
if err != nil { if err != nil {
return err return err
} }

View file

@ -38,19 +38,22 @@ type MsgCFilter struct {
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgCFilter) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgCFilter) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
// Read filter type // Read filter type
err := readElement(r, &msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err return err
} }
msg.FilterType = FilterType(buf[0])
// Read the hash of the filter's block // Read the hash of the filter's block
err = readElement(r, &msg.BlockHash) if _, err := io.ReadFull(r, msg.BlockHash[:]); err != nil {
if err != nil {
return err return err
} }
// Read filter data // Read filter data
msg.Data, err = ReadVarBytes(r, pver, MaxCFilterDataSize, var err error
msg.Data, err = ReadVarBytesBuf(r, pver, buf, MaxCFilterDataSize,
"cfilter data") "cfilter data")
return err return err
} }
@ -65,17 +68,20 @@ func (msg *MsgCFilter) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) er
return messageError("MsgCFilter.BtcEncode", str) return messageError("MsgCFilter.BtcEncode", str)
} }
err := writeElement(w, msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err return err
} }
err = writeElement(w, msg.BlockHash) if _, err := w.Write(msg.BlockHash[:]); err != nil {
if err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
// Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > MaxBlockLocatorsPerMsg { if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+ str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg) "[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) msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
hash := &locatorHashes[i] hash := &locatorHashes[i]
err := readElement(r, hash) _, err := io.ReadFull(r, hash[:])
if err != nil { if err != nil {
return err return err
} }
msg.AddBlockLocatorHash(hash) 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. // 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) return messageError("MsgGetBlocks.BtcEncode", str)
} }
err := writeElement(w, msg.ProtocolVersion) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
littleEndian.PutUint32(buf[:4], msg.ProtocolVersion)
if _, err := w.Write(buf[:4]); err != nil {
return err return err
} }
err = WriteVarInt(w, pver, uint64(count)) err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil { if err != nil {
return err return err
} }
for _, hash := range msg.BlockLocatorHashes { for _, hash := range msg.BlockLocatorHashes {
err = writeElement(w, hash) _, err := w.Write(hash[:])
if err != nil { if err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
err := readElement(r, &msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err 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. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
err := writeElement(w, msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
err := readElement(r, &msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err return err
} }
msg.FilterType = FilterType(buf[0])
err = readElement(r, &msg.StartHeight) if _, err := io.ReadFull(r, buf[:4]); err != nil {
if err != nil {
return err 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. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
err := writeElement(w, msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err return err
} }
err = writeElement(w, &msg.StartHeight) littleEndian.PutUint32(buf[:4], msg.StartHeight)
if err != nil { if _, err := w.Write(buf[:4]); err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFilters) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFilters) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
err := readElement(r, &msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err return err
} }
msg.FilterType = FilterType(buf[0])
err = readElement(r, &msg.StartHeight) if _, err := io.ReadFull(r, buf[:4]); err != nil {
if err != nil {
return err 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. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFilters) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFilters) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
err := writeElement(w, msg.FilterType) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
buf[0] = byte(msg.FilterType)
if _, err := w.Write(buf[:1]); err != nil {
return err return err
} }
err = writeElement(w, &msg.StartHeight) littleEndian.PutUint32(buf[:4], msg.StartHeight)
if err != nil { if _, err := w.Write(buf[:4]); err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
@ -55,7 +58,7 @@ func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding)
msg.InvList = make([]*InvVect, 0, count) msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
iv := &invList[i] iv := &invList[i]
err := readInvVect(r, pver, iv) err := readInvVectBuf(r, pver, iv, buf)
if err != nil { if err != nil {
return err return err
} }
@ -75,13 +78,16 @@ func (msg *MsgGetData) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
return messageError("MsgGetData.BtcEncode", str) 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 { if err != nil {
return err return err
} }
for _, iv := range msg.InvList { for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv) err := writeInvVectBuf(w, pver, iv, buf)
if err != nil { if err != nil {
return err 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
// Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > MaxBlockLocatorsPerMsg { if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+ str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg) "[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) msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
hash := &locatorHashes[i] hash := &locatorHashes[i]
err := readElement(r, hash) _, err := io.ReadFull(r, hash[:])
if err != nil { if err != nil {
return err return err
} }
msg.AddBlockLocatorHash(hash) 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. // 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) return messageError("MsgGetHeaders.BtcEncode", str)
} }
err := writeElement(w, msg.ProtocolVersion) buf := binarySerializer.Borrow()
if err != nil { defer binarySerializer.Return(buf)
littleEndian.PutUint32(buf[:4], msg.ProtocolVersion)
if _, err := w.Write(buf[:4]); err != nil {
return err return err
} }
err = WriteVarInt(w, pver, uint64(count)) err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil { if err != nil {
return err return err
} }
for _, hash := range msg.BlockLocatorHashes { for _, hash := range msg.BlockLocatorHashes {
err := writeElement(w, hash) _, err := w.Write(hash[:])
if err != nil { if err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
@ -55,12 +58,12 @@ func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding)
msg.Headers = make([]*BlockHeader, 0, count) msg.Headers = make([]*BlockHeader, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
bh := &headers[i] bh := &headers[i]
err := readBlockHeader(r, pver, bh) err := readBlockHeaderBuf(r, pver, bh, buf)
if err != nil { if err != nil {
return err return err
} }
txCount, err := ReadVarInt(r, pver) txCount, err := ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
return err return err
} }
@ -88,13 +91,16 @@ func (msg *MsgHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
return messageError("MsgHeaders.BtcEncode", str) 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 { if err != nil {
return err return err
} }
for _, bh := range msg.Headers { for _, bh := range msg.Headers {
err := writeBlockHeader(w, pver, bh) err := writeBlockHeaderBuf(w, pver, bh, buf)
if err != nil { if err != nil {
return err 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 // of transactions on header messages. This is really just an
// artifact of the way the original implementation serializes // artifact of the way the original implementation serializes
// block headers, but it is required. // block headers, but it is required.
err = WriteVarInt(w, pver, 0) err = WriteVarIntBuf(w, pver, 0, buf)
if err != nil { if err != nil {
return err 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
@ -63,7 +66,7 @@ func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) erro
msg.InvList = make([]*InvVect, 0, count) msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
iv := &invList[i] iv := &invList[i]
err := readInvVect(r, pver, iv) err := readInvVectBuf(r, pver, iv, buf)
if err != nil { if err != nil {
return err return err
} }
@ -83,13 +86,16 @@ func (msg *MsgInv) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) erro
return messageError("MsgInv.BtcEncode", str) 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 { if err != nil {
return err return err
} }
for _, iv := range msg.InvList { for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv) err := writeInvVectBuf(w, pver, iv, buf)
if err != nil { if err != nil {
return err return err
} }

View file

@ -49,18 +49,21 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncodi
return messageError("MsgMerkleBlock.BtcDecode", str) 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 { if err != nil {
return err return err
} }
err = readElement(r, &msg.Transactions) if _, err := io.ReadFull(r, buf[:4]); err != nil {
if err != nil {
return err return err
} }
msg.Transactions = littleEndian.Uint32(buf[:4])
// Read num block locator hashes and limit to max. // Read num block locator hashes and limit to max.
count, err := ReadVarInt(r, pver) count, err := ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
return err return err
} }
@ -76,14 +79,14 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncodi
msg.Hashes = make([]*chainhash.Hash, 0, count) msg.Hashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
hash := &hashes[i] hash := &hashes[i]
err := readElement(r, hash) _, err := io.ReadFull(r, hash[:])
if err != nil { if err != nil {
return err return err
} }
msg.AddTxHash(hash) msg.AddTxHash(hash)
} }
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock, msg.Flags, err = ReadVarBytesBuf(r, pver, buf, maxFlagsPerMerkleBlock,
"merkle block flags size") "merkle block flags size")
return err return err
} }
@ -111,28 +114,32 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncodi
return messageError("MsgMerkleBlock.BtcDecode", str) 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 { if err != nil {
return err return err
} }
err = writeElement(w, msg.Transactions) littleEndian.PutUint32(buf[:4], msg.Transactions)
if err != nil { if _, err := w.Write(buf[:4]); err != nil {
return err return err
} }
err = WriteVarInt(w, pver, uint64(numHashes)) err = WriteVarIntBuf(w, pver, uint64(numHashes), buf)
if err != nil { if err != nil {
return err return err
} }
for _, hash := range msg.Hashes { for _, hash := range msg.Hashes {
err = writeElement(w, hash) _, err := w.Write(hash[:])
if err != nil { if err != nil {
return err 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 // 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. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 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 { if err != nil {
return err return err
} }
@ -52,7 +55,7 @@ func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding)
msg.InvList = make([]*InvVect, 0, count) msg.InvList = make([]*InvVect, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
iv := &invList[i] iv := &invList[i]
err := readInvVect(r, pver, iv) err := readInvVectBuf(r, pver, iv, buf)
if err != nil { if err != nil {
return err return err
} }
@ -72,13 +75,16 @@ func (msg *MsgNotFound) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding)
return messageError("MsgNotFound.BtcEncode", str) 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 { if err != nil {
return err return err
} }
for _, iv := range msg.InvList { for _, iv := range msg.InvList {
err := writeInvVect(w, pver, iv) err := writeInvVectBuf(w, pver, iv, buf)
if err != nil { if err != nil {
return err 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 // NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others. // the version unlike most others.
if pver > BIP0031Version { if pver > BIP0031Version {
err := readElement(r, &msg.Nonce) nonce, err := binarySerializer.Uint64(r, littleEndian)
if err != nil { if err != nil {
return err return err
} }
msg.Nonce = nonce
} }
return nil 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 // NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
// the version unlike most others. // the version unlike most others.
if pver > BIP0031Version { if pver > BIP0031Version {
err := writeElement(w, msg.Nonce) err := binarySerializer.PutUint64(w, littleEndian, msg.Nonce)
if err != nil { if err != nil {
return err 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 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. // 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 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 // 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. // 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 { if err != nil {
return err return err
} }
msg.Cmd = cmd msg.Cmd = cmd
// Code indicating why the command was rejected. // Code indicating why the command was rejected.
err = readElement(r, &msg.Code) if _, err := io.ReadFull(r, buf[:1]); err != nil {
if err != nil {
return err return err
} }
msg.Code = RejectCode(buf[0])
// Human readable string with specific details (over and above the // Human readable string with specific details (over and above the
// reject code above) about why the command was rejected. // reject code above) about why the command was rejected.
reason, err := ReadVarString(r, pver) reason, err := readVarStringBuf(r, pver, buf)
if err != nil { if err != nil {
return err 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 // CmdBlock and CmdTx messages have an additional hash field that
// identifies the specific block or transaction. // identifies the specific block or transaction.
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx { if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
err := readElement(r, &msg.Hash) _, err := io.ReadFull(r, msg.Hash[:])
if err != nil { if err != nil {
return err return err
} }
@ -123,20 +126,23 @@ func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) e
} }
// Command that was rejected. // 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 { if err != nil {
return err return err
} }
// Code indicating why the command was rejected. // Code indicating why the command was rejected.
err = writeElement(w, msg.Code) buf[0] = byte(msg.Code)
if err != nil { if _, err := w.Write(buf[:1]); err != nil {
return err return err
} }
// Human readable string with specific details (over and above the // Human readable string with specific details (over and above the
// reject code above) about why the command was rejected. // 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 { if err != nil {
return err 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 // CmdBlock and CmdTx messages have an additional hash field that
// identifies the specific block or transaction. // identifies the specific block or transaction.
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx { if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
err := writeElement(w, &msg.Hash) _, err := w.Write(msg.Hash[:])
if err != nil { if err != nil {
return err return err
} }

View file

@ -93,7 +93,7 @@ const (
// scripts per transaction being simultaneously deserialized by 125 // scripts per transaction being simultaneously deserialized by 125
// peers. Thus, the peak usage of the free list is 12,500 * 512 = // peers. Thus, the peak usage of the free list is 12,500 * 512 =
// 6,400,000 bytes. // 6,400,000 bytes.
freeListMaxItems = 12500 freeListMaxItems = 125
// maxWitnessItemsPerInput is the maximum number of witness items to // maxWitnessItemsPerInput is the maximum number of witness items to
// be read for the witness data for a single TxIn. This number is // be read for the witness data for a single TxIn. This number is
@ -146,6 +146,10 @@ const (
WitnessFlag TxFlag = 0x01 WitnessFlag TxFlag = 0x01
) )
const scriptSlabSize = 1 << 22
type scriptSlab [scriptSlabSize]byte
// scriptFreeList defines a free list of byte slices (up to the maximum number // 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 // defined by the freeListMaxItems constant) that have a cap according to the
// freeListMaxScriptSize constant. It is used to provide temporary buffers for // 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 // 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. // 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 // 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. // 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 // 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 // to attempt to return said buffer via the Return function as it will be
// ignored and allowed to go the garbage collector. // ignored and allowed to go the garbage collector.
func (c scriptFreeList) Borrow(size uint64) []byte { func (c scriptFreeList) Borrow() *scriptSlab {
if size > freeListMaxScriptSize { var buf *scriptSlab
return make([]byte, size)
}
var buf []byte
select { select {
case buf = <-c: case buf = <-c:
default: 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 // 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 // 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 // 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. // are simply ignored so they can go to the garbage collector.
func (c scriptFreeList) Return(buf []byte) { func (c scriptFreeList) Return(buf *scriptSlab) {
// Ignore any buffers returned that aren't the expected size for the
// free list.
if cap(buf) != freeListMaxScriptSize {
return
}
// Return the buffer to the free list when it's not full. Otherwise let // Return the buffer to the free list when it's not full. Otherwise let
// it be garbage collected. // it be garbage collected.
select { select {
@ -201,7 +195,7 @@ func (c scriptFreeList) Return(buf []byte) {
// Create the concurrent safe free list to use for script deserialization. As // Create the concurrent safe free list to use for script deserialization. As
// previously described, this free list is maintained to significantly reduce // previously described, this free list is maintained to significantly reduce
// the number of allocations. // 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 // OutPoint defines a bitcoin data type that is used to track previous
// transaction outputs. // transaction outputs.
@ -452,13 +446,25 @@ func (msg *MsgTx) Copy() *MsgTx {
// See Deserialize for decoding transactions stored to disk, such as in a // See Deserialize for decoding transactions stored to disk, such as in a
// database, as opposed to decoding transactions from the wire. // database, as opposed to decoding transactions from the wire.
func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
version, err := binarySerializer.Uint32(r, littleEndian) buf := binarySerializer.Borrow()
if err != nil { 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 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 { if err != nil {
return err 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 // With the Segregated Witness specific fields decoded, we can
// now read in the actual txin count. // now read in the actual txin count.
count, err = ReadVarInt(r, pver) count, err = ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
return err return err
} }
@ -498,35 +504,6 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
return messageError("MsgTx.BtcDecode", str) 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. // Deserialize the inputs.
var totalScriptSize uint64 var totalScriptSize uint64
txIns := make([]TxIn, count) 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. // and needs to be returned to the pool on error.
ti := &txIns[i] ti := &txIns[i]
msg.TxIn[i] = ti msg.TxIn[i] = ti
err = readTxIn(r, pver, msg.Version, ti) err = readTxInBuf(r, pver, msg.Version, ti, buf, sbuf)
if err != nil { if err != nil {
returnScriptBuffers()
return err return err
} }
totalScriptSize += uint64(len(ti.SignatureScript)) totalScriptSize += uint64(len(ti.SignatureScript))
sbuf = sbuf[len(ti.SignatureScript):]
} }
count, err = ReadVarInt(r, pver) count, err = ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
returnScriptBuffers()
return err 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 // message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count. // without a sane upper bound on this count.
if count > uint64(maxTxOutPerMessage) { if count > uint64(maxTxOutPerMessage) {
returnScriptBuffers()
str := fmt.Sprintf("too many output transactions to fit into "+ str := fmt.Sprintf("too many output transactions to fit into "+
"max message size [count %d, max %d]", count, "max message size [count %d, max %d]", count,
maxTxOutPerMessage) 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. // and needs to be returned to the pool on error.
to := &txOuts[i] to := &txOuts[i]
msg.TxOut[i] = to msg.TxOut[i] = to
err = ReadTxOut(r, pver, msg.Version, to) err = readTxOutBuf(r, pver, msg.Version, to, buf, sbuf)
if err != nil { if err != nil {
returnScriptBuffers()
return err return err
} }
totalScriptSize += uint64(len(to.PkScript)) 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 // 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 // For each input, the witness is encoded as a stack
// with one or more items. Therefore, we first read a // with one or more items. Therefore, we first read a
// varint which encodes the number of stack items. // varint which encodes the number of stack items.
witCount, err := ReadVarInt(r, pver) witCount, err := ReadVarIntBuf(r, pver, buf)
if err != nil { if err != nil {
returnScriptBuffers()
return err return err
} }
// Prevent a possible memory exhaustion attack by // Prevent a possible memory exhaustion attack by
// limiting the witCount value to a sane upper bound. // limiting the witCount value to a sane upper bound.
if witCount > maxWitnessItemsPerInput { if witCount > maxWitnessItemsPerInput {
returnScriptBuffers()
str := fmt.Sprintf("too many witness items to fit "+ str := fmt.Sprintf("too many witness items to fit "+
"into max message size [count %d, max %d]", "into max message size [count %d, max %d]",
witCount, maxWitnessItemsPerInput) witCount, maxWitnessItemsPerInput)
@ -605,23 +578,23 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
// item itself. // item itself.
txin.Witness = make([][]byte, witCount) txin.Witness = make([][]byte, witCount)
for j := uint64(0); j < witCount; j++ { for j := uint64(0); j < witCount; j++ {
txin.Witness[j], err = readScript( txin.Witness[j], err = readScriptBuf(
r, pver, maxWitnessItemSize, "script witness item", r, pver, buf, sbuf, maxWitnessItemSize,
"script witness item",
) )
if err != nil { if err != nil {
returnScriptBuffers()
return err return err
} }
totalScriptSize += uint64(len(txin.Witness[j])) totalScriptSize += uint64(len(txin.Witness[j]))
sbuf = sbuf[len(txin.Witness[j]):]
} }
} }
} }
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian) if _, err := io.ReadFull(r, buf[:4]); err != nil {
if err != nil {
returnScriptBuffers()
return err return err
} }
msg.LockTime = littleEndian.Uint32(buf[:4])
// Create a single allocation to house all of the scripts and set each // Create a single allocation to house all of the scripts and set each
// input signature script and output public key script to the // 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] msg.TxIn[i].SignatureScript = scripts[offset:end:end]
offset += scriptSize offset += scriptSize
// Return the temporary script buffer to the pool.
scriptPool.Return(signatureScript)
for j := 0; j < len(msg.TxIn[i].Witness); j++ { for j := 0; j < len(msg.TxIn[i].Witness); j++ {
// Copy each item within the witness stack for this // Copy each item within the witness stack for this
// input into the contiguous buffer at the appropriate // 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 end := offset + witnessElemSize
msg.TxIn[i].Witness[j] = scripts[offset:end:end] msg.TxIn[i].Witness[j] = scripts[offset:end:end]
offset += witnessElemSize 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++ { 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 end := offset + scriptSize
msg.TxOut[i].PkScript = scripts[offset:end:end] msg.TxOut[i].PkScript = scripts[offset:end:end]
offset += scriptSize offset += scriptSize
// Return the temporary script buffer to the pool.
scriptPool.Return(pkScript)
} }
return nil 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 // See Serialize for encoding transactions to be stored to disk, such as in a
// database, as opposed to encoding transactions for the wire. // database, as opposed to encoding transactions for the wire.
func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error { func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
err := binarySerializer.PutUint32(w, littleEndian, uint32(msg.Version)) buf := binarySerializer.Borrow()
if err != nil { 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 return err
} }
@ -745,26 +718,26 @@ func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error
} }
count := uint64(len(msg.TxIn)) count := uint64(len(msg.TxIn))
err = WriteVarInt(w, pver, count) err := WriteVarIntBuf(w, pver, count, buf)
if err != nil { if err != nil {
return err return err
} }
for _, ti := range msg.TxIn { for _, ti := range msg.TxIn {
err = writeTxIn(w, pver, msg.Version, ti) err = writeTxInBuf(w, pver, msg.Version, ti, buf)
if err != nil { if err != nil {
return err return err
} }
} }
count = uint64(len(msg.TxOut)) count = uint64(len(msg.TxOut))
err = WriteVarInt(w, pver, count) err = WriteVarIntBuf(w, pver, count, buf)
if err != nil { if err != nil {
return err return err
} }
for _, to := range msg.TxOut { for _, to := range msg.TxOut {
err = WriteTxOut(w, pver, msg.Version, to) err = WriteTxOutBuf(w, pver, msg.Version, to, buf)
if err != nil { if err != nil {
return err return err
} }
@ -775,14 +748,16 @@ func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error
// within the transaction. // within the transaction.
if doWitness { if doWitness {
for _, ti := range msg.TxIn { 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 { if err != nil {
return err 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 // 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. // readOutPointBuf reads the next sequence of bytes from r as an OutPoint.
func readOutPoint(r io.Reader, 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 readOutPointBuf(r io.Reader, pver uint32, version int32, op *OutPoint,
buf []byte) error {
_, err := io.ReadFull(r, op.Hash[:]) _, err := io.ReadFull(r, op.Hash[:])
if err != nil { if err != nil {
return err 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 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. // 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[:]) _, err := w.Write(op.Hash[:])
if err != nil { if err != nil {
return err 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 // 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 // memory exhaustion attacks and forced panics through malformed messages. The
// fieldName parameter is only used for the error message so it provides more // fieldName parameter is only used for the error message so it provides more
// context in the error. // 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 { if err != nil {
return nil, err return nil, err
} }
@ -983,58 +998,96 @@ func readScript(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) (
return nil, messageError("readScript", str) return nil, messageError("readScript", str)
} }
b := scriptPool.Borrow(count) _, err = io.ReadFull(r, s[:count])
_, err = io.ReadFull(r, b)
if err != nil { if err != nil {
scriptPool.Return(b)
return nil, err 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). // (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 { if err != nil {
return err return err
} }
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload, ti.SignatureScript, err = readScriptBuf(r, pver, buf, s, MaxMessagePayload,
"transaction input signature script") "transaction input signature script")
if err != nil { if err != nil {
return err 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 // writeTxInBuf encodes ti to the bitcoin protocol encoding for a transaction
// input (TxIn) to w. // input (TxIn) to w. If b is non-nil, the provided buffer will be used for
func writeTxIn(w io.Writer, pver uint32, version int32, ti *TxIn) error { // serializing small values. Otherwise a buffer will be drawn from the
err := WriteOutPoint(w, pver, version, &ti.PreviousOutPoint) // 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 { if err != nil {
return err return err
} }
err = WriteVarBytes(w, pver, ti.SignatureScript) err = WriteVarBytesBuf(w, pver, ti.SignatureScript, buf)
if err != nil { if err != nil {
return err 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 // ReadTxOut reads the next sequence of bytes from r as a transaction output
// (TxOut). // (TxOut).
func ReadTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error { 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 { if err != nil {
return err return err
} }
to.Value = int64(littleEndian.Uint64(buf))
to.PkScript, err = readScript(r, pver, MaxMessagePayload, to.PkScript, err = readScriptBuf(
"transaction output public key script") r, pver, buf, s, MaxMessagePayload,
"transaction output public key script",
)
return err 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 // NOTE: This function is exported in order to allow txscript to compute the
// new sighashes for witness transactions (BIP0143). // new sighashes for witness transactions (BIP0143).
func WriteTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error { 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 { if err != nil {
return err return err
} }
return WriteVarBytes(w, pver, to.PkScript) return WriteVarBytesBuf(w, pver, to.PkScript, buf)
} }
// writeTxWitness encodes the bitcoin protocol encoding for a transaction // writeTxWitnessBuf encodes the bitcoin protocol encoding for a transaction
// input's witness into to w. // input's witness into to w. If b is non-nil, the provided buffer will be used
func writeTxWitness(w io.Writer, pver uint32, version int32, wit [][]byte) error { // for serializing small values. Otherwise a buffer will be drawn from the
err := WriteVarInt(w, pver, uint64(len(wit))) // 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 { if err != nil {
return err return err
} }
for _, item := range wit { for _, item := range wit {
err = WriteVarBytes(w, pver, item) err = WriteVarBytesBuf(w, pver, item, buf)
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }

View file

@ -5,7 +5,6 @@
package wire package wire
import ( import (
"encoding/binary"
"io" "io"
"net" "net"
"time" "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 // version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp. // like version do not include the timestamp.
func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error { 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 // NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
// stop working somewhere around 2106. Also timestamp wasn't added until // stop working somewhere around 2106. Also timestamp wasn't added until
// protocol version >= NetAddressTimeVersion // protocol version >= NetAddressTimeVersion
if ts && pver >= NetAddressTimeVersion { if ts && pver >= NetAddressTimeVersion {
err := readElement(r, (*uint32Time)(&na.Timestamp)) if _, err := io.ReadFull(r, buf[:4]); err != nil {
if err != nil {
return err return err
} }
timestamp = time.Unix(int64(littleEndian.Uint32(buf[:4])), 0)
} }
err := readElements(r, &na.Services, &ip) if _, err := io.ReadFull(r, buf); err != nil {
if err != nil {
return err 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. // Sigh. Bitcoin protocol mixes little and big endian.
port, err := binarySerializer.Uint16(r, bigEndian) if _, err := io.ReadFull(r, buf[:2]); err != nil {
if err != nil {
return err return err
} }
port = bigEndian.Uint16(buf[:2])
*na = NetAddress{ *na = NetAddress{
Timestamp: na.Timestamp, Timestamp: timestamp,
Services: na.Services, Services: services,
IP: net.IP(ip[:]), IP: net.IP(ip[:]),
Port: port, 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 // version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp. // like version do not include the timestamp.
func writeNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error { 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 // NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
// stop working somewhere around 2106. Also timestamp wasn't added until // stop working somewhere around 2106. Also timestamp wasn't added until
// until protocol version >= NetAddressTimeVersion. // until protocol version >= NetAddressTimeVersion.
if ts && pver >= NetAddressTimeVersion { if ts && pver >= NetAddressTimeVersion {
err := writeElement(w, uint32(na.Timestamp.Unix())) littleEndian.PutUint32(buf[:4], uint32(na.Timestamp.Unix()))
if err != nil { if _, err := w.Write(buf[:4]); err != nil {
return err 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. // Ensure to always write 16 bytes even if the ip is nil.
var ip [16]byte var ip [16]byte
if na.IP != nil { if na.IP != nil {
copy(ip[:], na.IP.To16()) copy(ip[:], na.IP.To16())
} }
err := writeElements(w, na.Services, ip) if _, err := w.Write(ip[:]); err != nil {
if err != nil {
return err return err
} }
// Sigh. Bitcoin protocol mixes little and big endian. // 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
} }