github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/block.go (about)

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