diff --git a/cmd/geth/config.go b/cmd/geth/config.go index e33d57eef2..914cccb6d6 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -35,7 +35,6 @@ import ( "github.com/scroll-tech/go-ethereum/cmd/utils" "github.com/scroll-tech/go-ethereum/eth/catalyst" "github.com/scroll-tech/go-ethereum/eth/ethconfig" - "github.com/scroll-tech/go-ethereum/internal/debug" "github.com/scroll-tech/go-ethereum/internal/ethapi" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/metrics" @@ -149,7 +148,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) } - applyTraceConfig(ctx, &cfg.Eth) applyMetricConfig(ctx, &cfg) return stack, cfg @@ -213,11 +211,6 @@ func dumpConfig(ctx *cli.Context) error { return nil } -func applyTraceConfig(ctx *cli.Context, cfg *ethconfig.Config) { - subCfg := debug.ConfigTrace(ctx) - cfg.MPTWitness = subCfg.MPTWitness -} - func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) { if ctx.GlobalIsSet(utils.MetricsEnabledFlag.Name) { cfg.Metrics.Enabled = ctx.GlobalBool(utils.MetricsEnabledFlag.Name) diff --git a/core/blockchain.go b/core/blockchain.go index e523463bbd..63b244cc06 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -46,7 +46,6 @@ import ( "github.com/scroll-tech/go-ethereum/metrics" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/trie" - "github.com/scroll-tech/go-ethereum/trie/zkproof" ) var ( @@ -135,7 +134,6 @@ type CacheConfig struct { TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory Preimages bool // Whether to store preimage of trie key to the disk - MPTWitness int // How to generate witness data for mpt circuit, 0: nothing, 1: natural SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it } @@ -148,7 +146,6 @@ var defaultCacheConfig = &CacheConfig{ TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 256, SnapshotWait: true, - MPTWitness: int(zkproof.MPTWitnessNothing), } // BlockChain represents the canonical chain given a database with a genesis diff --git a/core/types/l2trace.go b/core/types/l2trace.go index f106a548f6..01eef9e187 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -19,7 +19,6 @@ type BlockTrace struct { StorageTrace *StorageTrace `json:"storageTrace"` TxStorageTraces []*StorageTrace `json:"txStorageTraces,omitempty"` ExecutionResults []*ExecutionResult `json:"executionResults"` - MPTWitness *json.RawMessage `json:"mptwitness,omitempty"` WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"` StartL1QueueIndex uint64 `json:"startL1QueueIndex"` } @@ -131,13 +130,12 @@ type ExtraData struct { } type AccountWrapper struct { - Address common.Address `json:"address"` - Nonce uint64 `json:"nonce"` - Balance *hexutil.Big `json:"balance"` - KeccakCodeHash common.Hash `json:"keccakCodeHash,omitempty"` - PoseidonCodeHash common.Hash `json:"poseidonCodeHash,omitempty"` - CodeSize uint64 `json:"codeSize"` - Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation + Address common.Address `json:"address"` + Nonce uint64 `json:"nonce"` + Balance *hexutil.Big `json:"balance"` + KeccakCodeHash common.Hash `json:"keccakCodeHash,omitempty"` + PoseidonCodeHash common.Hash `json:"poseidonCodeHash,omitempty"` + CodeSize uint64 `json:"codeSize"` } // StorageWrapper while key & value can also be retrieved from StructLogRes.Storage, diff --git a/core/vm/logger.go b/core/vm/logger.go index 3c909a13d3..0d237c021f 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -252,10 +252,6 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop if !l.cfg.DisableStorage { structLog.Storage = l.storage[contractAddress].Copy() } - - if err := traceStorage(l, scope, structLog.getOrInitExtraData()); err != nil { - log.Error("Failed to trace data", "opcode", op.String(), "err", err) - } } if l.cfg.EnableReturnData { structLog.ReturnData.Write(rData) diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index 34157fdecc..27ecf372a1 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -62,18 +62,6 @@ func traceContractCode(l *StructLogger, scope *ScopeContext, extraData *types.Ex return nil } -// traceStorage get contract's storage at storage_address -func traceStorage(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { - if scope.Stack.len() == 0 { - return nil - } - key := common.Hash(scope.Stack.peek().Bytes32()) - storage := getWrappedAccountForStorage(l, scope.Contract.Address(), key) - extraData.StateList = append(extraData.StateList, storage) - - return nil -} - // traceContractAccount gets the contract's account func traceContractAccount(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { // Get account state. @@ -113,21 +101,6 @@ func getWrappedAccountForAddr(l *StructLogger, address common.Address) *types.Ac } } -func getWrappedAccountForStorage(l *StructLogger, address common.Address, key common.Hash) *types.AccountWrapper { - return &types.AccountWrapper{ - Address: address, - Nonce: l.env.StateDB.GetNonce(address), - Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), - KeccakCodeHash: l.env.StateDB.GetKeccakCodeHash(address), - PoseidonCodeHash: l.env.StateDB.GetPoseidonCodeHash(address), - CodeSize: l.env.StateDB.GetCodeSize(address), - Storage: &types.StorageWrapper{ - Key: key.String(), - Value: l.env.StateDB.GetState(address, key).String(), - }, - } -} - func getCodeForAddr(l *StructLogger, address common.Address) []byte { return l.env.StateDB.GetCode(address) } diff --git a/eth/backend.go b/eth/backend.go index 497ec9ee2d..61d1bf85c3 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -192,7 +192,6 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client sync_service.EthCl TrieTimeLimit: config.TrieTimeout, SnapshotLimit: config.SnapshotCache, Preimages: config.Preimages, - MPTWitness: config.MPTWitness, } ) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 42ba8739d8..4205c04087 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -206,9 +206,6 @@ type Config struct { // Arrow Glacier block override (TODO: remove after the fork) OverrideArrowGlacier *big.Int `toml:",omitempty"` - // Trace option - MPTWitness int - // Check circuit capacity in block validator CheckCircuitCapacity bool diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index debe7715d4..3e1afe1182 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -60,7 +60,6 @@ func (c Config) MarshalTOML() (interface{}, error) { Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideArrowGlacier *big.Int `toml:",omitempty"` - MPTWitness int CheckCircuitCapacity bool EnableRollupVerify bool MaxBlockRange int64 @@ -108,7 +107,6 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.Checkpoint = c.Checkpoint enc.CheckpointOracle = c.CheckpointOracle enc.OverrideArrowGlacier = c.OverrideArrowGlacier - enc.MPTWitness = c.MPTWitness enc.CheckCircuitCapacity = c.CheckCircuitCapacity enc.EnableRollupVerify = c.EnableRollupVerify enc.MaxBlockRange = c.MaxBlockRange @@ -160,7 +158,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideArrowGlacier *big.Int `toml:",omitempty"` - MPTWitness *int CheckCircuitCapacity *bool EnableRollupVerify *bool MaxBlockRange *int64 @@ -295,9 +292,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideArrowGlacier != nil { c.OverrideArrowGlacier = dec.OverrideArrowGlacier } - if dec.MPTWitness != nil { - c.MPTWitness = *dec.MPTWitness - } if dec.CheckCircuitCapacity != nil { c.CheckCircuitCapacity = *dec.CheckCircuitCapacity } diff --git a/internal/debug/flags.go b/internal/debug/flags.go index dc99094712..0ab3253b8b 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -91,12 +91,6 @@ var ( Name: "trace", Usage: "Write execution trace to the given file", } - // mpt witness settings - mptWitnessFlag = cli.IntFlag{ - Name: "trace.mptwitness", - Usage: "Output witness for mpt circuit with Specified order (default = no output, 1 = by executing order", - Value: 0, - } ) // Flags holds all command-line flags required for debugging. @@ -113,7 +107,6 @@ var Flags = []cli.Flag{ blockprofilerateFlag, cpuprofileFlag, traceFlag, - mptWitnessFlag, } var glogger *log.GlogHandler @@ -127,14 +120,11 @@ func init() { // TraceConfig export options about trace type TraceConfig struct { TracePath string - // Trace option - MPTWitness int } func ConfigTrace(ctx *cli.Context) *TraceConfig { cfg := new(TraceConfig) cfg.TracePath = ctx.GlobalString(traceFlag.Name) - cfg.MPTWitness = ctx.GlobalInt(mptWitnessFlag.Name) return cfg } diff --git a/internal/utesting/zktrie_gen_witness_test.go b/internal/utesting/zktrie_gen_witness_test.go deleted file mode 100644 index 6982d7389c..0000000000 --- a/internal/utesting/zktrie_gen_witness_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package utesting - -import ( - "encoding/json" - "fmt" - "io" - "os" - "testing" - - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/trie/zkproof" -) - -func init() { - orderScheme := os.Getenv("OP_ORDER") - var orderSchemeI int - if orderScheme != "" { - if n, err := fmt.Sscanf(orderScheme, "%d", &orderSchemeI); err == nil && n == 1 { - zkproof.SetOrderScheme(zkproof.MPTWitnessType(orderSchemeI)) - } - } -} - -func loadStaff(t *testing.T, fname string) *types.BlockTrace { - f, err := os.Open(fname) - if err != nil { - t.Fatal(err) - } - defer f.Close() - bt, err := io.ReadAll(f) - if err != nil { - t.Fatal(err) - } - - out := new(types.BlockTrace) - - err = json.Unmarshal(bt, out) - if err != nil { - t.Fatal(err) - } - - return out -} - -func TestWriterCreation(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/deploy.json") - writer, err := zkproof.NewZkTrieProofWriter(trace.StorageTrace) - if err != nil { - t.Fatal(err) - } - - if len(writer.TracingAccounts()) != 3 { - t.Error("unexpected tracing account data", writer.TracingAccounts()) - } - - if v, existed := writer.TracingAccounts()[common.HexToAddress("0x08c683b684d1e24cab8ce6de5c8c628d993ac140")]; !existed || v != nil { - t.Error("wrong tracing status for uninited address", v, existed) - } - - if v, existed := writer.TracingAccounts()[common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571")]; !existed || v == nil { - t.Error("wrong tracing status for establied address", v, existed) - } - -} - -func TestGreeterTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/greeter.json") - writer, err := zkproof.NewZkTrieProofWriter(trace.StorageTrace) - if err != nil { - t.Fatal(err) - } - - od := zkproof.NewSimpleOrderer() - theTx := trace.ExecutionResults[0] - zkproof.HandleTx(od, theTx) - - t.Log(od) - - for _, op := range od.SavedOp() { - _, err = writer.HandleNewState(op) - if err != nil { - t.Fatal(err) - } - } - - traces, err := zkproof.HandleBlockTrace(trace) - t.Log("traces: ", len(traces)) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } -} - -func TestTokenTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/token.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } - -} - -func TestCallTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/call.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } - - trace = loadStaff(t, "blocktraces/mpt_witness/call_edge.json") - traces, err = zkproof.HandleBlockTrace(trace) - outObj, _ = json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } -} - -func TestCreateTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/create.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } - - trace = loadStaff(t, "blocktraces/mpt_witness/deploy.json") - traces, err = zkproof.HandleBlockTrace(trace) - outObj, _ = json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } - -} - -func TestFailedCallTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/fail_call.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } - - trace = loadStaff(t, "blocktraces/mpt_witness/fail_create.json") - traces, err = zkproof.HandleBlockTrace(trace) - outObj, _ = json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } - -} - -// notice: now only work with OP_ORDER=2 -func TestDeleteTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/delete.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } -} - -// notice: now only work with OP_ORDER=2 -func TestDestructTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/destruct.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } -} - -func TestMutipleTx(t *testing.T) { - trace := loadStaff(t, "blocktraces/mpt_witness/multi_txs.json") - traces, err := zkproof.HandleBlockTrace(trace) - outObj, _ := json.Marshal(traces) - t.Log(string(outObj)) - if err != nil { - t.Fatal(err) - } -} diff --git a/rollup/tracing/tracing.go b/rollup/tracing/tracing.go index f83e419c64..b8cf5eba93 100644 --- a/rollup/tracing/tracing.go +++ b/rollup/tracing/tracing.go @@ -25,7 +25,6 @@ import ( "github.com/scroll-tech/go-ethereum/rollup/fees" "github.com/scroll-tech/go-ethereum/rollup/rcfg" "github.com/scroll-tech/go-ethereum/rollup/withdrawtrie" - "github.com/scroll-tech/go-ethereum/trie/zkproof" ) var ( @@ -599,15 +598,6 @@ func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, erro } } - // only zktrie model has the ability to get `mptwitness`. - if env.chainConfig.Scroll.ZktrieEnabled() { - // we use MPTWitnessNothing by default and do not allow switch among MPTWitnessType atm. - // MPTWitness will be removed from traces in the future. - if err := zkproof.FillBlockTraceForMPTWitness(zkproof.MPTWitnessNothing, blockTrace); err != nil { - log.Error("fill mpt witness fail", "error", err) - } - } - blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state) return blockTrace, nil diff --git a/trie/zkproof/orderer.go b/trie/zkproof/orderer.go deleted file mode 100644 index 3aac0cbce4..0000000000 --- a/trie/zkproof/orderer.go +++ /dev/null @@ -1,349 +0,0 @@ -package zkproof - -import ( - "math/big" - "sort" - - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/common/hexutil" - "github.com/scroll-tech/go-ethereum/core/types" -) - -type opIterator interface { - next() *types.AccountWrapper -} - -type opOrderer interface { - readonly(bool) - absorb(*types.AccountWrapper) - absorbStorage(*types.AccountWrapper, *types.StorageWrapper) - end_absorb() opIterator -} - -type iterateOp []*types.AccountWrapper - -func (ops *iterateOp) next() *types.AccountWrapper { - - sl := *ops - - if len(sl) == 0 { - return nil - } - - *ops = sl[1:] - return sl[0] -} - -type simpleOrderer struct { - readOnly int - savedOp []*types.AccountWrapper -} - -func (od *simpleOrderer) SavedOp() []*types.AccountWrapper { return od.savedOp } - -func (od *simpleOrderer) readonly(mode bool) { - if mode { - od.readOnly += 1 - } else if od.readOnly == 0 { - panic("unexpected readonly mode stack pop") - } else { - od.readOnly -= 1 - } -} - -func (od *simpleOrderer) absorb(st *types.AccountWrapper) { - if od.readOnly > 0 { - return - } - od.savedOp = append(od.savedOp, st) -} - -func (od *simpleOrderer) absorbStorage(st *types.AccountWrapper, _ *types.StorageWrapper) { - od.absorb(st) -} - -func (od *simpleOrderer) end_absorb() opIterator { - ret := iterateOp(od.savedOp) - return &ret -} - -type multiOpIterator []opIterator - -func (opss *multiOpIterator) next() *types.AccountWrapper { - - sl := *opss - if len(sl) == 0 { - return nil - } - - op := sl[0].next() - - for op == nil { - - sl = sl[1:] - *opss = sl - if len(sl) == 0 { - return nil - } - op = sl[0].next() - } - return op -} - -type rwTblOrderer struct { - readOnly int - readOnlySnapshot struct { - accounts map[string]*types.AccountWrapper - storages map[string]map[string]*types.StorageWrapper - } - initedData map[common.Address]*types.AccountWrapper - - // help to track all accounts being touched, and provide the - // completed account status for storage updating - traced map[string]*types.AccountWrapper - - opAccNonce map[string]*types.AccountWrapper - opAccBalance map[string]*types.AccountWrapper - opAccCodeHash map[string]*types.AccountWrapper - opStorage map[string]map[string]*types.StorageWrapper -} - -func NewSimpleOrderer() *simpleOrderer { return &simpleOrderer{} } - -func NewRWTblOrderer(inited map[common.Address]*types.StateAccount) *rwTblOrderer { - - initedAcc := make(map[common.Address]*types.AccountWrapper) - for addr, data := range inited { - if data == nil { - initedAcc[addr] = &types.AccountWrapper{ - Address: addr, - Balance: (*hexutil.Big)(big.NewInt(0)), - } - } else { - bl := data.Balance - if bl == nil { - bl = big.NewInt(0) - } - - initedAcc[addr] = &types.AccountWrapper{ - Address: addr, - Nonce: data.Nonce, - Balance: (*hexutil.Big)(bl), - KeccakCodeHash: common.BytesToHash(data.KeccakCodeHash), - PoseidonCodeHash: common.BytesToHash(data.PoseidonCodeHash), - CodeSize: data.CodeSize, - } - } - - } - - return &rwTblOrderer{ - initedData: initedAcc, - traced: make(map[string]*types.AccountWrapper), - opAccNonce: make(map[string]*types.AccountWrapper), - opAccBalance: make(map[string]*types.AccountWrapper), - opAccCodeHash: make(map[string]*types.AccountWrapper), - opStorage: make(map[string]map[string]*types.StorageWrapper), - } -} - -func (od *rwTblOrderer) readonly(mode bool) { - if mode { - if od.readOnly == 0 { - od.readOnlySnapshot.accounts = make(map[string]*types.AccountWrapper) - od.readOnlySnapshot.storages = make(map[string]map[string]*types.StorageWrapper) - } - od.readOnly += 1 - } else if od.readOnly == 0 { - panic("unexpected readonly mode stack pop") - } else { - od.readOnly -= 1 - if od.readOnly == 0 { - for addrS, st := range od.readOnlySnapshot.accounts { - od.absorb(st) - if m, existed := od.readOnlySnapshot.storages[addrS]; existed { - for _, stg := range m { - st.Storage = stg - od.absorbStorage(st, nil) - } - } - } - } - } -} - -func (od *rwTblOrderer) absorbStorage(st *types.AccountWrapper, before *types.StorageWrapper) { - if st.Storage == nil { - panic("do not call absorbStorage ") - } - - od.absorb(st) - addrStr := st.Address.String() - - if stg := st.Storage; stg != nil { - m, existed := od.opStorage[addrStr] - if !existed { - m = make(map[string]*types.StorageWrapper) - od.opStorage[addrStr] = m - } - - // key must be unified into 32 bytes - keyBytes := hexutil.MustDecode(stg.Key) - keyStr := common.BytesToHash(keyBytes).String() - - // trace every "touched" status for readOnly - if od.readOnly > 0 { - m, existed := od.readOnlySnapshot.storages[addrStr] - if !existed { - m = make(map[string]*types.StorageWrapper) - od.readOnlySnapshot.storages[addrStr] = m - } - if _, hashTraced := m[keyStr]; !hashTraced { - if before != nil { - m[keyStr] = before - } else { - m[keyStr] = stg - } - - } - } - - m[keyStr] = stg - } - -} - -func (od *rwTblOrderer) absorb(st *types.AccountWrapper) { - - initedRef, existed := od.initedData[st.Address] - if !existed { - panic("encounter unprepared status") - } - - addrStr := st.Address.String() - - // trace every "touched" status for readOnly - if od.readOnly > 0 { - snapShot, existed := od.traced[addrStr] - if !existed { - snapShot = initedRef - } - - if _, hasTraced := od.readOnlySnapshot.accounts[addrStr]; !hasTraced { - od.readOnlySnapshot.accounts[addrStr] = copyAccountState(snapShot) - } - } - - if isDeletedAccount(st) { - // for account delete, made a safer data for status - st = &types.AccountWrapper{ - Address: st.Address, - Balance: (*hexutil.Big)(big.NewInt(0)), - } - } - - od.traced[addrStr] = st - - // notice there would be at least one entry for all 3 fields when accessing an address - // this may caused extract "read" op in mpt circuit which has no corresponding one in rwtable - // we can avoid it unless obtaining more tips from the understanding of opcode - // but it would be ok if we have adopted the new lookup way (root_prev, root_cur) under discussion: - // https://github.com/privacy-scaling-explorations/zkevm-specs/issues/217 - - if traced, existed := od.opAccNonce[addrStr]; !existed { - traced = copyAccountState(st) - traced.Balance = initedRef.Balance - traced.KeccakCodeHash = initedRef.KeccakCodeHash - traced.PoseidonCodeHash = initedRef.PoseidonCodeHash - traced.CodeSize = initedRef.CodeSize - traced.Storage = nil - od.opAccNonce[addrStr] = traced - } else { - traced.Nonce = st.Nonce - } - - if traced, existed := od.opAccBalance[addrStr]; !existed { - traced = copyAccountState(st) - traced.KeccakCodeHash = initedRef.KeccakCodeHash - traced.PoseidonCodeHash = initedRef.PoseidonCodeHash - traced.CodeSize = initedRef.CodeSize - traced.Storage = nil - od.opAccBalance[addrStr] = traced - } else { - traced.Nonce = st.Nonce - traced.Balance = st.Balance - } - - if traced, existed := od.opAccCodeHash[addrStr]; !existed { - traced = copyAccountState(st) - traced.Storage = nil - od.opAccCodeHash[addrStr] = traced - } else { - traced.Nonce = st.Nonce - traced.Balance = st.Balance - traced.KeccakCodeHash = st.KeccakCodeHash - traced.PoseidonCodeHash = st.PoseidonCodeHash - traced.CodeSize = st.CodeSize - } - -} - -func (od *rwTblOrderer) end_absorb() opIterator { - // now sort every map by address / key - // inited has collected all address, just sort address once - sortedAddrs := make([]string, 0, len(od.traced)) - for addrs := range od.traced { - sortedAddrs = append(sortedAddrs, addrs) - } - sort.Strings(sortedAddrs) - - var iterNonce []*types.AccountWrapper - var iterBalance []*types.AccountWrapper - var iterCodeHash []*types.AccountWrapper - var iterStorage []*types.AccountWrapper - - for _, addrStr := range sortedAddrs { - - if v, existed := od.opAccNonce[addrStr]; existed { - iterNonce = append(iterNonce, v) - } - - if v, existed := od.opAccBalance[addrStr]; existed { - iterBalance = append(iterBalance, v) - } - - if v, existed := od.opAccCodeHash[addrStr]; existed { - iterCodeHash = append(iterCodeHash, v) - } - - if stgM, existed := od.opStorage[addrStr]; existed { - - tracedStatus := od.traced[addrStr] - if tracedStatus == nil { - panic("missed traced status found in storage slot") - } - - sortedKeys := make([]string, 0, len(stgM)) - for key := range stgM { - sortedKeys = append(sortedKeys, key) - } - sort.Strings(sortedKeys) - - for _, key := range sortedKeys { - st := copyAccountState(tracedStatus) - st.Storage = stgM[key] - iterStorage = append(iterStorage, st) - } - } - - } - - var finalRet []opIterator - for _, arr := range [][]*types.AccountWrapper{iterNonce, iterBalance, iterCodeHash, iterStorage} { - wrappedIter := iterateOp(arr) - finalRet = append(finalRet, &wrappedIter) - } - - wrappedRet := multiOpIterator(finalRet) - return &wrappedRet -} diff --git a/trie/zkproof/types.go b/trie/zkproof/types.go index 178c9912d8..51ad8cf9d0 100644 --- a/trie/zkproof/types.go +++ b/trie/zkproof/types.go @@ -4,14 +4,6 @@ import ( "github.com/scroll-tech/go-ethereum/common/hexutil" ) -type MPTWitnessType int - -const ( - MPTWitnessNothing MPTWitnessType = iota - MPTWitnessNatural - MPTWitnessRWTbl -) - // SMTPathNode represent a node in the SMT Path, all hash is saved by the present of // zktype.Hash type SMTPathNode struct { diff --git a/trie/zkproof/writer.go b/trie/zkproof/writer.go deleted file mode 100644 index 003533a774..0000000000 --- a/trie/zkproof/writer.go +++ /dev/null @@ -1,852 +0,0 @@ -package zkproof - -import ( - "bytes" - "encoding/json" - "fmt" - "math/big" - - zktrie "github.com/scroll-tech/zktrie/trie" - zkt "github.com/scroll-tech/zktrie/types" - - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/common/hexutil" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/ethdb/memorydb" - "github.com/scroll-tech/go-ethereum/log" - "github.com/scroll-tech/go-ethereum/trie" -) - -type proofList [][]byte - -func (n *proofList) Put(key []byte, value []byte) error { - *n = append(*n, value) - return nil -} - -func (n *proofList) Delete(key []byte) error { - panic("not supported") -} - -func addressToKey(addr common.Address) *zkt.Hash { - var preImage zkt.Byte32 - copy(preImage[:], addr.Bytes()) - - h, err := preImage.Hash() - if err != nil { - log.Error("hash failure", "preImage", hexutil.Encode(preImage[:])) - return nil - } - return zkt.NewHashFromBigInt(h) -} - -// resume the proof bytes into db and return the leaf node -func resumeProofs(proof []hexutil.Bytes, db *memorydb.Database) *zktrie.Node { - for _, buf := range proof { - - n, err := zktrie.DecodeSMTProof(buf) - if err != nil { - log.Warn("decode proof string fail", "error", err) - } else if n != nil { - hash, err := n.NodeHash() - if err != nil { - log.Warn("node has no valid node hash", "error", err) - } else { - //notice: must consistent with trie/merkletree.go - bt := hash[:] - db.Put(bt, buf) - if n.Type == zktrie.NodeTypeLeaf_New || n.Type == zktrie.NodeTypeEmpty_New { - return n - } - } - } - - } - - return nil -} - -// we have a trick here which suppose the proof array include all middle nodes along the -// whole path in sequence, from root to leaf -func decodeProofForMPTPath(proof proofList, path *SMTPath) { - - var lastNode *zktrie.Node - keyPath := big.NewInt(0) - path.KeyPathPart = (*hexutil.Big)(keyPath) - - keyCounter := big.NewInt(1) - - for _, buf := range proof { - n, err := zktrie.DecodeSMTProof(buf) - if err != nil { - log.Warn("decode proof string fail", "error", err) - } else if n != nil { - hash, err := n.NodeHash() - if err != nil { - log.Warn("node has no valid node hash", "error", err) - return - } - if lastNode == nil { - // notice: use little-endian represent inside Hash ([:] or Byte32()) - path.Root = hash[:] - } else { - if bytes.Equal(hash[:], lastNode.ChildL[:]) { - path.Path = append(path.Path, SMTPathNode{ - Value: hash[:], - Sibling: lastNode.ChildR[:], - }) - } else if bytes.Equal(hash[:], lastNode.ChildR[:]) { - path.Path = append(path.Path, SMTPathNode{ - Value: hash[:], - Sibling: lastNode.ChildL[:], - }) - keyPath.Add(keyPath, keyCounter) - } else { - panic("Unexpected proof form") - } - keyCounter.Mul(keyCounter, big.NewInt(2)) - } - switch n.Type { - case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1, zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3: - lastNode = n - case zktrie.NodeTypeLeaf_New: - vhash, _ := n.ValueHash() - path.Leaf = &SMTPathNode{ - //here we just return the inner represent of hash (little endian, reversed byte order to common hash) - Value: vhash[:], - Sibling: n.NodeKey[:], - } - //sanity check - keyPart := keyPath.Bytes() - for i, b := range keyPart { - ri := len(keyPart) - i - cb := path.Leaf.Sibling[ri-1] //notice the output is little-endian - if b&cb != b { - panic(fmt.Errorf("path key not match: part is %x but key is %x", keyPart, []byte(path.Leaf.Sibling[:]))) - } - } - - return - case zktrie.NodeTypeEmpty_New: - return - default: - panic(fmt.Errorf("unknown node type %d", n.Type)) - } - } - } - - panic("Unexpected finished here") -} - -type zktrieProofWriter struct { - db *trie.ZktrieDatabase - tracingZktrie *trie.ZkTrie - tracingStorageTries map[common.Address]*trie.ZkTrie - tracingAccounts map[common.Address]*types.StateAccount -} - -func (wr *zktrieProofWriter) TracingAccounts() map[common.Address]*types.StateAccount { - return wr.tracingAccounts -} - -func NewZkTrieProofWriter(storage *types.StorageTrace) (*zktrieProofWriter, error) { - - underlayerDb := memorydb.New() - zkDb := trie.NewZktrieDatabase(underlayerDb) - - accounts := make(map[common.Address]*types.StateAccount) - - // resuming proof bytes to underlayerDb - for addrs, proof := range storage.Proofs { - if n := resumeProofs(proof, underlayerDb); n != nil { - addr := common.HexToAddress(addrs) - if n.Type == zktrie.NodeTypeEmpty_New { - accounts[addr] = nil - } else if acc, err := types.UnmarshalStateAccount(n.Data()); err == nil { - if bytes.Equal(n.NodeKey[:], addressToKey(addr)[:]) { - accounts[addr] = acc - } else { - // should still mark the address as being trace (data not existed yet) - accounts[addr] = nil - } - - } else { - return nil, fmt.Errorf("decode account bytes fail: %s, raw data [%x]", err, n.Data()) - } - - } else { - return nil, fmt.Errorf("can not resume proof for address %s", addrs) - } - } - - storages := make(map[common.Address]*trie.ZkTrie) - - for addrs, stgLists := range storage.StorageProofs { - - addr := common.HexToAddress(addrs) - accState, existed := accounts[addr] - if !existed { - // trace is malformed but currently we just warn about that - log.Warn("no account state found for this addr, mal records", "address", addrs) - continue - } else if accState == nil { - // create an empty zktrie for uninit address - storages[addr], _ = trie.NewZkTrie(common.Hash{}, zkDb) - continue - } - - for keys, proof := range stgLists { - - if n := resumeProofs(proof, underlayerDb); n != nil { - var err error - storages[addr], err = trie.NewZkTrie(accState.Root, zkDb) - if err != nil { - return nil, fmt.Errorf("zktrie create failure for storage in addr <%s>: %s, (root %s)", addrs, err, accState.Root) - } - - } else { - return nil, fmt.Errorf("can not resume proof for storage %s@%s", keys, addrs) - } - - } - } - - for _, delProof := range storage.DeletionProofs { - - n, err := zktrie.DecodeSMTProof(delProof) - if err != nil { - log.Warn("decode delproof string fail", "error", err, "node", delProof) - } else if n != nil { - hash, err := n.NodeHash() - if err != nil { - log.Warn("node has no valid node hash", "error", err) - } else { - //notice: must consistent with trie/merkletree.go - bt := hash[:] - underlayerDb.Put(bt, delProof) - } - } - } - - zktrie, err := trie.NewZkTrie( - storage.RootBefore, - trie.NewZktrieDatabase(underlayerDb), - ) - if err != nil { - return nil, fmt.Errorf("zktrie create failure: %s", err) - } - - // sanity check - if !bytes.Equal(zktrie.Hash().Bytes(), storage.RootBefore.Bytes()) { - return nil, fmt.Errorf("unmatch init trie hash: expected %x but has %x", storage.RootBefore.Bytes(), zktrie.Hash().Bytes()) - } - - return &zktrieProofWriter{ - db: zkDb, - tracingZktrie: zktrie, - tracingAccounts: accounts, - tracingStorageTries: storages, - }, nil -} - -const ( - posSSTOREBefore = 0 - posCREATE = 0 - posCREATEAfter = 1 - posCALL = 2 - posSTATICCALL = 0 - - // posSELFDESTRUCT = 2 -) - -func getAccountState(l *types.StructLogRes, pos int) *types.AccountWrapper { - if exData := l.ExtraData; exData == nil { - return nil - } else if len(exData.StateList) < pos { - return nil - } else { - return exData.StateList[pos] - } -} - -func copyAccountState(st *types.AccountWrapper) *types.AccountWrapper { - - var stg *types.StorageWrapper - if st.Storage != nil { - stg = &types.StorageWrapper{ - Key: st.Storage.Key, - Value: st.Storage.Value, - } - } - - return &types.AccountWrapper{ - Nonce: st.Nonce, - Balance: (*hexutil.Big)(big.NewInt(0).Set(st.Balance.ToInt())), - KeccakCodeHash: st.KeccakCodeHash, - PoseidonCodeHash: st.PoseidonCodeHash, - CodeSize: st.CodeSize, - Address: st.Address, - Storage: stg, - } -} - -func isDeletedAccount(state *types.AccountWrapper) bool { - return state.Nonce == 0 && bytes.Equal(state.KeccakCodeHash.Bytes(), common.Hash{}.Bytes()) -} - -func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount { - - if isDeletedAccount(state) { - return nil - } - - return &types.StateAccount{ - Nonce: state.Nonce, - Balance: (*big.Int)(state.Balance), - KeccakCodeHash: state.KeccakCodeHash.Bytes(), - PoseidonCodeHash: state.PoseidonCodeHash.Bytes(), - CodeSize: state.CodeSize, - // Root omitted intentionally - } -} - -// for sanity check -func verifyAccount(addr common.Address, data *types.StateAccount, leaf *SMTPathNode) error { - - if leaf == nil { - if data != nil { - return fmt.Errorf("path has no corresponding leaf for account") - } else { - return nil - } - } - - addrKey := addressToKey(addr) - if !bytes.Equal(addrKey[:], leaf.Sibling) { - if data != nil { - return fmt.Errorf("unmatch leaf node in address: %s", addr) - } - } else if data != nil { - arr, flag := data.MarshalFields() - h, err := zkt.HandlingElemsAndByte32(flag, arr) - //log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16)) - - if err != nil { - return fmt.Errorf("fail to hash account: %v", err) - } - if !bytes.Equal(h[:], leaf.Value) { - return fmt.Errorf("unmatch data in leaf for address %s", addr) - } - } - return nil -} - -// for sanity check -func verifyStorage(key *zkt.Byte32, data *zkt.Byte32, leaf *SMTPathNode) error { - - emptyData := bytes.Equal(data[:], common.Hash{}.Bytes()) - - if leaf == nil { - if !emptyData { - return fmt.Errorf("path has no corresponding leaf for storage") - } else { - return nil - } - } - - keyHash, err := key.Hash() - if err != nil { - return err - } - - if !bytes.Equal(zkt.NewHashFromBigInt(keyHash)[:], leaf.Sibling) { - if !emptyData { - return fmt.Errorf("unmatch leaf node in storage: %x", key[:]) - } - } else { - h, err := data.Hash() - //log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16)) - - if err != nil { - return fmt.Errorf("fail to hash data: %v", err) - } - if !bytes.Equal(zkt.NewHashFromBigInt(h)[:], leaf.Value) { - return fmt.Errorf("unmatch data in leaf for storage %x", key[:]) - } - } - return nil -} - -// update traced account state, and return the corresponding trace object which -// is still opened for more infos -// the updated accData state is obtained by a closure which enable it being derived from current status -func (w *zktrieProofWriter) traceAccountUpdate(addr common.Address, updateAccData func(*types.StateAccount) *types.StateAccount) (*StorageTrace, error) { - - out := new(StorageTrace) - //account trie - out.Address = addr.Bytes() - out.AccountPath = [2]*SMTPath{{}, {}} - //fill dummy - out.AccountUpdate = [2]*StateAccount{} - - accDataBefore, existed := w.tracingAccounts[addr] - if !existed { - //sanity check - panic(fmt.Errorf("code do not add initialized status for account %s", addr)) - } - - var proof proofList - s_key, _ := zkt.ToSecureKeyBytes(addr.Bytes()) - if err := w.tracingZktrie.Prove(s_key.Bytes(), 0, &proof); err != nil { - return nil, fmt.Errorf("prove BEFORE state fail: %s", err) - } - - decodeProofForMPTPath(proof, out.AccountPath[0]) - if err := verifyAccount(addr, accDataBefore, out.AccountPath[0].Leaf); err != nil { - panic(fmt.Errorf("code fail to trace account status correctly: %s", err)) - } - if accDataBefore != nil { - // we have ensured the nBefore has a key corresponding to the query one - out.AccountKey = out.AccountPath[0].Leaf.Sibling - out.AccountUpdate[0] = &StateAccount{ - Nonce: int(accDataBefore.Nonce), - Balance: (*hexutil.Big)(big.NewInt(0).Set(accDataBefore.Balance)), - KeccakCodeHash: accDataBefore.KeccakCodeHash, - PoseidonCodeHash: accDataBefore.PoseidonCodeHash, - CodeSize: accDataBefore.CodeSize, - } - } - - accData := updateAccData(accDataBefore) - if accData != nil { - out.AccountUpdate[1] = &StateAccount{ - Nonce: int(accData.Nonce), - Balance: (*hexutil.Big)(big.NewInt(0).Set(accData.Balance)), - KeccakCodeHash: accData.KeccakCodeHash, - PoseidonCodeHash: accData.PoseidonCodeHash, - CodeSize: accData.CodeSize, - } - } - - if accData != nil { - if err := w.tracingZktrie.TryUpdateAccount(addr.Bytes32(), accData); err != nil { - return nil, fmt.Errorf("update zktrie account state fail: %s", err) - } - w.tracingAccounts[addr] = accData - } else if accDataBefore != nil { - if err := w.tracingZktrie.TryDelete(addr.Bytes32()); err != nil { - return nil, fmt.Errorf("delete zktrie account state fail: %s", err) - } - w.tracingAccounts[addr] = nil - } // notice if both before/after is nil, we do not touch zktrie - - proof = proofList{} - if err := w.tracingZktrie.Prove(s_key.Bytes(), 0, &proof); err != nil { - return nil, fmt.Errorf("prove AFTER state fail: %s", err) - } - - decodeProofForMPTPath(proof, out.AccountPath[1]) - if err := verifyAccount(addr, accData, out.AccountPath[1].Leaf); err != nil { - panic(fmt.Errorf("state AFTER has no valid account: %s", err)) - } - if accData != nil { - if out.AccountKey == nil { - out.AccountKey = out.AccountPath[1].Leaf.Sibling[:] - } - //now accountKey must has been filled - } - - // notice we have change that no leaf (account data) exist in either before or after, - // for that case we had to calculate the nodeKey here - if out.AccountKey == nil { - word := zkt.NewByte32FromBytesPaddingZero(addr.Bytes()) - k, err := word.Hash() - if err != nil { - panic(fmt.Errorf("unexpected hash error for address: %s", err)) - } - kHash := zkt.NewHashFromBigInt(k) - out.AccountKey = hexutil.Bytes(kHash[:]) - } - - return out, nil -} - -// update traced storage state, and return the corresponding trace object -func (w *zktrieProofWriter) traceStorageUpdate(addr common.Address, key, value []byte) (*StorageTrace, error) { - - trie := w.tracingStorageTries[addr] - if trie == nil { - return nil, fmt.Errorf("no trace storage trie for %s", addr) - } - - statePath := [2]*SMTPath{{}, {}} - stateUpdate := [2]*StateStorage{} - - storeKey := zkt.NewByte32FromBytesPaddingZero(common.BytesToHash(key).Bytes()) - storeValueBefore := trie.Get(storeKey[:]) - storeValue := zkt.NewByte32FromBytes(value) - valZero := zkt.Byte32{} - - if storeValueBefore != nil && !bytes.Equal(storeValueBefore[:], common.Hash{}.Bytes()) { - stateUpdate[0] = &StateStorage{ - Key: storeKey.Bytes(), - Value: storeValueBefore, - } - } - - var storageBeforeProof, storageAfterProof proofList - s_key, _ := zkt.ToSecureKeyBytes(storeKey.Bytes()) - if err := trie.Prove(s_key.Bytes(), 0, &storageBeforeProof); err != nil { - return nil, fmt.Errorf("prove BEFORE storage state fail: %s", err) - } - - decodeProofForMPTPath(storageBeforeProof, statePath[0]) - if err := verifyStorage(storeKey, zkt.NewByte32FromBytes(storeValueBefore), statePath[0].Leaf); err != nil { - panic(fmt.Errorf("storage BEFORE has no valid data: %s (%v)", err, statePath[0])) - } - - if !bytes.Equal(storeValue.Bytes(), common.Hash{}.Bytes()) { - if err := trie.TryUpdate(storeKey.Bytes(), storeValue.Bytes()); err != nil { - return nil, fmt.Errorf("update zktrie storage fail: %s", err) - } - stateUpdate[1] = &StateStorage{ - Key: storeKey.Bytes(), - Value: storeValue.Bytes(), - } - } else { - if err := trie.TryDelete(storeKey.Bytes()); err != nil { - return nil, fmt.Errorf("delete zktrie storage fail: %s", err) - } - } - - if err := trie.Prove(s_key.Bytes(), 0, &storageAfterProof); err != nil { - return nil, fmt.Errorf("prove AFTER storage state fail: %s", err) - } - decodeProofForMPTPath(storageAfterProof, statePath[1]) - if err := verifyStorage(storeKey, storeValue, statePath[1].Leaf); err != nil { - panic(fmt.Errorf("storage AFTER has no valid data: %s (%v)", err, statePath[1])) - } - - out, err := w.traceAccountUpdate(addr, - func(acc *types.StateAccount) *types.StateAccount { - if acc == nil { - // in case we read an unexist account - if !bytes.Equal(valZero.Bytes(), value) { - panic(fmt.Errorf("write to an unexist account [%s] which is not allowed", addr)) - } - return nil - } - - //sanity check - if accRootFromState := zkt.ReverseByteOrder(statePath[0].Root); !bytes.Equal(acc.Root[:], accRootFromState) { - panic(fmt.Errorf("unexpected storage root before: [%s] vs [%x]", acc.Root, accRootFromState)) - } - return &types.StateAccount{ - Nonce: acc.Nonce, - Balance: acc.Balance, - Root: common.BytesToHash(zkt.ReverseByteOrder(statePath[1].Root)), - KeccakCodeHash: acc.KeccakCodeHash, - PoseidonCodeHash: acc.PoseidonCodeHash, - CodeSize: acc.CodeSize, - } - }) - if err != nil { - return nil, fmt.Errorf("update account %s in SSTORE fail: %s", addr, err) - } - - if stateUpdate[1] != nil { - out.StateKey = statePath[1].Leaf.Sibling - } else if stateUpdate[0] != nil { - out.StateKey = statePath[0].Leaf.Sibling - } else { - // it occurs when we are handling SLOAD with non-exist value - // still no pretty idea, had to touch the internal behavior in zktrie .... - if h, err := storeKey.Hash(); err != nil { - return nil, fmt.Errorf("hash storekey fail: %s", err) - } else { - out.StateKey = zkt.NewHashFromBigInt(h)[:] - } - stateUpdate[1] = &StateStorage{ - Key: storeKey.Bytes(), - Value: valZero.Bytes(), - } - stateUpdate[0] = stateUpdate[1] - } - - out.StatePath = statePath - out.StateUpdate = stateUpdate - return out, nil -} - -func (w *zktrieProofWriter) HandleNewState(accountState *types.AccountWrapper) (*StorageTrace, error) { - - if accountState.Storage != nil { - storeAddr := hexutil.MustDecode(accountState.Storage.Key) - storeValue := hexutil.MustDecode(accountState.Storage.Value) - return w.traceStorageUpdate(accountState.Address, storeAddr, storeValue) - } else { - - var stateRoot common.Hash - accData := getAccountDataFromLogState(accountState) - - out, err := w.traceAccountUpdate(accountState.Address, func(accBefore *types.StateAccount) *types.StateAccount { - if accBefore != nil { - stateRoot = accBefore.Root - } - // we need to restore stateRoot from before - if accData != nil { - accData.Root = stateRoot - } - return accData - }) - if err != nil { - return nil, fmt.Errorf("update account state %s fail: %s", accountState.Address, err) - } - - hash := zkt.NewHashFromBytes(stateRoot[:]) - out.CommonStateRoot = hash[:] - return out, nil - } - -} - -func handleLogs(od opOrderer, currentContract common.Address, logs []*types.StructLogRes) { - logStack := []int{0} - contractStack := map[int]common.Address{} - callEnterAddress := currentContract - - // now trace every OP which could cause changes on state: - for i, sLog := range logs { - - //trace log stack by depth rather than scanning specified op - if sl := len(logStack); sl < sLog.Depth { - logStack = append(logStack, i) - //update currentContract according to previous op - contractStack[sl] = currentContract - currentContract = callEnterAddress - - } else if sl > sLog.Depth { - logStack = logStack[:sl-1] - currentContract = contractStack[sLog.Depth] - resumePos := logStack[len(logStack)-1] - calledLog := logs[resumePos] - - //no need to handle fail calling - if calledLog.ExtraData != nil { - if !calledLog.ExtraData.CallFailed { - //reentry the last log which "cause" the calling, some handling may needed - switch calledLog.Op { - case "CREATE", "CREATE2": - //addr, accDataBefore := getAccountDataFromProof(calledLog, posCALLBefore) - od.absorb(getAccountState(calledLog, posCREATEAfter)) - } - } else { - od.readonly(false) - } - } - - } else { - logStack[sl-1] = i - } - //sanity check - if len(logStack) != sLog.Depth { - panic("tracking log stack failure") - } - callEnterAddress = currentContract - - //check extra status for current op if it is a call - if extraData := sLog.ExtraData; extraData != nil { - if extraData.CallFailed || len(sLog.ExtraData.Caller) < 2 { - // no enough caller data (2) is being capture indicate we are in an immediate failure - // i.e. it fail before stack entry (like no enough balance for a "call with value"), - // or we just not handle this calling op correctly yet - - // for a failed option, now we just purpose nothing happens (FIXME: it is inconsentent with mpt_table) - // except for CREATE, for which the callee's nonce would be increased - switch sLog.Op { - case "CREATE", "CREATE2": - st := copyAccountState(extraData.Caller[0]) - st.Nonce += 1 - od.absorb(st) - } - } - - if extraData.CallFailed { - od.readonly(true) - } - // now trace caller's status first - if caller := extraData.Caller; len(caller) >= 2 { - od.absorb(caller[1]) - } - } - - switch sLog.Op { - case "SELFDESTRUCT": - // NOTE: this op code has been disabled so we treat it as nothing now - - //in SELFDESTRUCT, a call on target address is made so the balance would be updated - //in the last item - //stateTarget := getAccountState(sLog, posSELFDESTRUCT) - //od.absorb(stateTarget) - //then build an "deleted state", only address and other are default - //od.absorb(&types.AccountWrapper{Address: currentContract}) - - case "CREATE", "CREATE2": - // notice in immediate failure we have no enough tracing in extraData - if len(sLog.ExtraData.StateList) >= 2 { - state := getAccountState(sLog, posCREATE) - od.absorb(state) - //update contract to CREATE addr - callEnterAddress = state.Address - } - - case "CALL", "CALLCODE": - // notice in immediate failure we have no enough tracing in extraData - if len(sLog.ExtraData.StateList) >= 3 { - state := getAccountState(sLog, posCALL) - od.absorb(state) - callEnterAddress = state.Address - } - case "STATICCALL": - //static call has no update on target address (and no immediate failure?) - callEnterAddress = getAccountState(sLog, posSTATICCALL).Address - case "DELEGATECALL": - - case "SLOAD": - accountState := getAccountState(sLog, posSSTOREBefore) - od.absorbStorage(accountState, nil) - case "SSTORE": - log.Debug("build SSTORE", "pc", sLog.Pc, "key", sLog.Stack[len(sLog.Stack)-1]) - accountState := copyAccountState(getAccountState(sLog, posSSTOREBefore)) - // notice the log only provide the value BEFORE store and it is not suitable for our protocol, - // here we change it into value AFTER update - before := accountState.Storage - accountState.Storage = &types.StorageWrapper{ - Key: sLog.Stack[len(sLog.Stack)-1], - Value: sLog.Stack[len(sLog.Stack)-2], - } - od.absorbStorage(accountState, before) - - default: - } - } -} - -func HandleTx(od opOrderer, txResult *types.ExecutionResult) { - - // the from state is read before tx is handled and nonce is added, we combine both - preTxSt := copyAccountState(txResult.From) - preTxSt.Nonce += 1 - od.absorb(preTxSt) - - if txResult.Failed { - od.readonly(true) - } - - var toAddr common.Address - if state := txResult.AccountCreated; state != nil { - od.absorb(state) - toAddr = state.Address - } else { - toAddr = txResult.To.Address - } - - handleLogs(od, toAddr, txResult.StructLogs) - if txResult.Failed { - od.readonly(false) - } - - for _, state := range txResult.AccountsAfter { - // special case: for suicide, the state has been captured in SELFDESTRUCT - // and we skip it here - if isDeletedAccount(state) { - log.Debug("skip suicide address", "address", state.Address) - continue - } - - od.absorb(state) - } - -} - -const defaultOrdererScheme = MPTWitnessRWTbl - -var usedOrdererScheme = defaultOrdererScheme - -func SetOrderScheme(t MPTWitnessType) { usedOrdererScheme = t } - -// HandleBlockTrace only for backward compatibility -func HandleBlockTrace(block *types.BlockTrace) ([]*StorageTrace, error) { - return HandleBlockTraceEx(block, usedOrdererScheme) -} - -func HandleBlockTraceEx(block *types.BlockTrace, ordererScheme MPTWitnessType) ([]*StorageTrace, error) { - - writer, err := NewZkTrieProofWriter(block.StorageTrace) - if err != nil { - return nil, err - } - - var od opOrderer - switch ordererScheme { - case MPTWitnessNothing: - panic("should not come here when scheme is 0") - case MPTWitnessNatural: - od = &simpleOrderer{} - case MPTWitnessRWTbl: - od = NewRWTblOrderer(writer.tracingAccounts) - default: - return nil, fmt.Errorf("unrecognized scheme %d", ordererScheme) - } - - for _, tx := range block.ExecutionResults { - HandleTx(od, tx) - } - - // notice some coinbase addr (like all zero) is in fact not exist and should not be update - // TODO: not a good solution, just for patch ... - if coinbaseData := writer.tracingAccounts[block.Coinbase.Address]; coinbaseData != nil { - od.absorb(block.Coinbase) - } - - opDisp := od.end_absorb() - var outTrace []*StorageTrace - - for op := opDisp.next(); op != nil; op = opDisp.next() { - trace, err := writer.HandleNewState(op) - if err != nil { - return nil, err - } - outTrace = append(outTrace, trace) - } - - finalHash := writer.tracingZktrie.Hash() - if !bytes.Equal(finalHash.Bytes(), block.StorageTrace.RootAfter.Bytes()) { - return outTrace, fmt.Errorf("unmatch hash: [%x] vs [%x]", finalHash.Bytes(), block.StorageTrace.RootAfter.Bytes()) - } - - return outTrace, nil - -} - -func FillBlockTraceForMPTWitness(order MPTWitnessType, block *types.BlockTrace) error { - - if order == MPTWitnessNothing { - return nil - } - - trace, err := HandleBlockTraceEx(block, order) - if err != nil { - return err - } - - msg, err := json.Marshal(trace) - if err != nil { - return err - } - - rawmsg := json.RawMessage(msg) - - block.MPTWitness = &rawmsg - return nil -}