diff --git a/core/blockchain.go b/core/blockchain.go index f458da8257..2e88e2208f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1484,10 +1484,14 @@ func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types // writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead. // This function expects the chain mutex to be held. -func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { - if err := bc.writeBlockWithState(block, receipts, state); err != nil { +func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, st *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { + if err := bc.writeBlockWithState(block, receipts, st); err != nil { return NonStatTy, err } + + st.Witness.Block = block + state.DumpWitnessToFile(st.Witness) + currentBlock := bc.CurrentBlock() reorg, err := bc.forker.ReorgNeeded(currentBlock, block.Header()) if err != nil { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8857f7be0c..435accbe25 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,6 +17,7 @@ package core import ( + "crypto/sha256" "errors" "fmt" "math/big" @@ -4717,7 +4718,29 @@ func TestEIP3651(t *testing.T) { } } -func TestSingleWitness(t *testing.T) { +func hashAccount(addr common.Address) (output common.Hash) { + hasher := sha256.New() + hasher.Write(addr[:]) + res := hasher.Sum(nil) + copy(output[:], res[:]) + return output +} + +/* +func debugPrintTrie(db ethdb.Database, account common.Address) { + var bc BlockChain + latestBlock := bc.GetBlockByNumber(1) + storageRoot := bc.StateAt(latestBlock.Root()).GetStorageRoot(account) + + tr, err := trie.New(trie.StorageTrieID(latestBlock.Root(), hashAccount(account), storageRoot), s.Database()) + if err != nil { + panic(err) + } + trIterator +} +*/ + +func TestWitnessStorageClear(t *testing.T) { var ( engine = ethash.NewFaker() @@ -4728,15 +4751,14 @@ func TestSingleWitness(t *testing.T) { bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA ) - // Populate two slots + // Populate one slots aaStorage[common.HexToHash("01")] = common.HexToHash("01") + aaStorage[common.HexToHash("02")] = common.HexToHash("01") code := []byte{ - byte(vm.PUSH1), 0x1, // - byte(vm.NUMBER), // value = number + 1 - byte(vm.ADD), // - byte(vm.PUSH1), 0x3, // location - byte(vm.SSTORE), // Set slot[3] = number + 1 + byte(vm.PUSH1), 0x0, // value + byte(vm.PUSH1), 0x2, // key + byte(vm.SSTORE), } gspec := &Genesis{ Config: params.TestChainConfig, @@ -4746,6 +4768,7 @@ func TestSingleWitness(t *testing.T) { bb: { Code: code, Balance: big.NewInt(1), + Storage: aaStorage, }, }, } @@ -4760,9 +4783,10 @@ func TestSingleWitness(t *testing.T) { }) // Import the canonical chain cache := DefaultCacheConfigWithScheme(rawdb.PathScheme) - cache.SnapshotLimit = 0 // disable snapshot - cache.SnapshotLimit = 500 // enable snapshot - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), cache, gspec, nil, engine, vm.Config{ + cache.SnapshotLimit = 0 // disable snapshot + //cache.SnapshotLimit = 500 // enable snapshot + db := rawdb.NewMemoryDatabase() + chain, err := NewBlockChain(db, cache, gspec, nil, engine, vm.Config{ Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { diff --git a/core/state/state_witness.go b/core/state/state_witness.go index 0a3676d973..f5bd3eb88d 100644 --- a/core/state/state_witness.go +++ b/core/state/state_witness.go @@ -1,36 +1,90 @@ package state import ( + "bytes" "fmt" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "io" + "os" ) -type witness struct { +type Witness struct { + Block *types.Block + Root common.Hash + Lists map[common.Hash]map[string][]byte +} + +type EncodeWitness struct { + block types.Block root common.Hash - lists []map[string][]byte owners []common.Hash + paths [][]string + nodes [][][]byte } -func newWitness(originalRoot common.Hash) *witness { - return &witness{root: originalRoot} -} +func (w *Witness) EncodeRLP() []byte { + var e EncodeWitness + for owner, nodeMap := range w.Lists { + e.owners = append(e.owners, owner) + var paths []string + var nodes [][]byte -func (w *witness) addAccessList(owner common.Hash, list map[string][]byte) { - //fmt.Printf("Adding owner %x len %d\n", owner, len(list)) - if len(list) > 0 { - w.lists = append(w.lists, list) - w.owners = append(w.owners, owner) - } -} - -func (w *witness) Dump() { - fmt.Printf("[witness] Root %x\n", w.root) - for i, list := range w.lists { - owner := w.owners[i] - fmt.Printf("[witness] Owner %#x, %d entries: \n", owner, len(list)) - for path, v := range list { - fmt.Printf("[witness] - '%#x': %#x\n", path, v) + for path, node := range nodeMap { + paths = append(paths, path) + nodes = append(nodes, node) } + e.paths = append(e.paths, paths) + e.nodes = append(e.nodes, nodes) + } + res := new(bytes.Buffer) + if err := e.encode(res); err != nil { + panic(err) + } + return res.Bytes() +} + +// generated by rlpgen +func (obj *EncodeWitness) encode(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + w.ListEnd(_tmp0) + return w.Flush() +} + +func newWitness(originalRoot common.Hash) *Witness { + return &Witness{Root: originalRoot} +} + +func (w *Witness) addAccessList(owner common.Hash, list map[string][]byte) { + if len(list) > 0 { + w.Lists[owner] = list + } +} + +func (w *Witness) Dump() { + /* + fmt.Printf("[witness] Root %x\n", w.Root) + for i, list := range w.Lists { + owner := w.Owners[i] + fmt.Printf("[witness] Owner %#x, %d entries: \n", owner, len(list)) + for path, v := range list { + fmt.Printf("[witness] - '%#x': %#x\n", path, v) + } + } + */ +} + +func DumpWitnessToFile(w *Witness) { + enc := w.EncodeRLP() + path, err := os.Getwd() + if err != nil { + panic("shite") + } + outputFName := fmt.Sprintf("%d-%x.rlp\n", w.Block.NumberU64(), w.Block.Hash()) + err = os.WriteFile(path+"/"+outputFName, enc, 0644) + if err != nil { + panic("shite 2") } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 12075d3e9d..c3db1091c0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -84,6 +84,9 @@ type StateDB struct { stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution stateObjectsDestruct map[common.Address]*types.StateAccount // State objects destructed in the block along with its previous value + usedBlockHashes map[common.Hash]struct{} + codes map[common.Hash]Code + // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs @@ -138,6 +141,8 @@ type StateDB struct { // Testing hooks onCommit func(states *triestate.Set) // Hook invoked when commit is performed + + Witness *Witness } // New creates a new state from a given trie. @@ -172,6 +177,14 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) return sdb, nil } +func (s *StateDB) MarkUsedBlockHash(hash common.Hash) { + s.usedBlockHashes[hash] = struct{}{} +} + +func (s *StateDB) MarkWitnessCode(hash common.Hash, code Code) { + s.codes[hash] = code +} + // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. @@ -316,8 +329,15 @@ func (s *StateDB) TxIndex() int { func (s *StateDB) GetCode(addr common.Address) []byte { stateObject := s.getStateObject(addr) if stateObject != nil { - return stateObject.Code() + code := stateObject.Code() + if code != nil { + var codeHash common.Hash + copy(codeHash[:], stateObject.CodeHash()) + s.MarkWitnessCode(codeHash, stateObject.Code()) + } + return code } + return nil } @@ -1156,6 +1176,19 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.A return incomplete, nil } +type BlockProof struct { + blockHashes []common.Hash + codeHashes []common.Hash + codes []Code + witness Witness +} + +/* +func (s *StateDB) GetBlockProof() { + +} +*/ + // Commit writes the state to the underlying in-memory trie database. // Once the state is committed, tries cached in stateDB (including account // trie, storage tries) will no longer be functional. A new state instance @@ -1306,7 +1339,8 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er s.stateObjectsDirty = make(map[common.Address]struct{}) s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount) - w.Dump() + //w.Dump() + s.Witness = w return root, nil } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 56ff350201..39ee73e276 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -447,7 +447,11 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( lower = upper - 256 } if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) + res := interpreter.evm.Context.GetHash(num64).Bytes() + var bh common.Hash + copy(bh[:], res[:]) + interpreter.evm.StateDB.MarkUsedBlockHash(bh) + num.SetBytes(res[:]) } else { num.Clear() } diff --git a/core/vm/interface.go b/core/vm/interface.go index 26814d3d2f..fee005e608 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -78,6 +78,8 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) + + MarkUsedBlockHash(common.Hash) } // CallContext provides a basic interface for the EVM calling conventions. The EVM