Skip to content

Commit 9c54751

Browse files
authored
Merge pull request ethereum#26 from OffchainLabs/recordingdb
Recordingdb
2 parents 154d4ec + d0a5b77 commit 9c54751

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

arbitrum/recordingdb.go

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

0 commit comments

Comments
 (0)