1 package blockchain
2 3 import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7 "github.com/p9c/p9/pkg/block"
8 "math/big"
9 "sync"
10 "time"
11 12 "github.com/p9c/p9/pkg/chainhash"
13 "github.com/p9c/p9/pkg/database"
14 "github.com/p9c/p9/pkg/wire"
15 )
16 17 const (
18 // blockHdrSize is the size of a block header. This is simply the constant from wire and is only provided here for
19 // convenience since wire.MaxBlockHeaderPayload is quite long.
20 blockHdrSize = wire.MaxBlockHeaderPayload
21 // latestUtxoSetBucketVersion is the current version of the utxo set bucket that is used to track all unspent
22 // outputs.
23 latestUtxoSetBucketVersion = 2
24 // latestSpendJournalBucketVersion is the current version of the spend journal bucket that is used to track all
25 // spent transactions for use in reorgs.
26 latestSpendJournalBucketVersion = 1
27 )
28 29 var (
30 // blockIndexBucketName is the name of the db bucket used to house to the block headers and contextual information.
31 blockIndexBucketName = []byte("blockheaderidx")
32 // hashIndexBucketName is the name of the db bucket used to house to the block hash -> block height index.
33 hashIndexBucketName = []byte("hashidx")
34 // heightIndexBucketName is the name of the db bucket used to house to the block height -> block hash index.
35 heightIndexBucketName = []byte("heightidx")
36 // chainStateKeyName is the name of the db key used to store the best chain state.
37 chainStateKeyName = []byte("chainstate")
38 // spendJournalVersionKeyName is the name of the db key used to store the version of the spend journal currently in
39 // the database.
40 spendJournalVersionKeyName = []byte("spendjournalversion")
41 // spendJournalBucketName is the name of the db bucket used to house transactions outputs that are spent in each
42 // block.
43 spendJournalBucketName = []byte("spendjournal")
44 // utxoSetVersionKeyName is the name of the db key used to store the version of the utxo set currently in the
45 // database.
46 utxoSetVersionKeyName = []byte("utxosetversion")
47 // utxoSetBucketName is the name of the db bucket used to house the unspent transaction output set.
48 utxoSetBucketName = []byte("utxosetv2")
49 // byteOrder is the preferred byte order used for serializing numeric fields for storage in the database.
50 byteOrder = binary.LittleEndian
51 )
52 53 // errNotInMainChain signifies that a block hash or height that is not in the main chain was requested.
54 type errNotInMainChain string
55 56 // DBError implements the error interface.
57 func (e errNotInMainChain) Error() string {
58 return string(e)
59 }
60 61 // isNotInMainChainErr returns whether or not the passed error is an errNotInMainChain error.
62 func isNotInMainChainErr(e error) bool {
63 _, ok := e.(errNotInMainChain)
64 return ok
65 }
66 67 // errDeserialize signifies that a problem was encountered when
68 // deserializing data.
69 type errDeserialize string
70 71 // DBError is an implementation of the errors.* interface
72 func (e errDeserialize) Error() string {
73 return string(e)
74 }
75 76 // isDeserializeErr returns whether or not the passed error is an errDeserialize error.
77 func isDeserializeErr(e error) bool {
78 _, ok := e.(errDeserialize)
79 return ok
80 }
81 82 // // isDbBucketNotFoundErr returns whether or not the passed error is a
83 // database.DBError with an error code of database.ErrBucketNotFound.
84 // func isDbBucketNotFoundErr(// e error) bool {
85 // dbErr, ok := err.(database.DBError)
86 // return ok && dbErr.ErrorCode == database.ErrBucketNotFound
87 // }
88 89 // dbFetchVersion fetches an individual version with the given key from the metadata bucket. It is primarily used to
90 // track versions on entities such as buckets. It returns zero if the provided key does not exist.
91 func dbFetchVersion(dbTx database.Tx, key []byte) uint32 {
92 serialized := dbTx.Metadata().Get(key)
93 if serialized == nil {
94 return 0
95 }
96 return byteOrder.Uint32(serialized[:])
97 }
98 99 // dbPutVersion uses an existing database transaction to update the provided key in the metadata bucket to the given
100 // version. It is primarily used to track versions on entities such as buckets.
101 func dbPutVersion(dbTx database.Tx, key []byte, version uint32) (e error) {
102 var serialized [4]byte
103 byteOrder.PutUint32(serialized[:], version)
104 return dbTx.Metadata().Put(key, serialized[:])
105 }
106 107 // dbFetchOrCreateVersion uses an existing database transaction to attempt to fetch the provided key from the metadata
108 // bucket as a version and in the case it doesn't exist it adds the entry with the provided default version and returns
109 // that.
110 //
111 // This is useful during upgrades to automatically handle loading and adding version keys as necessary.
112 func dbFetchOrCreateVersion(dbTx database.Tx, key []byte, defaultVersion uint32) (uint32, error) {
113 version := dbFetchVersion(dbTx, key)
114 if version == 0 {
115 version = defaultVersion
116 e := dbPutVersion(dbTx, key, version)
117 if e != nil {
118 return 0, e
119 }
120 }
121 return version, nil
122 }
123 124 // The transaction spend journal consists of an entry for each block
125 // connected to the main chain which contains the transaction outputs the block spends serialized such that the order is the reverse of the order they were spent. This is required because reorganizing the chain necessarily entails disconnecting blocks to get back to the point of the fork which implies unspending all of the transaction outputs that each block previously spent.
126 // Since the utxo set, by definition,
127 // only contains unspent transaction outputs, the spent transaction outputs must be resurrected from somewhere. There is more than one way this could be done, however this is the most straight forward method that does not require having a transaction index and unpruned
128 // blockchain.
129 // NOTE: This format is NOT self describing.
130 // The additional details such as the number of entries (transaction inputs) are expected to come from the block itself and the utxo set (for legacy entries). The rationale in doing this is to save space. This is also the reason the spent outputs are serialized in the reverse order they are spent because later transactions are allowed to spend outputs from earlier ones in the same block.
131 // The reserved field below used to keep track of the version of the
132 // containing transaction when the height in the header code was non-zero, however the height is always non-zero now, but keeping the extra reserved field allows backwards compatibility.
133 // The serialized format is:
134 // [<header code><reserved><compressed txout>],...
135 // Field Type Size
136 // header code VLQ variable
137 // reserved byte 1
138 // compressed txout
139 // compressed amount VLQ variable
140 // compressed script []byte variable
141 // The serialized header code format is:
142 // bit 0 - containing transaction is a coinbase
143 // bits 1-x - height of the block that contains the spent txout
144 // Example 1:
145 // From block 170 in main blockchain.
146 // 1300320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c
147 // <><><------------------------------------------------------------------>
148 // | | |
149 // | reserved compressed txout
150 // header code
151 // - header code: 0x13 (coinbase, height 9)
152 // - reserved: 0x00
153 // - compressed txout 0:
154 // - 0x32: VLQ-encoded compressed amount for 5000000000 (50 DUO)
155 // - 0x05: special script type pay-to-pubkey
156 // - 0x11...5c: x-coordinate of the pubkey
157 // Example 2:
158 // Adapted from block 100025 in main blockchain.
159 // 8b99700091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e868b99700086c64700b2fb57eadf61e106a100a7445a8c3f67898841ec
160 // <----><><----------------------------------------------><----><><---------------------------------------------->
161 // | | | | | |
162 // | reserved compressed txout | reserved
163 // compressed txout
164 // header code header code
165 // - Last spent output:
166 // - header code: 0x8b9970 (not coinbase, height 100024)
167 // - reserved: 0x00
168 // - compressed txout:
169 // - 0x91f20f: VLQ-encoded compressed amount for 34405000000 (344.05 DUO)
170 // - 0x00: special script type pay-to-pubkey-hash
171 // - 0x6e...86: pubkey hash
172 // - Second to last spent output:
173 // - header code: 0x8b9970 (not coinbase, height 100024)
174 // - reserved: 0x00
175 // - compressed txout:
176 // - 0x86c647: VLQ-encoded compressed amount for 13761000000 (137.61 DUO)
177 // - 0x00: special script type pay-to-pubkey-hash
178 // - 0xb2...ec: pubkey hash
179 // -----------------------------------------------------------------------------
180 181 // SpentTxOut contains a spent transaction output and potentially additional contextual information such as whether or
182 // not it was contained
183 //
184 // in a coinbase transaction, the version of the transaction it was contained in, and which block height the containing
185 // transaction was included in.
186 //
187 // As described in the comments above, the additional contextual information will only be valid when this spent txout is
188 // spending the last unspent output of the containing transaction.
189 type SpentTxOut struct {
190 // Amount is the amount of the output.
191 Amount int64
192 // PkScipt is the the public key script for the output.
193 PkScript []byte
194 // Height is the height of the the block containing the creating tx.
195 Height int32
196 // Denotes if the creating tx is a coinbase.
197 IsCoinBase bool
198 }
199 200 // FetchSpendJournal attempts to retrieve the spend journal, or the set of outputs spent for the target block.
201 //
202 // This provides a view of all the outputs that will be consumed once the target block is connected to the end of the
203 // main chain.
204 //
205 // This function is safe for concurrent access.
206 func (b *BlockChain) FetchSpendJournal(targetBlock *block.Block) ([]SpentTxOut, error) {
207 b.ChainLock.RLock()
208 defer b.ChainLock.RUnlock()
209 var spendEntries []SpentTxOut
210 e := b.db.View(
211 func(dbTx database.Tx) (e error) {
212 spendEntries, e = dbFetchSpendJournalEntry(dbTx, targetBlock)
213 return e
214 },
215 )
216 if e != nil {
217 return nil, e
218 }
219 return spendEntries, nil
220 }
221 222 // spentTxOutHeaderCode returns the calculated header code to be used when serializing the provided stxo entry.
223 func spentTxOutHeaderCode(stxo *SpentTxOut) uint64 {
224 // As described in the serialization format comments, the header code encodes the height shifted over one bit and
225 // the coinbase flag in the lowest bit.
226 headerCode := uint64(stxo.Height) << 1
227 if stxo.IsCoinBase {
228 headerCode |= 0x01
229 }
230 return headerCode
231 }
232 233 // spentTxOutSerializeSize returns the number of bytes it would take to serialize the passed stxo according to the
234 // format described above.
235 func spentTxOutSerializeSize(stxo *SpentTxOut) int {
236 size := serializeSizeVLQ(spentTxOutHeaderCode(stxo))
237 if stxo.Height > 0 {
238 // The legacy v1 spend journal format conditionally tracked the
239 // containing transaction version when the height was non-zero,
240 // so this is required for backwards compat.
241 size += serializeSizeVLQ(0)
242 }
243 return size + compressedTxOutSize(uint64(stxo.Amount), stxo.PkScript)
244 }
245 246 // putSpentTxOut serializes the passed stxo according to the format described above directly into the passed target byte
247 // slice.
248 //
249 // The target byte slice must be at least large enough to handle the number of bytes returned by the
250 // SpentTxOutSerializeSize function or it will panic.
251 func putSpentTxOut(target []byte, stxo *SpentTxOut) int {
252 headerCode := spentTxOutHeaderCode(stxo)
253 offset := putVLQ(target, headerCode)
254 if stxo.Height > 0 {
255 // The legacy v1 spend journal format conditionally tracked the containing transaction version when the height
256 // was non-zero, so this is required for backwards compat.
257 offset += putVLQ(target[offset:], 0)
258 }
259 return offset + putCompressedTxOut(
260 target[offset:], uint64(stxo.Amount),
261 stxo.PkScript,
262 )
263 }
264 265 // decodeSpentTxOut decodes the passed serialized stxo entry, possibly followed by other data, into the passed stxo
266 // struct. It returns the number of bytes read.
267 func decodeSpentTxOut(serialized []byte, stxo *SpentTxOut) (int, error) {
268 // Ensure there are bytes to decode.
269 if len(serialized) == 0 {
270 return 0, errDeserialize("no serialized bytes")
271 }
272 // Deserialize the header code.
273 code, offset := deserializeVLQ(serialized)
274 if offset >= len(serialized) {
275 return offset, errDeserialize(
276 "unexpected end of data after header code",
277 )
278 }
279 // Decode the header code. Bit 0 indicates containing transaction is a coinbase. Bits 1-x encode height of
280 // containing transaction.
281 stxo.IsCoinBase = code&0x01 != 0
282 stxo.Height = int32(code >> 1)
283 if stxo.Height > 0 {
284 // The legacy v1 spend journal format conditionally tracked the containing transaction version when the height
285 // was non-zero, so this is required for backwards compat.
286 _, bytesRead := deserializeVLQ(serialized[offset:])
287 offset += bytesRead
288 if offset >= len(serialized) {
289 return offset, errDeserialize(
290 "unexpected end of data after reserved",
291 )
292 }
293 }
294 // Decode the compressed txout.
295 amount, pkScript, bytesRead, e := decodeCompressedTxOut(
296 serialized[offset:],
297 )
298 offset += bytesRead
299 if e != nil {
300 return offset, errDeserialize(
301 fmt.Sprint(
302 "unable to decode txout: ", e,
303 ),
304 )
305 }
306 stxo.Amount = int64(amount)
307 stxo.PkScript = pkScript
308 return offset, nil
309 }
310 311 // deserializeSpendJournalEntry decodes the passed serialized byte slice into a slice of spent txouts according to the
312 // format described in detail above.
313 //
314 // Since the serialization format is not self describing as noted in the format comments this function also requires the
315 // transactions that spend the txouts.
316 func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx) ([]SpentTxOut, error) {
317 // Calculate the total number of stxos.
318 var numStxos int
319 for _, tx := range txns {
320 numStxos += len(tx.TxIn)
321 }
322 // When a block has no spent txouts there is nothing to serialize.
323 if len(serialized) == 0 {
324 // Ensure the block actually has no stxos. This should never happen unless there is database corruption or an
325 // empty entry erroneously made its way into the database.
326 if numStxos != 0 {
327 return nil, AssertError(
328 fmt.Sprintf(
329 "mismatched spend journal serialization - no serialization for expected %d stxos",
330 numStxos,
331 ),
332 )
333 }
334 return nil, nil
335 }
336 // Loop backwards through all transactions so everything is read in reverse order to match the serialization order.
337 stxoIdx := numStxos - 1
338 offset := 0
339 stxos := make([]SpentTxOut, numStxos)
340 for txIdx := len(txns) - 1; txIdx > -1; txIdx-- {
341 tx := txns[txIdx]
342 // Loop backwards through all of the transaction inputs and read the associated stxo.
343 for txInIdx := len(tx.TxIn) - 1; txInIdx > -1; txInIdx-- {
344 txIn := tx.TxIn[txInIdx]
345 stxo := &stxos[stxoIdx]
346 stxoIdx--
347 n, e := decodeSpentTxOut(serialized[offset:], stxo)
348 offset += n
349 if e != nil {
350 return nil, errDeserialize(
351 fmt.Sprintf(
352 "unable to decode stxo for %v: %v",
353 txIn.PreviousOutPoint, e,
354 ),
355 )
356 }
357 }
358 }
359 return stxos, nil
360 }
361 362 // serializeSpendJournalEntry serializes all of the passed spent txouts into a single byte slice according to the format
363 // described in detail above.
364 func serializeSpendJournalEntry(stxos []SpentTxOut) []byte {
365 if len(stxos) == 0 {
366 return nil
367 }
368 // Calculate the size needed to serialize the entire journal entry.
369 var size int
370 for i := range stxos {
371 size += spentTxOutSerializeSize(&stxos[i])
372 }
373 serialized := make([]byte, size)
374 // Serialize each individual stxo directly into the slice in reverse order one after the other.
375 var offset int
376 for i := len(stxos) - 1; i > -1; i-- {
377 offset += putSpentTxOut(serialized[offset:], &stxos[i])
378 }
379 return serialized
380 }
381 382 // dbFetchSpendJournalEntry fetches the spend journal entry for the passed block and deserializes it into a slice of
383 // spent txout entries.
384 //
385 // NOTE: Legacy entries will not have the coinbase flag or height set unless it was the final output spend in the
386 // containing transaction.
387 //
388 // It is up to the caller to handle this properly by looking the information up in the utxo set.
389 func dbFetchSpendJournalEntry(dbTx database.Tx, block *block.Block) ([]SpentTxOut, error) {
390 // Exclude the coinbase transaction since it can't spend anything.
391 spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
392 serialized := spendBucket.Get(block.Hash()[:])
393 blockTxns := block.WireBlock().Transactions[1:]
394 stxos, e := deserializeSpendJournalEntry(serialized, blockTxns)
395 if e != nil {
396 // Ensure any deserialization errors are returned as database corruption errors.
397 if isDeserializeErr(e) {
398 return nil, database.DBError{
399 ErrorCode: database.ErrCorruption,
400 Description: fmt.Sprintf(
401 "corrupt spend information for %v: %v",
402 block.Hash(), e,
403 ),
404 }
405 }
406 return nil, e
407 }
408 return stxos, nil
409 }
410 411 // dbPutSpendJournalEntry uses an existing database transaction to update the spend journal entry for the given block
412 // hash using the provided slice of spent txouts. The spent txouts slice must contain an entry for every txout the
413 // transactions in the block spend in the order they are spent.
414 func dbPutSpendJournalEntry(dbTx database.Tx, blockHash *chainhash.Hash, stxos []SpentTxOut) (e error) {
415 spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
416 serialized := serializeSpendJournalEntry(stxos)
417 return spendBucket.Put(blockHash[:], serialized)
418 }
419 420 // dbRemoveSpendJournalEntry uses an existing database transaction to remove the spend journal entry for the passed
421 // block hash.
422 func dbRemoveSpendJournalEntry(dbTx database.Tx, blockHash *chainhash.Hash) (e error) {
423 spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
424 return spendBucket.Delete(blockHash[:])
425 }
426 427 // The unspent transaction output (
428 // utxo) set consists of an entry for each unspent output using a format that is optimized to reduce space using domain specific compression algorithms. This format is a slightly modified version of the format used in Bitcoin Core.
429 // Each entry is keyed by an outpoint as specified below.
430 // It is important to note that the key encoding uses a VLQ, which employs an MSB encoding so iteration of utxos when doing byte-wise comparisons will produce them in order.
431 // The serialized key format is:
432 // <hash><output index>
433 // Field Type Size
434 // hash chainhash.Hash chainhash.HashSize
435 // output index VLQ variable
436 // The serialized value format is:
437 // <header code><compressed txout>
438 // Field Type Size
439 // header code VLQ variable
440 // compressed txout
441 // compressed amount VLQ variable
442 // compressed script []byte variable
443 // The serialized header code format is:
444 // bit 0 - containing transaction is a coinbase
445 // bits 1-x - height of the block that contains the unspent txout
446 // Example 1:
447 // From tx in main blockchain:
448 // Blk 1, 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098:0
449 // 03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52
450 // <><------------------------------------------------------------------>
451 // | |
452 // header code compressed txout
453 // - header code: 0x03 (coinbase, height 1)
454 // - compressed txout:
455 // - 0x32: VLQ-encoded compressed amount for 5000000000 (50 DUO)
456 // - 0x04: special script type pay-to-pubkey
457 // - 0x96...52: x-coordinate of the pubkey
458 // Example 2:
459 // From tx in main blockchain:
460 // Blk 113931,
461 // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f:2
462 // 8cf316800900b8025be1b3efc63b0ad48e7f9f10e87544528d58
463 // <----><------------------------------------------>
464 // | |
465 // header code compressed txout
466 // - header code: 0x8cf316 (not coinbase, height 113931)
467 // - compressed txout:
468 // - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 DUO)
469 // - 0x00: special script type pay-to-pubkey-hash
470 // - 0xb8...58: pubkey hash
471 // Example 3:
472 // From tx in main blockchain:
473 // Blk 338156,
474 // 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620:22
475 // a8a2588ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6
476 // <----><-------------------------------------------------->
477 // | |
478 // header code compressed txout
479 // - header code: 0xa8a258 (not coinbase, height 338156)
480 // - compressed txout:
481 // - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.
482 // 66875659 DUO)
483 // - 0x01: special script type pay-to-script-hash
484 // - 0x1d...e6: script hash
485 // -----------------------------------------------------------------------------
486 487 // maxUint32VLQSerializeSize is the maximum number of bytes a max uint32 takes to serialize as a VLQ.
488 var maxUint32VLQSerializeSize = serializeSizeVLQ(1<<32 - 1)
489 490 // outpointKeyPool defines a concurrent safe free list of byte slices used to provide temporary buffers for outpoint
491 // database keys.
492 var outpointKeyPool = sync.Pool{
493 New: func() interface{} {
494 b := make([]byte, chainhash.HashSize+maxUint32VLQSerializeSize)
495 return &b // Pointer to slice to avoid boxing alloc.
496 },
497 }
498 499 // outpointKey returns a key suitable for use as a database key in the utxo set while making use of a free list.
500 //
501 // A new buffer is allocated if there are not already any available on the free list. The returned byte slice should be
502 // returned to the free list by using the recycleOutpointKey function when the caller is done with it _unless_ the slice
503 // will need to live for longer than the caller can calculate such as when used to write to the database.
504 func outpointKey(outpoint wire.OutPoint) *[]byte {
505 // A VLQ employs an MSB encoding, so they are useful not only to reduce the amount of storage space, but also so
506 // iteration of utxos when doing byte-wise comparisons will produce them in order.
507 key := outpointKeyPool.Get().(*[]byte)
508 idx := uint64(outpoint.Index)
509 *key = (*key)[:chainhash.HashSize+serializeSizeVLQ(idx)]
510 copy(*key, outpoint.Hash[:])
511 putVLQ((*key)[chainhash.HashSize:], idx)
512 return key
513 }
514 515 // recycleOutpointKey puts the provided byte slice, which should have been obtained via the outpointKey function, back
516 // on the free list.
517 func recycleOutpointKey(key *[]byte) {
518 outpointKeyPool.Put(key)
519 }
520 521 // utxoEntryHeaderCode returns the calculated header code to be used when serializing the provided utxo entry.
522 func utxoEntryHeaderCode(entry *UtxoEntry) (rv uint64, e error) {
523 if entry.IsSpent() {
524 return 0, AssertError("attempt to serialize spent UXTO header")
525 }
526 // As described in the serialization format comments, the header code encodes the height shifted over one bit and
527 // the coinbase flag in the lowest bit.
528 headerCode := uint64(entry.BlockHeight()) << 1
529 if entry.IsCoinBase() {
530 headerCode |= 0x01
531 }
532 return headerCode, nil
533 }
534 535 // serializeUtxoEntry returns the entry serialized to a format that is suitable for long-term storage. The format is
536 // described in detail above.
537 func serializeUtxoEntry(entry *UtxoEntry) ([]byte, error) {
538 // Spent outputs have no serialization.
539 if entry.IsSpent() {
540 return nil, nil
541 }
542 // Encode the header code.
543 headerCode, e := utxoEntryHeaderCode(entry)
544 if e != nil {
545 return nil, e
546 }
547 // Calculate the size needed to serialize the entry.
548 size := serializeSizeVLQ(headerCode) +
549 compressedTxOutSize(uint64(entry.Amount()), entry.PkScript())
550 // Serialize the header code followed by the compressed unspent transaction output.
551 serialized := make([]byte, size)
552 offset := putVLQ(serialized, headerCode)
553 _ = putCompressedTxOut(
554 serialized[offset:], uint64(entry.Amount()),
555 entry.PkScript(),
556 )
557 return serialized, nil
558 }
559 560 // deserializeUtxoEntry decodes a utxo entry from the passed serialized byte slice into a new UtxoEntry using a format
561 // that is suitable for long -term storage. The format is described in detail above.
562 func deserializeUtxoEntry(serialized []byte) (entry *UtxoEntry, e error) {
563 // Deserialize the header code.
564 code, offset := deserializeVLQ(serialized)
565 if offset >= len(serialized) {
566 return nil, errDeserialize("unexpected end of data after header")
567 }
568 // Decode the header code. Bit 0 indicates whether the containing transaction is a coinbase. Bits 1-x encode height
569 // of containing transaction.
570 isCoinBase := code&0x01 != 0
571 blockHeight := int32(code >> 1)
572 // Decode the compressed unspent transaction output.
573 var amount uint64
574 var pkScript []byte
575 amount, pkScript, _, e = decodeCompressedTxOut(serialized[offset:])
576 if e != nil {
577 return nil, errDeserialize(
578 fmt.Sprint(
579 "unable to decode utxo:", e,
580 ),
581 )
582 }
583 entry = &UtxoEntry{
584 amount: int64(amount),
585 pkScript: pkScript,
586 blockHeight: blockHeight,
587 packedFlags: 0,
588 }
589 if isCoinBase {
590 entry.packedFlags |= tfCoinBase
591 }
592 return entry, nil
593 }
594 595 // dbFetchUtxoEntryByHash attempts to find and fetch a utxo for the given hash. It uses a cursor and seek to try and do
596 // this as efficiently as possible. When there are no entries for the provided hash, nil will be returned for the both
597 // the entry and the error.
598 func dbFetchUtxoEntryByHash(dbTx database.Tx, hash *chainhash.Hash) (*UtxoEntry, error) {
599 // Attempt to find an entry by seeking for the hash along with a zero index. Due to the fact the keys are serialized
600 // as <hash><index>, where the index uses an MSB encoding, if there are any entries for the hash at all, one will be
601 // found.
602 cursor := dbTx.Metadata().Bucket(utxoSetBucketName).Cursor()
603 key := outpointKey(wire.OutPoint{Hash: *hash, Index: 0})
604 ok := cursor.Seek(*key)
605 recycleOutpointKey(key)
606 if !ok {
607 return nil, nil
608 }
609 // An entry was found, but it could just be an entry with the next highest hash after the requested one, so make
610 // sure the hashes actually match.
611 cursorKey := cursor.Key()
612 if len(cursorKey) < chainhash.HashSize {
613 return nil, nil
614 }
615 if !bytes.Equal(hash[:], cursorKey[:chainhash.HashSize]) {
616 return nil, nil
617 }
618 return deserializeUtxoEntry(cursor.Value())
619 }
620 621 // dbFetchUtxoEntry uses an existing database transaction to fetch the specified transaction output from the utxo set.
622 // When there is no entry for the provided output, nil will be returned for both the entry and the error.
623 func dbFetchUtxoEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error) {
624 // Fetch the unspent transaction output information for the passed transaction output. Return now when there is no
625 // entry.
626 key := outpointKey(outpoint)
627 utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
628 serializedUtxo := utxoBucket.Get(*key)
629 recycleOutpointKey(key)
630 if serializedUtxo == nil {
631 return nil, nil
632 }
633 // A non-nil zero-length entry means there is an entry in the database for a spent transaction output which should
634 // never be the case.
635 if len(serializedUtxo) == 0 {
636 return nil, AssertError(
637 fmt.Sprint(
638 "database contains entry for spent tx output ",
639 outpoint,
640 ),
641 )
642 }
643 // Deserialize the utxo entry and return it.
644 entry, e := deserializeUtxoEntry(serializedUtxo)
645 if e != nil {
646 // Ensure any deserialization errors are returned as database corruption errors.
647 if isDeserializeErr(e) {
648 return nil, database.DBError{
649 ErrorCode: database.ErrCorruption,
650 Description: fmt.Sprintf(
651 "corrupt utxo entry for %v: %v",
652 outpoint, e,
653 ),
654 }
655 }
656 return nil, e
657 }
658 return entry, nil
659 }
660 661 // dbPutUtxoView uses an existing database transaction to update the utxo set in the database based on the provided utxo
662 // view contents and state.
663 //
664 // In particular, only the entries that have been marked as modified are written to the database.
665 func dbPutUtxoView(dbTx database.Tx, view *UtxoViewpoint) (e error) {
666 utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
667 for outpoint, entry := range view.entries {
668 // No need to update the database if the entry was not modified.
669 if entry == nil || !entry.isModified() {
670 continue
671 }
672 // Remove the utxo entry if it is spent.
673 if entry.IsSpent() {
674 key := outpointKey(outpoint)
675 e := utxoBucket.Delete(*key)
676 recycleOutpointKey(key)
677 if e != nil {
678 return e
679 }
680 continue
681 }
682 // Serialize and store the utxo entry.
683 serialized, e := serializeUtxoEntry(entry)
684 if e != nil {
685 return e
686 }
687 key := outpointKey(outpoint)
688 e = utxoBucket.Put(*key, serialized)
689 // NOTE: The key is intentionally not recycled here since the database interface contract prohibits
690 // modifications. It will be garbage collected normally when the database is done with it.
691 if e != nil {
692 return e
693 }
694 }
695 return nil
696 }
697 698 // The block index consists of two buckets with an entry for every block in
699 // the main chain. One bucket is for the hash to height mapping and the other is for the height to hash mapping.
700 // The serialized format for values in the hash to height bucket is:
701 // <height>
702 // Field Type Size
703 // height uint32 4 bytes
704 // The serialized format for values in the height to hash bucket is:
705 // <hash>
706 // Field Type Size
707 // hash chainhash.Hash chainhash.HashSize
708 // -----------------------------------------------------------------------------
709 710 // dbPutBlockIndex uses an existing database transaction to update or add the block index entries for the hash to height
711 // and height to hash mappings for the provided values.
712 func dbPutBlockIndex(dbTx database.Tx, hash *chainhash.Hash, height int32) (e error) {
713 // Serialize the height for use in the index entries.
714 var serializedHeight [4]byte
715 byteOrder.PutUint32(serializedHeight[:], uint32(height))
716 // Add the block hash to height mapping to the index.
717 meta := dbTx.Metadata()
718 hashIndex := meta.Bucket(hashIndexBucketName)
719 if e := hashIndex.Put(hash[:], serializedHeight[:]); E.Chk(e) {
720 return e
721 }
722 // Add the block height to hash mapping to the index.
723 heightIndex := meta.Bucket(heightIndexBucketName)
724 return heightIndex.Put(serializedHeight[:], hash[:])
725 }
726 727 // dbRemoveBlockIndex uses an existing database transaction remove block index entries from the hash to height and
728 // height to hash mappings for the provided values.
729 func dbRemoveBlockIndex(dbTx database.Tx, hash *chainhash.Hash, height int32) (e error) {
730 // Remove the block hash to height mapping.
731 meta := dbTx.Metadata()
732 hashIndex := meta.Bucket(hashIndexBucketName)
733 if e := hashIndex.Delete(hash[:]); E.Chk(e) {
734 return e
735 }
736 // Remove the block height to hash mapping.
737 var serializedHeight [4]byte
738 byteOrder.PutUint32(serializedHeight[:], uint32(height))
739 heightIndex := meta.Bucket(heightIndexBucketName)
740 return heightIndex.Delete(serializedHeight[:])
741 }
742 743 // dbFetchHeightByHash uses an existing database transaction to retrieve the height for the provided hash from the
744 // index.
745 func dbFetchHeightByHash(dbTx database.Tx, hash *chainhash.Hash) (int32, error) {
746 meta := dbTx.Metadata()
747 hashIndex := meta.Bucket(hashIndexBucketName)
748 serializedHeight := hashIndex.Get(hash[:])
749 if serializedHeight == nil {
750 str := fmt.Sprintf(
751 "dbFetchHeightByHash: block %s is not in the main chain", hash,
752 )
753 return 0, errNotInMainChain(str)
754 }
755 return int32(byteOrder.Uint32(serializedHeight)), nil
756 }
757 758 // dbFetchHashByHeight uses an existing database transaction to retrieve the hash for the provided height from the
759 // index.
760 func dbFetchHashByHeight(dbTx database.Tx, height int32) (*chainhash.Hash, error) {
761 var serializedHeight [4]byte
762 byteOrder.PutUint32(serializedHeight[:], uint32(height))
763 meta := dbTx.Metadata()
764 heightIndex := meta.Bucket(heightIndexBucketName)
765 hashBytes := heightIndex.Get(serializedHeight[:])
766 if hashBytes == nil {
767 str := fmt.Sprintf(
768 "no block at height %d exists", height,
769 )
770 return nil, errNotInMainChain(str)
771 }
772 var hash chainhash.Hash
773 copy(hash[:], hashBytes)
774 return &hash, nil
775 }
776 777 // The best chain state consists of the best block hash and height, the total number of transactions up to and including
778 // those in the best block, and the accumulated work sum up to and including the best block.
779 //
780 // The serialized format is:
781 //
782 // <block hash><block height><total txns><work sum length><work sum>
783 // Field Type Size
784 // block hash chainhash.Hash chainhash.HashSize
785 // block height uint32 4 bytes
786 // total txns uint64 8 bytes
787 // work sum length uint32 4 bytes
788 // work sum big.Int work sum length
789 // -----------------------------------------------------------------------------
790 791 // bestChainState represents the data to be stored the database for the current best chain state.
792 type bestChainState struct {
793 hash chainhash.Hash
794 height uint32
795 totalTxns uint64
796 workSum *big.Int
797 }
798 799 // serializeBestChainState returns the serialization of the passed block best chain state. This is data to be stored in
800 // the chain state bucket.
801 func serializeBestChainState(state bestChainState) []byte {
802 // Calculate the full size needed to serialize the chain state.
803 workSumBytes := state.workSum.Bytes()
804 workSumBytesLen := uint32(len(workSumBytes))
805 serializedLen := chainhash.HashSize + 4 + 8 + 4 + workSumBytesLen
806 // Serialize the chain state.
807 serializedData := make([]byte, serializedLen)
808 copy(serializedData[0:chainhash.HashSize], state.hash[:])
809 offset := uint32(chainhash.HashSize)
810 byteOrder.PutUint32(serializedData[offset:], state.height)
811 offset += 4
812 byteOrder.PutUint64(serializedData[offset:], state.totalTxns)
813 offset += 8
814 byteOrder.PutUint32(serializedData[offset:], workSumBytesLen)
815 offset += 4
816 copy(serializedData[offset:], workSumBytes)
817 return serializedData[:]
818 }
819 820 // deserializeBestChainState deserializes the passed serialized best chain state. This is data stored in the chain state
821 // bucket and is updated after every block is connected or disconnected form the main chain. block.
822 func deserializeBestChainState(serializedData []byte) (bestChainState, error) {
823 // Ensure the serialized data has enough bytes to properly deserialize the hash, height, total transactions, and
824 // work sum length.
825 if len(serializedData) < chainhash.HashSize+16 {
826 return bestChainState{}, database.DBError{
827 ErrorCode: database.ErrCorruption,
828 Description: "corrupt best chain state",
829 }
830 }
831 state := bestChainState{}
832 copy(state.hash[:], serializedData[0:chainhash.HashSize])
833 offset := uint32(chainhash.HashSize)
834 state.height = byteOrder.Uint32(serializedData[offset : offset+4])
835 offset += 4
836 state.totalTxns = byteOrder.Uint64(serializedData[offset : offset+8])
837 offset += 8
838 workSumBytesLen := byteOrder.Uint32(serializedData[offset : offset+4])
839 offset += 4
840 // Ensure the serialized data has enough bytes to deserialize the work sum.
841 if uint32(len(serializedData[offset:])) < workSumBytesLen {
842 return bestChainState{}, database.DBError{
843 ErrorCode: database.ErrCorruption,
844 Description: "corrupt best chain state",
845 }
846 }
847 workSumBytes := serializedData[offset : offset+workSumBytesLen]
848 state.workSum = new(big.Int).SetBytes(workSumBytes)
849 return state, nil
850 }
851 852 // dbPutBestState uses an existing database transaction to update the best chain state with the given parameters.
853 func dbPutBestState(dbTx database.Tx, snapshot *BestState, workSum *big.Int) (e error) {
854 // Serialize the current best chain state.
855 serializedData := serializeBestChainState(
856 bestChainState{
857 hash: snapshot.Hash,
858 height: uint32(snapshot.Height),
859 totalTxns: snapshot.TotalTxns,
860 workSum: workSum,
861 },
862 )
863 // Store the current best chain state into the database.
864 return dbTx.Metadata().Put(chainStateKeyName, serializedData)
865 }
866 867 // createChainState initializes both the database and the chain state to the genesis block. This includes creating the
868 // necessary buckets and inserting the genesis block so it must only be called on an uninitialized database.
869 func (b *BlockChain) createChainState() (e error) {
870 // Create a new node from the genesis block and set it as the best node.
871 genesisBlock := block.NewBlock(b.params.GenesisBlock)
872 // Tracec(func() string {
873 // xx, _ := genesisBlock.Bytes()
874 // return hex.EncodeToString(xx)
875 // })
876 genesisBlock.SetHeight(0)
877 header := &genesisBlock.WireBlock().Header
878 node := NewBlockNode(header, nil)
879 node.status = statusDataStored | statusValid
880 var df Diffs
881 df, e = b.CalcNextRequiredDifficultyPlan9Controller(node)
882 node.Diffs.Store(df)
883 if e != nil {
884 }
885 b.BestChain.SetTip(node)
886 // Add the new node to the index which is used for faster lookups.
887 b.Index.addNode(node)
888 // Initialize the state related to the best block. Since it is the genesis block, use its timestamp for the median
889 // time.
890 numTxns := uint64(len(genesisBlock.WireBlock().Transactions))
891 blockSize := uint64(genesisBlock.WireBlock().SerializeSize())
892 blockWeight := uint64(GetBlockWeight(genesisBlock))
893 b.stateSnapshot = newBestState(
894 node, blockSize, blockWeight, numTxns,
895 numTxns, time.Unix(node.timestamp, 0),
896 )
897 // Create the initial the database chain state including creating the necessary index buckets and inserting the
898 // genesis block.
899 e = b.db.Update(
900 func(dbTx database.Tx) (e error) {
901 meta := dbTx.Metadata()
902 // Create the bucket that houses the block index data.
903 _, e = meta.CreateBucket(blockIndexBucketName)
904 if e != nil {
905 return e
906 }
907 // Create the bucket that houses the chain block hash to height index.
908 _, e = meta.CreateBucket(hashIndexBucketName)
909 if e != nil {
910 return e
911 }
912 // Create the bucket that houses the chain block height to hash index.
913 _, e = meta.CreateBucket(heightIndexBucketName)
914 if e != nil {
915 return e
916 }
917 // Create the bucket that houses the spend journal data and store its
918 // version.
919 _, e = meta.CreateBucket(spendJournalBucketName)
920 if e != nil {
921 return e
922 }
923 e = dbPutVersion(
924 dbTx, utxoSetVersionKeyName,
925 latestUtxoSetBucketVersion,
926 )
927 if e != nil {
928 return e
929 }
930 // Create the bucket that houses the utxo set and store its version. Note that the genesis block coinbase
931 // transaction is intentionally not inserted here since it is not spendable by consensus rules.
932 _, e = meta.CreateBucket(utxoSetBucketName)
933 if e != nil {
934 return e
935 }
936 e = dbPutVersion(
937 dbTx, spendJournalVersionKeyName,
938 latestSpendJournalBucketVersion,
939 )
940 if e != nil {
941 return e
942 }
943 // Save the genesis block to the block index database.
944 e = dbStoreBlockNode(dbTx, node)
945 if e != nil {
946 return e
947 }
948 // Add the genesis block hash to height and height to hash mappings to the index.
949 e = dbPutBlockIndex(dbTx, &node.hash, node.height)
950 if e != nil {
951 return e
952 }
953 // Store the current best chain state into the database.
954 node.workSum = CalcWork(node.bits, node.height, node.version)
955 e = dbPutBestState(dbTx, b.stateSnapshot, node.workSum)
956 if e != nil {
957 return e
958 }
959 // Store the genesis block into the database.
960 return dbStoreBlock(dbTx, genesisBlock)
961 },
962 )
963 return e
964 }
965 966 // initChainState attempts to load and initialize the chain state from the database. When the db does not yet contain
967 // any chain state, both it and the chain state are initialized to the genesis block.
968 func (b *BlockChain) initChainState() (e error) {
969 // Determine the state of the chain database. We may need to initialize everything from scratch or upgrade certain
970 // buckets.
971 var initialized, hasBlockIndex bool
972 e = b.db.View(
973 func(dbTx database.Tx) (e error) {
974 initialized = dbTx.Metadata().Get(chainStateKeyName) != nil
975 hasBlockIndex = dbTx.Metadata().Bucket(blockIndexBucketName) != nil
976 return nil
977 },
978 )
979 if e != nil {
980 return e
981 }
982 if !initialized {
983 // At this point the database has not already been initialized, so initialize both it and the chain state to the
984 // genesis block.
985 return b.createChainState()
986 }
987 if !hasBlockIndex {
988 e = migrateBlockIndex(b.db)
989 if e != nil {
990 return nil
991 }
992 }
993 // Attempt to load the chain state from the database.
994 e = b.db.View(
995 func(dbTx database.Tx) (e error) {
996 // Fetch the stored chain state from the database metadata. When it doesn't exist, it means the database hasn't
997 // been initialized for use with chain yet, so break out now to allow that to happen under a writable database
998 // transaction.
999 serializedData := dbTx.Metadata().Get(chainStateKeyName)
1000 T.F("serialized chain state: %0x", serializedData)
1001 state, e := deserializeBestChainState(serializedData)
1002 if e != nil {
1003 return e
1004 }
1005 // Load all of the headers from the data for the known best chain and construct the blk index accordingly.
1006 // Since the number of nodes are already known, perform a single alloc for them versus a whole bunch of little
1007 // ones to reduce pressure on the GC.
1008 T.Ln("loading blk index...")
1009 blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
1010 // Determine how many blocks will be loaded into the index so we can allocate the right amount.
1011 var blockCount int32
1012 cursor := blockIndexBucket.Cursor()
1013 for ok := cursor.First(); ok; ok = cursor.Next() {
1014 blockCount++
1015 }
1016 blockNodes := make([]BlockNode, blockCount)
1017 var i int32
1018 var lastNode *BlockNode
1019 cursor = blockIndexBucket.Cursor()
1020 for ok := cursor.First(); ok; ok = cursor.Next() {
1021 var header *wire.BlockHeader
1022 var status blockStatus
1023 header, status, e = deserializeBlockRow(cursor.Value())
1024 if e != nil {
1025 return e
1026 }
1027 // Determine the parent blk node. Since we iterate blk headers in order of height, if the blocks are
1028 // mostly linear there is a very good chance the previous header processed is the parent.
1029 var parent *BlockNode
1030 if lastNode == nil {
1031 blockHash := header.BlockHash()
1032 if !blockHash.IsEqual(b.params.GenesisHash) {
1033 return AssertError(
1034 fmt.Sprintf(
1035 "initChainState: expected first entry in blk index"+
1036 " to be genesis blk, found %s",
1037 blockHash,
1038 ),
1039 )
1040 }
1041 } else if header.PrevBlock == lastNode.hash {
1042 // Since we iterate blk headers in order of height, if the blocks are mostly linear there is a very
1043 // good chance the previous header processed is the parent.
1044 parent = lastNode
1045 } else {
1046 parent = b.Index.LookupNode(&header.PrevBlock)
1047 if parent == nil {
1048 return AssertError(
1049 fmt.Sprint(
1050 "initChainState: Could not find parent for blk ",
1051 header.BlockHash(),
1052 ),
1053 )
1054 }
1055 }
1056 // Initialize the blk node for the blk, connect it, and add it to the blk index.
1057 node := &blockNodes[i]
1058 initBlockNode(node, header, parent)
1059 node.status = status
1060 b.Index.addNode(node)
1061 lastNode = node
1062 i++
1063 }
1064 // Set the best chain view to the stored best state.
1065 tip := b.Index.LookupNode(&state.hash)
1066 if tip == nil {
1067 return AssertError(
1068 fmt.Sprintf(
1069 "initChainState: cannot find chain tip %s in blk index",
1070 state.hash,
1071 ),
1072 )
1073 }
1074 b.BestChain.SetTip(tip)
1075 // Load the raw blk bytes for the best blk.
1076 blockBytes, e := dbTx.FetchBlock(&state.hash)
1077 if e != nil {
1078 return e
1079 }
1080 var blk wire.Block
1081 e = blk.Deserialize(bytes.NewReader(blockBytes))
1082 if e != nil {
1083 return e
1084 }
1085 // As a final consistency check, we'll run through all the nodes which are ancestors of the current chain tip,
1086 // and mark them as valid if they aren't already marked as such. This is a safe assumption as all the blk
1087 // before the current tip are valid by definition.
1088 for iterNode := tip; iterNode != nil; iterNode = iterNode.parent {
1089 // If this isn't already marked as valid in the index, then we'll mark it as valid now to ensure consistency
1090 // once we 're up and running.
1091 if !iterNode.status.KnownValid() {
1092 I.F(
1093 "Block %v (height=%v) ancestor of chain tip not"+
1094 " marked as valid, upgrading to valid for consistency",
1095 iterNode.hash, iterNode.height,
1096 )
1097 b.Index.SetStatusFlags(iterNode, statusValid)
1098 }
1099 }
1100 // Initialize the state related to the best blk.
1101 blockSize := uint64(len(blockBytes))
1102 blockWeight := uint64(GetBlockWeight(block.NewBlock(&blk)))
1103 numTxns := uint64(len(blk.Transactions))
1104 b.stateSnapshot = newBestState(
1105 tip, blockSize, blockWeight,
1106 numTxns, state.totalTxns, tip.CalcPastMedianTime(),
1107 )
1108 return nil
1109 },
1110 )
1111 if e != nil {
1112 return e
1113 }
1114 // As we might have updated the index after it was loaded, we'll attempt to flush the index to the DB. This will
1115 // only result in a write if the elements are dirty, so it'll usually be a noop.
1116 return b.Index.flushToDB()
1117 }
1118 1119 // deserializeBlockRow parses a value in the block index bucket into a block header and block status bitfield.
1120 func deserializeBlockRow(blockRow []byte) (*wire.BlockHeader, blockStatus, error) {
1121 buffer := bytes.NewReader(blockRow)
1122 var header wire.BlockHeader
1123 e := header.Deserialize(buffer)
1124 if e != nil {
1125 return nil, statusNone, e
1126 }
1127 statusByte, e := buffer.ReadByte()
1128 if e != nil {
1129 return nil, statusNone, e
1130 }
1131 return &header, blockStatus(statusByte), nil
1132 }
1133 1134 // dbFetchHeaderByHash uses an existing database transaction to retrieve the block header for the provided hash.
1135 func dbFetchHeaderByHash(dbTx database.Tx, hash *chainhash.Hash) (*wire.BlockHeader, error) {
1136 headerBytes, e := dbTx.FetchBlockHeader(hash)
1137 if e != nil {
1138 return nil, e
1139 }
1140 var header wire.BlockHeader
1141 e = header.Deserialize(bytes.NewReader(headerBytes))
1142 if e != nil {
1143 return nil, e
1144 }
1145 return &header, nil
1146 }
1147 1148 /*// dbFetchHeaderByHeight uses an existing database transaction to retrieve
1149 the block header for the provided height.
1150 func dbFetchHeaderByHeight(dbTx database.Tx,
1151 height int32) (*wire.BlockHeader, error) {
1152 hash, e := dbFetchHashByHeight(dbTx, height)
1153 if e != nil {
1154 return nil, e
1155 }
1156 return dbFetchHeaderByHash(dbTx, hash)
1157 } */
1158 1159 // dbFetchBlockByNode uses an existing database transaction to retrieve the raw block for the provided node, deserialize
1160 // it, and return a util.Block with the height set.
1161 func dbFetchBlockByNode(dbTx database.Tx, node *BlockNode) (*block.Block, error) {
1162 // Load the raw block bytes from the database.
1163 blockBytes, e := dbTx.FetchBlock(&node.hash)
1164 if e != nil {
1165 return nil, e
1166 }
1167 // Create the encapsulated block and set the height appropriately.
1168 block, e := block.NewFromBytes(blockBytes)
1169 if e != nil {
1170 return nil, e
1171 }
1172 block.SetHeight(node.height)
1173 return block, nil
1174 }
1175 1176 // dbStoreBlockNode stores the block header and validation status to the block index bucket. This overwrites the current
1177 // entry if there exists one.
1178 func dbStoreBlockNode(dbTx database.Tx, node *BlockNode) (e error) {
1179 // Serialize block data to be stored.
1180 w := bytes.NewBuffer(make([]byte, 0, blockHdrSize+1))
1181 header := node.Header()
1182 e = header.Serialize(w)
1183 if e != nil {
1184 return e
1185 }
1186 e = w.WriteByte(byte(node.status))
1187 if e != nil {
1188 return e
1189 }
1190 value := w.Bytes()
1191 // Write block header data to block index bucket.
1192 blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
1193 key := blockIndexKey(&node.hash, uint32(node.height))
1194 return blockIndexBucket.Put(key, value)
1195 }
1196 1197 // dbStoreBlock stores the provided block in the database if it is not already there. The full block data is written to
1198 // ffldb.
1199 func dbStoreBlock(dbTx database.Tx, block *block.Block) (e error) {
1200 hasBlock, e := dbTx.HasBlock(block.Hash())
1201 if e != nil {
1202 return e
1203 }
1204 if hasBlock {
1205 return nil
1206 }
1207 return dbTx.StoreBlock(block)
1208 }
1209 1210 // blockIndexKey generates the binary key for an entry in the block index bucket. The key is composed of the block
1211 // height encoded as a big-endian 32 -bit unsigned int followed by the 32 byte block hash.
1212 func blockIndexKey(blockHash *chainhash.Hash, blockHeight uint32) []byte {
1213 indexKey := make([]byte, chainhash.HashSize+4)
1214 binary.BigEndian.PutUint32(indexKey[0:4], blockHeight)
1215 copy(indexKey[4:chainhash.HashSize+4], blockHash[:])
1216 return indexKey
1217 }
1218 1219 // BlockByHeight returns the block at the given height in the main chain. This function is safe for concurrent access.
1220 func (b *BlockChain) BlockByHeight(blockHeight int32) (*block.Block, error) {
1221 // Lookup the block height in the best chain.
1222 node := b.BestChain.NodeByHeight(blockHeight)
1223 if node == nil {
1224 str := fmt.Sprintf("no block at height %d exists", blockHeight)
1225 return nil, errNotInMainChain(str)
1226 }
1227 // Load the block from the database and return it.
1228 var block *block.Block
1229 e := b.db.View(
1230 func(dbTx database.Tx) (e error) {
1231 block, e = dbFetchBlockByNode(dbTx, node)
1232 return e
1233 },
1234 )
1235 return block, e
1236 }
1237 1238 // BlockByHash returns the block from the main chain with the given hash with the appropriate chain height set.
1239 //
1240 // This function is safe for concurrent access.
1241 func (b *BlockChain) BlockByHash(hash *chainhash.Hash) (block *block.Block, e error) {
1242 // Lookup the block hash in block index and ensure it is in the best chain.
1243 node := b.Index.LookupNode(hash)
1244 if node == nil || !b.BestChain.Contains(node) {
1245 str := fmt.Sprintf("blockByHash: block %s is not in the main chain", hash)
1246 return nil, errNotInMainChain(str)
1247 }
1248 // Load the block from the database and return it.
1249 e = b.db.View(
1250 func(dbTx database.Tx) (er error) {
1251 block, e = dbFetchBlockByNode(dbTx, node)
1252 return er
1253 },
1254 )
1255 return block, e
1256 }
1257