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 5package btcutil 6 7import ( 8 "bytes" 9 "fmt" 10 "io" 11 12 "github.com/btcsuite/btcd/chaincfg/chainhash" 13 "github.com/btcsuite/btcd/wire" 14) 15 16// OutOfRangeError describes an error due to accessing an element that is out 17// of range. 18type 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. 23const BlockHeightUnknown = int32(-1) 24 25// Error satisfies the error interface and prints human-readable errors. 26func (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. 34type 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. 45func (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. 53func (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 w := bytes.NewBuffer(make([]byte, 0, b.msgBlock.SerializeSize())) 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. 74func (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. 96func (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. 114func (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. 144func (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. 176func (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. 193func (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. 210func (b *Block) Height() int32 { 211 return b.blockHeight 212} 213 214// SetHeight sets the height of the block in the block chain. 215func (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. 221func 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. 230func 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. 242func 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. 259func NewBlockFromBlockAndBytes(msgBlock *wire.MsgBlock, serializedBlock []byte) *Block { 260 return &Block{ 261 msgBlock: msgBlock, 262 serializedBlock: serializedBlock, 263 blockHeight: BlockHeightUnknown, 264 } 265} 266