Expose new SerializeSize API for blocks.

This commit adds a new function named SerializeSize to the public API for
MsgBlock which can be used to determine how many bytes the serialized data would
take without having to actually serialize it.  In addition, it makes the
exported BlockVersion an untyped constant as well as changes the block and
tx versions to a signed integer to more closely match the protocol.

Finally, this commit also adds tests for the new function.

The following benchmark shows the difference between using the new
function to get the serialize size for a typical block and serializing
into a temporary buffer and taking the length of it:

Bufffer: BenchmarkBlockSerializeSizeBuffer     200000          27050 ns/op
New:     BenchmarkBlockSerializeSizeNew     100000000             34 ns/op

Closes #19.
This commit is contained in:
Dave Collins 2014-06-29 14:40:20 -05:00
parent 7dcb68275f
commit 843e71515a
5 changed files with 59 additions and 17 deletions

View file

@ -11,7 +11,7 @@ import (
)
// BlockVersion is the current latest supported block version.
const BlockVersion uint32 = 2
const BlockVersion = 2
// Version 4 bytes + Timestamp 4 bytes + Bits 4 bytes + Nonce 4 bytes +
// PrevBlock and MerkleRoot hashes.
@ -21,7 +21,7 @@ const MaxBlockHeaderPayload = 16 + (HashSize * 2)
// block (MsgBlock) and headers (MsgHeaders) messages.
type BlockHeader struct {
// Version of the block. This is not the same as the protocol version.
Version uint32
Version int32
// Hash of the previous block in the block chain.
PrevBlock ShaHash

View file

@ -141,36 +141,36 @@ func TstReadMessageHeader(r io.Reader) (int, *messageHeader, error) {
// TstReadOutPoint makes the internal readOutPoint function available to the
// test package.
func TstReadOutPoint(r io.Reader, pver uint32, version uint32, op *OutPoint) error {
func TstReadOutPoint(r io.Reader, pver uint32, version int32, op *OutPoint) error {
return readOutPoint(r, pver, version, op)
}
// TstWriteOutPoint makes the internal writeOutPoint function available to the
// test package.
func TstWriteOutPoint(w io.Writer, pver uint32, version uint32, op *OutPoint) error {
func TstWriteOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error {
return writeOutPoint(w, pver, version, op)
}
// TstReadTxOut makes the internal readTxOut function available to the test
// package.
func TstReadTxOut(r io.Reader, pver uint32, version uint32, to *TxOut) error {
func TstReadTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
return readTxOut(r, pver, version, to)
}
// TstWriteTxOut makes the internal writeTxOut function available to the test
// package.
func TstWriteTxOut(w io.Writer, pver uint32, version uint32, to *TxOut) error {
func TstWriteTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error {
return writeTxOut(w, pver, version, to)
}
// TstReadTxIn makes the internal readTxIn function available to the test
// package.
func TstReadTxIn(r io.Reader, pver uint32, version uint32, ti *TxIn) error {
func TstReadTxIn(r io.Reader, pver uint32, version int32, ti *TxIn) error {
return readTxIn(r, pver, version, ti)
}
// TstWriteTxIn makes the internal writeTxIn function available to the test
// package.
func TstWriteTxIn(w io.Writer, pver uint32, version uint32, ti *TxIn) error {
func TstWriteTxIn(w io.Writer, pver uint32, version int32, ti *TxIn) error {
return writeTxIn(w, pver, version, ti)
}

View file

@ -194,6 +194,20 @@ func (msg *MsgBlock) Serialize(w io.Writer) error {
return msg.BtcEncode(w, 0)
}
// SerializeSize returns the number of bytes it would take to serialize the
// the block.
func (msg *MsgBlock) SerializeSize() int {
// Block header bytes + Serialized varint size for the number of
// transactions.
n := blockHeaderLen + VarIntSerializeSize(uint64(len(msg.Transactions)))
for _, tx := range msg.Transactions {
n += tx.SerializeSize()
}
return n
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgBlock) Command() string {

View file

@ -449,6 +449,34 @@ func TestBlockOverflowErrors(t *testing.T) {
}
}
// TestBlockSerializeSize performs tests to ensure the serialize size for
// various blocks is accurate.
func TestBlockSerializeSize(t *testing.T) {
// Block with no transactions.
noTxBlock := btcwire.NewMsgBlock(&blockOne.Header)
tests := []struct {
in *btcwire.MsgBlock // Block to encode
size int // Expected serialized size
}{
// Block with no transactions.
{noTxBlock, 81},
// First block in the mainnet block chain.
{&blockOne, len(blockOneBytes)},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
serializedSize := test.in.SerializeSize()
if serializedSize != test.size {
t.Errorf("MsgBlock.SerializeSize: #%d got: %d, want: "+
"%d", i, serializedSize, test.size)
continue
}
}
}
var blockOne = btcwire.MsgBlock{
Header: btcwire.BlockHeader{
Version: 1,

View file

@ -133,7 +133,7 @@ func NewTxOut(value int64, pkScript []byte) *TxOut {
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
// inputs and outputs.
type MsgTx struct {
Version uint32
Version int32
TxIn []*TxIn
TxOut []*TxOut
LockTime uint32
@ -240,7 +240,7 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
if err != nil {
return err
}
msg.Version = binary.LittleEndian.Uint32(buf[:])
msg.Version = int32(binary.LittleEndian.Uint32(buf[:]))
count, err := readVarInt(r, pver)
if err != nil {
@ -324,7 +324,7 @@ func (msg *MsgTx) Deserialize(r io.Reader) error {
// database, as opposed to encoding transactions for the wire.
func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32) error {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], msg.Version)
binary.LittleEndian.PutUint32(buf[:], uint32(msg.Version))
_, err := w.Write(buf[:])
if err != nil {
return err
@ -428,7 +428,7 @@ func NewMsgTx() *MsgTx {
}
// readOutPoint reads the next sequence of bytes from r as an OutPoint.
func readOutPoint(r io.Reader, pver uint32, version uint32, op *OutPoint) error {
func readOutPoint(r io.Reader, pver uint32, version int32, op *OutPoint) error {
_, err := io.ReadFull(r, op.Hash[:])
if err != nil {
return err
@ -445,7 +445,7 @@ func readOutPoint(r io.Reader, pver uint32, version uint32, op *OutPoint) error
// writeOutPoint encodes op to the bitcoin protocol encoding for an OutPoint
// to w.
func writeOutPoint(w io.Writer, pver uint32, version uint32, op *OutPoint) error {
func writeOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error {
_, err := w.Write(op.Hash[:])
if err != nil {
return err
@ -462,7 +462,7 @@ func writeOutPoint(w io.Writer, pver uint32, version uint32, op *OutPoint) error
// readTxIn reads the next sequence of bytes from r as a transaction input
// (TxIn).
func readTxIn(r io.Reader, pver uint32, version uint32, ti *TxIn) error {
func readTxIn(r io.Reader, pver uint32, version int32, ti *TxIn) error {
var op OutPoint
err := readOutPoint(r, pver, version, &op)
if err != nil {
@ -488,7 +488,7 @@ func readTxIn(r io.Reader, pver uint32, version uint32, ti *TxIn) error {
// writeTxIn encodes ti to the bitcoin protocol encoding for a transaction
// input (TxIn) to w.
func writeTxIn(w io.Writer, pver uint32, version uint32, ti *TxIn) error {
func writeTxIn(w io.Writer, pver uint32, version int32, ti *TxIn) error {
err := writeOutPoint(w, pver, version, &ti.PreviousOutpoint)
if err != nil {
return err
@ -511,7 +511,7 @@ func writeTxIn(w io.Writer, pver uint32, version uint32, ti *TxIn) error {
// readTxOut reads the next sequence of bytes from r as a transaction output
// (TxOut).
func readTxOut(r io.Reader, pver uint32, version uint32, to *TxOut) error {
func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
var buf [8]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
@ -530,7 +530,7 @@ func readTxOut(r io.Reader, pver uint32, version uint32, to *TxOut) error {
// writeTxOut encodes to into the bitcoin protocol encoding for a transaction
// output (TxOut) to w.
func writeTxOut(w io.Writer, pver uint32, version uint32, to *TxOut) error {
func writeTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(to.Value))
_, err := w.Write(buf[:])