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 }