1 // Package indexers implements optional block chain indexes.
2 package index
3 4 import (
5 "encoding/binary"
6 "errors"
7 "github.com/p9c/p9/pkg/block"
8 9 "github.com/p9c/p9/pkg/blockchain"
10 "github.com/p9c/p9/pkg/database"
11 )
12 13 var (
14 // byteOrder is the preferred byte order used for serializing numeric fields for storage in the database.
15 byteOrder = binary.LittleEndian
16 // errInterruptRequested indicates that an operation was cancelled due to a user-requested interrupt.
17 errInterruptRequested = errors.New("interrupt requested")
18 )
19 20 // NeedsInputser provides a generic interface for an indexer to specify the it requires the ability to look up inputs
21 // for a transaction.
22 type NeedsInputser interface {
23 NeedsInputs() bool
24 }
25 26 // Indexer provides a generic interface for an indexer that is managed by an index manager such as the Manager type
27 // provided by this package.
28 type Indexer interface {
29 // Key returns the key of the index as a byte slice.
30 Key() []byte
31 // Name returns the human-readable name of the index.
32 Name() string
33 // Create is invoked when the indexer manager determines the index needs to be created for the first time.
34 Create(dbTx database.Tx) error
35 // Init is invoked when the index manager is first initializing the index. This differs from the Create method in
36 // that it is called on every load, including the case the index was just created.
37 Init() error
38 // ConnectBlock is invoked when a new block has been connected to the main chain. The set of output spent within a
39 // block is also passed in so indexers can access the pevious output scripts input spent if required.
40 ConnectBlock(database.Tx, *block.Block, []blockchain.SpentTxOut) error
41 // DisconnectBlock is invoked when a block has been disconnected from the main chain. The set of outputs scripts
42 // that were spent within this block is also returned so indexers can clean up the prior index state for this block
43 DisconnectBlock(database.Tx, *block.Block, []blockchain.SpentTxOut) error
44 }
45 46 // AssertError identifies an error that indicates an internal code consistency issue and should be treated as a critical
47 // and unrecoverable error.
48 type AssertError string
49 50 // DBError returns the assertion error as a huma-readable string and satisfies the error interface.
51 func (e AssertError) Error() string {
52 return "assertion failed: " + string(e)
53 }
54 55 // errDeserialize signifies that a problem was encountered when deserializing data.
56 type errDeserialize string
57 58 // DBError implements the error interface.
59 func (e errDeserialize) Error() string {
60 return string(e)
61 }
62 63 // isDeserializeErr returns whether or not the passed error is an errDeserialize error.
64 func isDeserializeErr(e error) bool {
65 _, ok := e.(errDeserialize)
66 return ok
67 }
68 69 // internalBucket is an abstraction over a database bucket. It is used to make the code easier to test since it allows
70 // mock objects in the tests to only implement these functions instead of everything a database.Bucket supports.
71 type internalBucket interface {
72 Get(key []byte) []byte
73 Put(key []byte, value []byte) error
74 Delete(key []byte) error
75 }
76 77 // interruptRequested returns true when the provided channel has been closed. This simplifies early shutdown slightly
78 // since the caller can just use an if statement instead of a select.
79 func interruptRequested(interrupted <-chan struct{}) bool {
80 select {
81 case <-interrupted:
82 return true
83 default:
84 }
85 return false
86 }
87