github.com/Cleverse/go-ethereum@v0.0.0-20220927095127-45113064e7f2/arbitrum/recordingdb.go (about) 1 package arbitrum 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 9 "github.com/ethereum/go-ethereum/common" 10 "github.com/ethereum/go-ethereum/consensus" 11 "github.com/ethereum/go-ethereum/core" 12 "github.com/ethereum/go-ethereum/core/rawdb" 13 "github.com/ethereum/go-ethereum/core/state" 14 "github.com/ethereum/go-ethereum/core/types" 15 "github.com/ethereum/go-ethereum/crypto" 16 "github.com/ethereum/go-ethereum/ethdb" 17 "github.com/ethereum/go-ethereum/log" 18 "github.com/ethereum/go-ethereum/rlp" 19 "github.com/ethereum/go-ethereum/trie" 20 ) 21 22 type RecordingKV struct { 23 inner *trie.Database 24 readDbEntries map[common.Hash][]byte 25 enableBypass bool 26 } 27 28 func NewRecordingKV(inner *trie.Database) *RecordingKV { 29 return &RecordingKV{inner, make(map[common.Hash][]byte), false} 30 } 31 32 func (db *RecordingKV) Has(key []byte) (bool, error) { 33 return false, errors.New("recording KV doesn't support Has") 34 } 35 36 func (db *RecordingKV) Get(key []byte) ([]byte, error) { 37 var hash common.Hash 38 var res []byte 39 var err error 40 if len(key) == 32 { 41 copy(hash[:], key) 42 res, err = db.inner.Node(hash) 43 } else if len(key) == len(rawdb.CodePrefix)+32 && bytes.HasPrefix(key, rawdb.CodePrefix) { 44 // Retrieving code 45 copy(hash[:], key[len(rawdb.CodePrefix):]) 46 res, err = db.inner.DiskDB().Get(key) 47 } else { 48 err = fmt.Errorf("recording KV attempted to access non-hash key %v", hex.EncodeToString(key)) 49 } 50 if err != nil { 51 return nil, err 52 } 53 if db.enableBypass { 54 return res, nil 55 } 56 if crypto.Keccak256Hash(res) != hash { 57 return nil, fmt.Errorf("recording KV attempted to access non-hash key %v", hash) 58 } 59 db.readDbEntries[hash] = res 60 return res, nil 61 } 62 63 func (db *RecordingKV) Put(key []byte, value []byte) error { 64 return errors.New("recording KV doesn't support Put") 65 } 66 67 func (db *RecordingKV) Delete(key []byte) error { 68 return errors.New("recording KV doesn't support Delete") 69 } 70 71 func (db *RecordingKV) NewBatch() ethdb.Batch { 72 if db.enableBypass { 73 return db.inner.DiskDB().NewBatch() 74 } 75 log.Error("recording KV: attempted to create batch when bypass not enabled") 76 return nil 77 } 78 79 func (db *RecordingKV) NewBatchWithSize(size int) ethdb.Batch { 80 if db.enableBypass { 81 return db.inner.DiskDB().NewBatchWithSize(size) 82 } 83 log.Error("recording KV: attempted to create batch when bypass not enabled") 84 return nil 85 } 86 87 func (db *RecordingKV) NewIterator(prefix []byte, start []byte) ethdb.Iterator { 88 if db.enableBypass { 89 return db.inner.DiskDB().NewIterator(prefix, start) 90 } 91 log.Error("recording KV: attempted to create iterator when bypass not enabled") 92 return nil 93 } 94 95 func (db *RecordingKV) NewSnapshot() (ethdb.Snapshot, error) { 96 // This is fine as RecordingKV doesn't support mutation 97 return db, nil 98 } 99 100 func (db *RecordingKV) Stat(property string) (string, error) { 101 return "", errors.New("recording KV doesn't support Stat") 102 } 103 104 func (db *RecordingKV) Compact(start []byte, limit []byte) error { 105 return nil 106 } 107 108 func (db *RecordingKV) Close() error { 109 return nil 110 } 111 112 func (db *RecordingKV) Release() { 113 return 114 } 115 116 func (db *RecordingKV) GetRecordedEntries() map[common.Hash][]byte { 117 return db.readDbEntries 118 } 119 func (db *RecordingKV) EnableBypass() { 120 db.enableBypass = true 121 } 122 123 type RecordingChainContext struct { 124 bc core.ChainContext 125 minBlockNumberAccessed uint64 126 initialBlockNumber uint64 127 } 128 129 func NewRecordingChainContext(inner core.ChainContext, blocknumber uint64) *RecordingChainContext { 130 return &RecordingChainContext{ 131 bc: inner, 132 minBlockNumberAccessed: blocknumber, 133 initialBlockNumber: blocknumber, 134 } 135 } 136 137 func (r *RecordingChainContext) Engine() consensus.Engine { 138 return r.bc.Engine() 139 } 140 141 func (r *RecordingChainContext) GetHeader(hash common.Hash, num uint64) *types.Header { 142 if num < r.minBlockNumberAccessed { 143 r.minBlockNumberAccessed = num 144 } 145 return r.bc.GetHeader(hash, num) 146 } 147 148 func (r *RecordingChainContext) GetMinBlockNumberAccessed() uint64 { 149 return r.minBlockNumberAccessed 150 } 151 152 func PrepareRecording(blockchain *core.BlockChain, lastBlockHeader *types.Header) (*state.StateDB, core.ChainContext, *RecordingKV, error) { 153 rawTrie := blockchain.StateCache().TrieDB() 154 recordingKeyValue := NewRecordingKV(rawTrie) 155 recordingStateDatabase := state.NewDatabase(rawdb.NewDatabase(recordingKeyValue)) 156 var prevRoot common.Hash 157 if lastBlockHeader != nil { 158 prevRoot = lastBlockHeader.Root 159 } 160 recordingStateDb, err := state.New(prevRoot, recordingStateDatabase, nil) 161 if err != nil { 162 return nil, nil, nil, fmt.Errorf("failed to create recordingStateDb: %w", err) 163 } 164 var recordingChainContext *RecordingChainContext 165 if lastBlockHeader != nil { 166 if !lastBlockHeader.Number.IsUint64() { 167 return nil, nil, nil, errors.New("block number not uint64") 168 } 169 recordingChainContext = NewRecordingChainContext(blockchain, lastBlockHeader.Number.Uint64()) 170 } 171 return recordingStateDb, recordingChainContext, recordingKeyValue, nil 172 } 173 174 func PreimagesFromRecording(chainContextIf core.ChainContext, recordingDb *RecordingKV) (map[common.Hash][]byte, error) { 175 entries := recordingDb.GetRecordedEntries() 176 recordingChainContext, ok := chainContextIf.(*RecordingChainContext) 177 if (recordingChainContext == nil) || (!ok) { 178 return nil, errors.New("recordingChainContext invalid") 179 } 180 blockchain, ok := recordingChainContext.bc.(*core.BlockChain) 181 if (blockchain == nil) || (!ok) { 182 return nil, errors.New("blockchain invalid") 183 } 184 for i := recordingChainContext.GetMinBlockNumberAccessed(); i <= recordingChainContext.initialBlockNumber; i++ { 185 header := blockchain.GetHeaderByNumber(i) 186 hash := header.Hash() 187 bytes, err := rlp.EncodeToBytes(header) 188 if err != nil { 189 panic(fmt.Sprintf("Error RLP encoding header: %v\n", err)) 190 } 191 entries[hash] = bytes 192 } 193 return entries, nil 194 }