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  }