227 lines
7.3 KiB
Go
227 lines
7.3 KiB
Go
// Copyright (c) 2013 Conformal Systems LLC.
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package btcutil
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/conformal/btcwire"
|
|
)
|
|
|
|
// OutOfRangeError describes an error due to accessing an element that is out
|
|
// of range.
|
|
type OutOfRangeError string
|
|
|
|
// BlockHeightUnknown is the value returned for a block height that is unknown.
|
|
// This is typically because the block has not been inserted into the main chain
|
|
// yet.
|
|
const BlockHeightUnknown = int64(-1)
|
|
|
|
// Error satisfies the error interface and prints human-readable errors.
|
|
func (e OutOfRangeError) Error() string {
|
|
return string(e)
|
|
}
|
|
|
|
// 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.
|
|
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
|
|
blockSha *btcwire.ShaHash // Cached block hash
|
|
blockHeight int64 // Height in the main block chain
|
|
txShas []*btcwire.ShaHash // Cached transaction hashes
|
|
txShasGenerated bool // ALL transaction hashes generated
|
|
}
|
|
|
|
// MsgBlock returns the underlying btcwire.MsgBlock for the Block.
|
|
func (b *Block) MsgBlock() *btcwire.MsgBlock {
|
|
// Return the cached block.
|
|
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
|
|
}
|
|
|
|
// Encode the MsgBlock into raw block bytes.
|
|
var w bytes.Buffer
|
|
err := b.msgBlock.BtcEncode(&w, b.protocolVersion)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
rawBlock := w.Bytes()
|
|
|
|
// Cache the encoded bytes and return them.
|
|
b.rawBlock = rawBlock
|
|
return rawBlock, b.protocolVersion, nil
|
|
}
|
|
|
|
// Sha returns the block identifier hash for the Block. This is equivalent to
|
|
// calling BlockSha on the underlying btcwire.MsgBlock, however it caches the
|
|
// result so subsequent calls are more efficient.
|
|
func (b *Block) Sha() (*btcwire.ShaHash, error) {
|
|
// Return the cached block hash if it has already been generated.
|
|
if b.blockSha != nil {
|
|
return b.blockSha, nil
|
|
}
|
|
|
|
// Generate the block hash. Ignore the error since BlockSha can't
|
|
// currently fail.
|
|
sha, _ := b.msgBlock.BlockSha(b.protocolVersion)
|
|
|
|
// Cache the block hash and return it.
|
|
b.blockSha = &sha
|
|
return &sha, nil
|
|
}
|
|
|
|
// TxSha returns the hash for the requested transaction number in the Block.
|
|
// The supplied index is 0 based. That is to say, the first transaction is the
|
|
// block is txNum 0. This is equivalent to calling TxSha on the underlying
|
|
// btcwire.MsgTx, however it caches the result so subsequent calls are more
|
|
// efficient.
|
|
func (b *Block) TxSha(txNum int) (*btcwire.ShaHash, error) {
|
|
// Ensure the requested transaction is in range.
|
|
numTx := b.msgBlock.Header.TxnCount
|
|
if txNum < 0 || uint64(txNum) > numTx {
|
|
str := fmt.Sprintf("transaction index %d is out of range - max %d",
|
|
txNum, numTx-1)
|
|
return nil, OutOfRangeError(str)
|
|
}
|
|
|
|
// Generate slice to hold all of the transaction hashes if needed.
|
|
if len(b.txShas) == 0 {
|
|
b.txShas = make([]*btcwire.ShaHash, numTx)
|
|
}
|
|
|
|
// Return the cached hash if it has already been generated.
|
|
if b.txShas[txNum] != nil {
|
|
return b.txShas[txNum], nil
|
|
}
|
|
|
|
// Generate the hash for the transaction. Ignore the error since TxSha
|
|
// can't currently fail.
|
|
sha, _ := b.msgBlock.Transactions[txNum].TxSha(b.protocolVersion)
|
|
|
|
// Cache the transaction hash and return it.
|
|
b.txShas[txNum] = &sha
|
|
return &sha, nil
|
|
}
|
|
|
|
// TxShas returns a slice of hashes for all transactions in the Block. This is
|
|
// equivalent to calling TxSha on each underlying btcwire.MsgTx, however it
|
|
// caches the result so subsequent calls are more efficient.
|
|
func (b *Block) TxShas() ([]*btcwire.ShaHash, error) {
|
|
// Return cached hashes if they have ALL already been generated. This
|
|
// flag is necessary because the transaction hashes are lazily generated
|
|
// in a sparse fashion.
|
|
if b.txShasGenerated {
|
|
return b.txShas, nil
|
|
}
|
|
|
|
// Generate slice to hold all of the transaction hashes if needed.
|
|
if len(b.txShas) == 0 {
|
|
b.txShas = make([]*btcwire.ShaHash, b.msgBlock.Header.TxnCount)
|
|
}
|
|
|
|
// Generate and cache the transaction hashes for all that haven't already
|
|
// been done.
|
|
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)
|
|
b.txShas[i] = &sha
|
|
}
|
|
}
|
|
|
|
b.txShasGenerated = true
|
|
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() (txlocD []btcwire.TxLoc, err error) {
|
|
rawMsg, pver, err := b.Bytes()
|
|
if err != nil {
|
|
return
|
|
}
|
|
rbuf := bytes.NewBuffer(rawMsg)
|
|
|
|
var mblock btcwire.MsgBlock
|
|
txloc, err := mblock.BtcDecodeTxLoc(rbuf, pver)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return txloc, err
|
|
}
|
|
|
|
// Height returns the saved height of the block in the blockchain. 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.
|
|
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 {
|
|
return &Block{
|
|
msgBlock: msgBlock,
|
|
protocolVersion: pver,
|
|
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.
|
|
var msgBlock btcwire.MsgBlock
|
|
br := bytes.NewBuffer(rawBlock)
|
|
err := msgBlock.BtcDecode(br, pver)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b := Block{
|
|
msgBlock: &msgBlock,
|
|
rawBlock: rawBlock,
|
|
protocolVersion: pver,
|
|
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 {
|
|
return &Block{
|
|
msgBlock: msgBlock,
|
|
rawBlock: rawBlock,
|
|
protocolVersion: pver,
|
|
blockHeight: BlockHeightUnknown,
|
|
}
|
|
}
|