block.go raw

   1  package block
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"github.com/p9c/p9/pkg/util"
   7  	"io"
   8  	
   9  	"github.com/p9c/p9/pkg/chainhash"
  10  	"github.com/p9c/p9/pkg/wire"
  11  )
  12  
  13  // OutOfRangeError describes an error due to accessing an element that is out of range.
  14  type OutOfRangeError string
  15  
  16  // BlockHeightUnknown is the value returned for a block height that is unknown. This is typically because the block has
  17  // not been inserted into the main chain yet.
  18  const BlockHeightUnknown = int32(-1)
  19  
  20  // Error satisfies the error interface and prints human-readable errors.
  21  func (e OutOfRangeError) Error() string {
  22  	return string(e)
  23  }
  24  
  25  // SetBlockBytes sets the internal serialized block byte buffer to the passed buffer. It is used to inject errors and is
  26  // only available to the test package.
  27  func (b *Block) SetBlockBytes(buf []byte) {
  28  	b.serializedBlock = buf
  29  }
  30  
  31  // Block defines a bitcoin block that provides easier and more efficient manipulation of raw blocks. It also memorizes
  32  // hashes for the block and its transactions on their first access so subsequent accesses don't have to repeat the
  33  // relatively expensive hashing operations.
  34  type Block struct {
  35  	msgBlock                 *wire.Block     // Underlying WireBlock
  36  	serializedBlock          []byte          // Serialized bytes for the block
  37  	serializedBlockNoWitness []byte          // Serialized bytes for block w/o witness data
  38  	blockHash                *chainhash.Hash // Cached block hash
  39  	blockHeight              int32           // Height in the main block chain
  40  	transactions             []*util.Tx      // Transactions
  41  	txnsGenerated            bool            // ALL wrapped transactions generated
  42  }
  43  
  44  // WireBlock returns the underlying wire.Block for the Block.
  45  func (b *Block) WireBlock() *wire.Block {
  46  	// Return the cached block.
  47  	return b.msgBlock
  48  }
  49  
  50  // Bytes returns the serialized bytes for the Block.
  51  // This is equivalent to calling Serialize on the underlying wire.Block,
  52  // however it caches the result so subsequent calls are more efficient.
  53  func (b *Block) Bytes() ([]byte, error) {
  54  	// Return the cached serialized bytes if it has already been generated.
  55  	if len(b.serializedBlock) != 0 {
  56  		return b.serializedBlock, nil
  57  	}
  58  	// Serialize the Block.
  59  	w := bytes.NewBuffer(make([]byte, 0, b.msgBlock.SerializeSize()))
  60  	e := b.msgBlock.Serialize(w)
  61  	if e != nil {
  62  		return nil, e
  63  	}
  64  	serializedBlock := w.Bytes()
  65  	// Cache the serialized bytes and return them.
  66  	b.serializedBlock = serializedBlock
  67  	return serializedBlock, nil
  68  }
  69  
  70  // BytesNoWitness returns the serialized bytes for the block with transactions
  71  // encoded without any witness data.
  72  func (b *Block) BytesNoWitness() ([]byte, error) {
  73  	// Return the cached serialized bytes if it has already been generated.
  74  	if len(b.serializedBlockNoWitness) != 0 {
  75  		return b.serializedBlockNoWitness, nil
  76  	}
  77  	// Serialize the Block.
  78  	var w bytes.Buffer
  79  	e := b.msgBlock.SerializeNoWitness(&w)
  80  	if e != nil {
  81  		return nil, e
  82  	}
  83  	serializedBlock := w.Bytes()
  84  	// Cache the serialized bytes and return them.
  85  	b.serializedBlockNoWitness = serializedBlock
  86  	return serializedBlock, nil
  87  }
  88  
  89  // Hash returns the block identifier hash for the Block. This is equivalent to calling BlockHash on the underlying
  90  // wire.Block, however it caches the result so subsequent calls are more efficient.
  91  func (b *Block) Hash() *chainhash.Hash {
  92  	// Return the cached block hash if it has already been generated.
  93  	if b.blockHash != nil {
  94  		return b.blockHash
  95  	}
  96  	// Cache the block hash and return it.
  97  	hash := b.msgBlock.BlockHash()
  98  	b.blockHash = &hash
  99  	return &hash
 100  }
 101  
 102  // Tx returns a wrapped transaction (util.Tx) for the transaction at the specified index in the Block. The supplied
 103  // index is 0 based. That is to say, the first transaction in the block is txNum 0. This is nearly equivalent to
 104  // accessing the raw transaction (wire.MsgTx) from the underlying wire.Block, however the wrapped transaction has
 105  // some helpful properties such as caching the hash so subsequent calls are more efficient.
 106  func (b *Block) Tx(txNum int) (*util.Tx, error) {
 107  	// Ensure the requested transaction is in range.
 108  	numTx := uint64(len(b.msgBlock.Transactions))
 109  	if txNum < 0 || uint64(txNum) > numTx {
 110  		str := fmt.Sprintf(
 111  			"transaction index %d is out of range - max %d",
 112  			txNum, numTx-1,
 113  		)
 114  		return nil, OutOfRangeError(str)
 115  	}
 116  	// Generate slice to hold all of the wrapped transactions if needed.
 117  	if len(b.transactions) == 0 {
 118  		b.transactions = make([]*util.Tx, numTx)
 119  	}
 120  	// Return the wrapped transaction if it has already been generated.
 121  	if b.transactions[txNum] != nil {
 122  		return b.transactions[txNum], nil
 123  	}
 124  	// Generate and cache the wrapped transaction and return it.
 125  	newTx := util.NewTx(b.msgBlock.Transactions[txNum])
 126  	newTx.SetIndex(txNum)
 127  	b.transactions[txNum] = newTx
 128  	return newTx, nil
 129  }
 130  
 131  // Transactions returns a slice of wrapped transactions (util.Tx) for all transactions in the Block. This is nearly
 132  // equivalent to accessing the raw transactions (wire.MsgTx) in the underlying wire.Block, however it instead
 133  // provides easy access to wrapped versions (util.Tx) of them.
 134  func (b *Block) Transactions() []*util.Tx {
 135  	// Return transactions if they have ALL already been generated. This flag is necessary because the wrapped
 136  	// transactions are lazily generated in a sparse fashion.
 137  	if b.txnsGenerated {
 138  		return b.transactions
 139  	}
 140  	// Generate slice to hold all of the wrapped transactions if needed.
 141  	if len(b.transactions) == 0 {
 142  		b.transactions = make([]*util.Tx, len(b.msgBlock.Transactions))
 143  	}
 144  	// Generate and cache the wrapped transactions for all that haven't already been done.
 145  	for i, tx := range b.transactions {
 146  		if tx == nil {
 147  			newTx := util.NewTx(b.msgBlock.Transactions[i])
 148  			newTx.SetIndex(i)
 149  			b.transactions[i] = newTx
 150  		}
 151  	}
 152  	b.txnsGenerated = true
 153  	return b.transactions
 154  }
 155  
 156  // TxHash returns the hash for the requested transaction number in the Block. The supplied index is 0 based. That is to
 157  // say, the first transaction in the block is txNum 0. This is equivalent to calling TxHash on the underlying
 158  // wire.MsgTx, however it caches the result so subsequent calls are more efficient.
 159  func (b *Block) TxHash(txNum int) (*chainhash.Hash, error) {
 160  	// Attempt to get a wrapped transaction for the specified index. It will be created lazily if needed or simply
 161  	// return the cached version if it has already been generated.
 162  	tx, e := b.Tx(txNum)
 163  	if e != nil {
 164  		return nil, e
 165  	}
 166  	// Defer to the wrapped transaction which will return the cached hash if it has already been generated.
 167  	return tx.Hash(), nil
 168  }
 169  
 170  // TxLoc returns the offsets and lengths of each transaction in a raw block. It is used to allow fast indexing into
 171  // transactions within the raw byte stream.
 172  func (b *Block) TxLoc() ([]wire.TxLoc, error) {
 173  	rawMsg, e := b.Bytes()
 174  	if e != nil {
 175  		return nil, e
 176  	}
 177  	rbuf := bytes.NewBuffer(rawMsg)
 178  	var mblock wire.Block
 179  	txLocs, e := mblock.DeserializeTxLoc(rbuf)
 180  	if e != nil {
 181  		return nil, e
 182  	}
 183  	return txLocs, e
 184  }
 185  
 186  // Height returns the saved height of the block in the block chain. This value will be BlockHeightUnknown if it hasn't
 187  // already explicitly been set.
 188  func (b *Block) Height() int32 {
 189  	return b.blockHeight
 190  }
 191  
 192  // SetHeight sets the height of the block in the block chain.
 193  func (b *Block) SetHeight(height int32) {
 194  	b.blockHeight = height
 195  }
 196  
 197  // NewBlock returns a new instance of a bitcoin block given an underlying wire.Block.  See Block.
 198  func NewBlock(msgBlock *wire.Block) *Block {
 199  	return &Block{
 200  		msgBlock:    msgBlock,
 201  		blockHeight: BlockHeightUnknown,
 202  	}
 203  }
 204  
 205  // NewFromBytes returns a new instance of a bitcoin block given the serialized bytes.  See Block.
 206  func NewFromBytes(serializedBlock []byte) (*Block, error) {
 207  	br := bytes.NewReader(serializedBlock)
 208  	b, e := NewFromReader(br)
 209  	if e != nil {
 210  		return nil, e
 211  	}
 212  	b.serializedBlock = serializedBlock
 213  	return b, nil
 214  }
 215  
 216  // NewFromReader returns a new instance of a bitcoin block given a Reader to deserialize the block.  See Block.
 217  func NewFromReader(r io.Reader) (*Block, error) {
 218  	// Deserialize the bytes into a Block.
 219  	var msgBlock wire.Block
 220  	e := msgBlock.Deserialize(r)
 221  	if e != nil {
 222  		return nil, e
 223  	}
 224  	b := Block{
 225  		msgBlock:    &msgBlock,
 226  		blockHeight: BlockHeightUnknown,
 227  	}
 228  	return &b, nil
 229  }
 230  
 231  // NewFromBlockAndBytes returns a new instance of a bitcoin block given an underlying wire.Block and the
 232  // serialized bytes for it. See Block.
 233  func NewFromBlockAndBytes(msgBlock *wire.Block, serializedBlock []byte) *Block {
 234  	return &Block{
 235  		msgBlock:        msgBlock,
 236  		serializedBlock: serializedBlock,
 237  		blockHeight:     BlockHeightUnknown,
 238  	}
 239  }
 240