Remove dependency on protocol version.
This commit unfortunately changes the public API of Block which I ordinarily don't like to do, but in this case, I felt it was necessary. The blocks used throughout the database and elsewhere should be indepedent of the protocol version which is used to encode the block to wire format. Each block has its own Version field which should be the deciding factor for the serialization and deserialization of blocks. In practice, they are currently the same encoding, but that may not always be the case, and it's important the blocks are stable depending on their own version regardless of the protocol version. This makes use of the new Serialize and Deserialize functions on MsgBlock which are intended for long-term storage as opposed to wire encoding.
This commit is contained in:
parent
761970e639
commit
43095c66bc
3 changed files with 75 additions and 110 deletions
89
block.go
89
block.go
|
@ -25,13 +25,12 @@ func (e OutOfRangeError) Error() string {
|
|||
}
|
||||
|
||||
// Block defines a bitcoin block that provides easier and more efficient
|
||||
// manipulation of raw wire protocol blocks. It also memoizes hashes for the
|
||||
// block and its transactions on their first access so subsequent accesses don't
|
||||
// have to repeat the relatively expensive hashing operations.
|
||||
// manipulation of raw blocks. It also memoizes hashes for the block and its
|
||||
// transactions on their first access so subsequent accesses don't have to
|
||||
// repeat the relatively expensive hashing operations.
|
||||
type Block struct {
|
||||
msgBlock *btcwire.MsgBlock // Underlying MsgBlock
|
||||
rawBlock []byte // Raw wire encoded bytes for the block
|
||||
protocolVersion uint32 // Protocol version used to encode rawBlock
|
||||
serializedBlock []byte // Serialized bytes for the block
|
||||
blockSha *btcwire.ShaHash // Cached block hash
|
||||
blockHeight int64 // Height in the main block chain
|
||||
txShas []*btcwire.ShaHash // Cached transaction hashes
|
||||
|
@ -44,28 +43,26 @@ func (b *Block) MsgBlock() *btcwire.MsgBlock {
|
|||
return b.msgBlock
|
||||
}
|
||||
|
||||
// Bytes returns the raw wire protocol encoded bytes for the Block and the
|
||||
// protocol version used to encode it. This is equivalent to calling BtcEncode
|
||||
// on the underlying btcwire.MsgBlock, however it caches the result so
|
||||
// subsequent calls are more efficient.
|
||||
func (b *Block) Bytes() ([]byte, uint32, error) {
|
||||
// Return the cached raw block bytes and associated protocol version if
|
||||
// it has already been generated.
|
||||
if len(b.rawBlock) != 0 {
|
||||
return b.rawBlock, b.protocolVersion, nil
|
||||
// Bytes returns the serialized bytes for the Block. This is equivalent to
|
||||
// calling Serialize on the underlying btcwire.MsgBlock, however it caches the
|
||||
// result so subsequent calls are more efficient.
|
||||
func (b *Block) Bytes() ([]byte, error) {
|
||||
// Return the cached serialized bytes if it has already been generated.
|
||||
if len(b.serializedBlock) != 0 {
|
||||
return b.serializedBlock, nil
|
||||
}
|
||||
|
||||
// Encode the MsgBlock into raw block bytes.
|
||||
// Serialize the MsgBlock.
|
||||
var w bytes.Buffer
|
||||
err := b.msgBlock.BtcEncode(&w, b.protocolVersion)
|
||||
err := b.msgBlock.Serialize(&w)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
rawBlock := w.Bytes()
|
||||
serializedBlock := w.Bytes()
|
||||
|
||||
// Cache the encoded bytes and return them.
|
||||
b.rawBlock = rawBlock
|
||||
return rawBlock, b.protocolVersion, nil
|
||||
// Cache the serialized bytes and return them.
|
||||
b.serializedBlock = serializedBlock
|
||||
return serializedBlock, nil
|
||||
}
|
||||
|
||||
// Sha returns the block identifier hash for the Block. This is equivalent to
|
||||
|
@ -79,7 +76,7 @@ func (b *Block) Sha() (*btcwire.ShaHash, error) {
|
|||
|
||||
// Generate the block hash. Ignore the error since BlockSha can't
|
||||
// currently fail.
|
||||
sha, _ := b.msgBlock.BlockSha(b.protocolVersion)
|
||||
sha, _ := b.msgBlock.BlockSha()
|
||||
|
||||
// Cache the block hash and return it.
|
||||
b.blockSha = &sha
|
||||
|
@ -112,7 +109,7 @@ func (b *Block) TxSha(txNum int) (*btcwire.ShaHash, error) {
|
|||
|
||||
// Generate the hash for the transaction. Ignore the error since TxSha
|
||||
// can't currently fail.
|
||||
sha, _ := b.msgBlock.Transactions[txNum].TxSha(b.protocolVersion)
|
||||
sha, _ := b.msgBlock.Transactions[txNum].TxSha()
|
||||
|
||||
// Cache the transaction hash and return it.
|
||||
b.txShas[txNum] = &sha
|
||||
|
@ -140,7 +137,7 @@ func (b *Block) TxShas() ([]*btcwire.ShaHash, error) {
|
|||
for i, hash := range b.txShas {
|
||||
if hash == nil {
|
||||
// Ignore the error since TxSha can't currently fail.
|
||||
sha, _ := b.msgBlock.Transactions[i].TxSha(b.protocolVersion)
|
||||
sha, _ := b.msgBlock.Transactions[i].TxSha()
|
||||
b.txShas[i] = &sha
|
||||
}
|
||||
}
|
||||
|
@ -149,79 +146,69 @@ func (b *Block) TxShas() ([]*btcwire.ShaHash, error) {
|
|||
return b.txShas, nil
|
||||
}
|
||||
|
||||
// ProtocolVersion returns the protocol version that was used to create the
|
||||
// underlying btcwire.MsgBlock.
|
||||
func (b *Block) ProtocolVersion() uint32 {
|
||||
return b.protocolVersion
|
||||
}
|
||||
|
||||
// TxLoc returns the offsets and lengths of each transaction in a raw block.
|
||||
// It is used to allow fast indexing into transactions within the raw byte
|
||||
// stream.
|
||||
func (b *Block) TxLoc() ([]btcwire.TxLoc, error) {
|
||||
rawMsg, pver, err := b.Bytes()
|
||||
rawMsg, err := b.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rbuf := bytes.NewBuffer(rawMsg)
|
||||
|
||||
var mblock btcwire.MsgBlock
|
||||
txLocs, err := mblock.BtcDecodeTxLoc(rbuf, pver)
|
||||
txLocs, err := mblock.DeserializeTxLoc(rbuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txLocs, err
|
||||
}
|
||||
|
||||
// Height returns the saved height of the block in the blockchain. This value
|
||||
// Height returns the saved height of the block in the block chain. This value
|
||||
// will be BlockHeightUnknown if it hasn't already explicitly been set.
|
||||
func (b *Block) Height() int64 {
|
||||
return b.blockHeight
|
||||
}
|
||||
|
||||
// SetHeight sets the height of the block in the blockchain.
|
||||
// SetHeight sets the height of the block in the block chain.
|
||||
func (b *Block) SetHeight(height int64) {
|
||||
b.blockHeight = height
|
||||
}
|
||||
|
||||
// NewBlock returns a new instance of a bitcoin block given an underlying
|
||||
// btcwire.MsgBlock and protocol version. See Block.
|
||||
func NewBlock(msgBlock *btcwire.MsgBlock, pver uint32) *Block {
|
||||
// btcwire.MsgBlock. See Block.
|
||||
func NewBlock(msgBlock *btcwire.MsgBlock) *Block {
|
||||
return &Block{
|
||||
msgBlock: msgBlock,
|
||||
protocolVersion: pver,
|
||||
blockHeight: BlockHeightUnknown,
|
||||
msgBlock: msgBlock,
|
||||
blockHeight: BlockHeightUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBlockFromBytes returns a new instance of a bitcoin block given the
|
||||
// raw wire encoded bytes and protocol version used to encode those bytes.
|
||||
// See Block.
|
||||
func NewBlockFromBytes(rawBlock []byte, pver uint32) (*Block, error) {
|
||||
// Decode the raw block bytes into a MsgBlock.
|
||||
// serialized bytes. See Block.
|
||||
func NewBlockFromBytes(serializedBlock []byte) (*Block, error) {
|
||||
// Deserialize the bytes into a MsgBlock.
|
||||
var msgBlock btcwire.MsgBlock
|
||||
br := bytes.NewBuffer(rawBlock)
|
||||
err := msgBlock.BtcDecode(br, pver)
|
||||
br := bytes.NewBuffer(serializedBlock)
|
||||
err := msgBlock.Deserialize(br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := Block{
|
||||
msgBlock: &msgBlock,
|
||||
rawBlock: rawBlock,
|
||||
protocolVersion: pver,
|
||||
serializedBlock: serializedBlock,
|
||||
blockHeight: BlockHeightUnknown,
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
// NewBlockFromBlockAndBytes returns a new instance of a bitcoin block given
|
||||
// an underlying btcwire.MsgBlock, protocol version and raw Block. See Block.
|
||||
func NewBlockFromBlockAndBytes(msgBlock *btcwire.MsgBlock, rawBlock []byte, pver uint32) *Block {
|
||||
// an underlying btcwire.MsgBlock and the serialized bytes for it. See Block.
|
||||
func NewBlockFromBlockAndBytes(msgBlock *btcwire.MsgBlock, serializedBlock []byte) *Block {
|
||||
return &Block{
|
||||
msgBlock: msgBlock,
|
||||
rawBlock: rawBlock,
|
||||
protocolVersion: pver,
|
||||
serializedBlock: serializedBlock,
|
||||
blockHeight: BlockHeightUnknown,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,9 @@ import (
|
|||
|
||||
// TestBlock tests the API for Block.
|
||||
func TestBlock(t *testing.T) {
|
||||
pver := btcwire.ProtocolVersion
|
||||
b := btcutil.NewBlock(&Block100000, pver)
|
||||
b := btcutil.NewBlock(&Block100000)
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
if gotPver := b.ProtocolVersion(); gotPver != pver {
|
||||
t.Errorf("ProtocolVersion: wrong protocol version - got %v, want %v",
|
||||
gotPver, pver)
|
||||
}
|
||||
if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) {
|
||||
t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v",
|
||||
spew.Sdump(msgBlock), spew.Sdump(&Block100000))
|
||||
|
@ -89,7 +84,7 @@ func TestBlock(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create a new block to nuke all cached data.
|
||||
b = btcutil.NewBlock(&Block100000, pver)
|
||||
b = btcutil.NewBlock(&Block100000)
|
||||
|
||||
// Request slice of all transaction shas multiple times to test
|
||||
// generation and caching.
|
||||
|
@ -125,34 +120,28 @@ func TestBlock(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Encode the test block to bytes.
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
err = Block100000.BtcEncode(&block100000Buf, pver)
|
||||
err = Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("BtcEncode: %v", err)
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Request raw bytes multiple times to test generation and caching.
|
||||
// Request serialized bytes multiple times to test generation and
|
||||
// caching.
|
||||
for i := 0; i < 2; i++ {
|
||||
rawBytes, tmpPver, err := b.Bytes()
|
||||
serializedBytes, err := b.Bytes()
|
||||
if err != nil {
|
||||
t.Errorf("Bytes: %v", err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(rawBytes, block100000Bytes) {
|
||||
if !bytes.Equal(serializedBytes, block100000Bytes) {
|
||||
t.Errorf("Bytes #%d wrong bytes - got %v, want %v", i,
|
||||
spew.Sdump(rawBytes),
|
||||
spew.Sdump(serializedBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
continue
|
||||
}
|
||||
if tmpPver != pver {
|
||||
t.Errorf("Bytes #%d wrong protocol version - "+
|
||||
"got %v, want %v", i, spew.Sdump(rawBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
continue
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction offsets and length for the transaction in Block100000.
|
||||
|
@ -176,39 +165,34 @@ func TestBlock(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestNewBlockFromBytes tests creation of a Block from raw bytes.
|
||||
// TestNewBlockFromBytes tests creation of a Block from serialized bytes.
|
||||
func TestNewBlockFromBytes(t *testing.T) {
|
||||
// Encode the test block to bytes.
|
||||
pver := btcwire.ProtocolVersion
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
err := Block100000.BtcEncode(&block100000Buf, pver)
|
||||
err := Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("BtcEncode: %v", err)
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Create a new block from the encoded bytes.
|
||||
b, err := btcutil.NewBlockFromBytes(block100000Bytes, pver)
|
||||
// Create a new block from the serialized bytes.
|
||||
b, err := btcutil.NewBlockFromBytes(block100000Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("NewBlockFromBytes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
rawBytes, tmpPver, err := b.Bytes()
|
||||
serializedBytes, err := b.Bytes()
|
||||
if err != nil {
|
||||
t.Errorf("Bytes: %v", err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(rawBytes, block100000Bytes) {
|
||||
if !bytes.Equal(serializedBytes, block100000Bytes) {
|
||||
t.Errorf("Bytes: wrong bytes - got %v, want %v",
|
||||
spew.Sdump(rawBytes),
|
||||
spew.Sdump(serializedBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
}
|
||||
if tmpPver != pver {
|
||||
t.Errorf("Bytes: wrong protocol version - got %v, want %v",
|
||||
tmpPver, pver)
|
||||
}
|
||||
|
||||
// Ensure the generated MsgBlock is correct.
|
||||
if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) {
|
||||
|
@ -220,34 +204,28 @@ func TestNewBlockFromBytes(t *testing.T) {
|
|||
// TestNewBlockFromBlockAndBytes tests creation of a Block from a MsgBlock and
|
||||
// raw bytes.
|
||||
func TestNewBlockFromBlockAndBytes(t *testing.T) {
|
||||
// Encode the test block to bytes.
|
||||
pver := btcwire.ProtocolVersion
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
err := Block100000.BtcEncode(&block100000Buf, pver)
|
||||
err := Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("BtcEncode: %v", err)
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Create a new block from the encoded bytes.
|
||||
b := btcutil.NewBlockFromBlockAndBytes(&Block100000,
|
||||
block100000Bytes, pver)
|
||||
// Create a new block from the serialized bytes.
|
||||
b := btcutil.NewBlockFromBlockAndBytes(&Block100000, block100000Bytes)
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
rawBytes, tmpPver, err := b.Bytes()
|
||||
serializedBytes, err := b.Bytes()
|
||||
if err != nil {
|
||||
t.Errorf("Bytes: %v", err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(rawBytes, block100000Bytes) {
|
||||
if !bytes.Equal(serializedBytes, block100000Bytes) {
|
||||
t.Errorf("Bytes: wrong bytes - got %v, want %v",
|
||||
spew.Sdump(rawBytes),
|
||||
spew.Sdump(serializedBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
}
|
||||
if tmpPver != pver {
|
||||
t.Errorf("Bytes: wrong protocol version - got %v, want %v",
|
||||
tmpPver, pver)
|
||||
}
|
||||
if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) {
|
||||
t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v",
|
||||
spew.Sdump(msgBlock), spew.Sdump(&Block100000))
|
||||
|
@ -264,17 +242,16 @@ func TestBlockErrors(t *testing.T) {
|
|||
testErr.Error(), wantErr)
|
||||
}
|
||||
|
||||
// Encode the test block to bytes.
|
||||
pver := btcwire.ProtocolVersion
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
err := Block100000.BtcEncode(&block100000Buf, pver)
|
||||
err := Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("BtcEncode: %v", err)
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Create a new block from the encoded bytes.
|
||||
b, err := btcutil.NewBlockFromBytes(block100000Bytes, pver)
|
||||
// Create a new block from the serialized bytes.
|
||||
b, err := btcutil.NewBlockFromBytes(block100000Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("NewBlockFromBytes: %v", err)
|
||||
return
|
||||
|
@ -282,7 +259,7 @@ func TestBlockErrors(t *testing.T) {
|
|||
|
||||
// Truncate the block byte buffer to force errors.
|
||||
shortBytes := block100000Bytes[:80]
|
||||
_, err = btcutil.NewBlockFromBytes(shortBytes, pver)
|
||||
_, err = btcutil.NewBlockFromBytes(shortBytes)
|
||||
if err != io.EOF {
|
||||
t.Errorf("NewBlockFromBytes: did not get expected error - "+
|
||||
"got %v, want %v", err, io.EOF)
|
||||
|
|
|
@ -11,8 +11,9 @@ interface. The functions are only exported while the tests are being run.
|
|||
|
||||
package btcutil
|
||||
|
||||
// SetBlockBytes sets the internal raw block byte buffer to the passed buffer.
|
||||
// It is used to inject errors and only available to the test package.
|
||||
// SetBlockBytes sets the internal serialized block byte buffer to the passed
|
||||
// buffer. It is used to inject errors and is only available to the test
|
||||
// package.
|
||||
func (b *Block) SetBlockBytes(buf []byte) {
|
||||
b.rawBlock = buf
|
||||
b.serializedBlock = buf
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue