refactor: remove unused storage traces (#819)

This commit is contained in:
Ömer Faruk Irmak 2024-06-14 15:02:00 +03:00 committed by GitHub
parent fc9db9f65f
commit 316138099f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 6 additions and 1481 deletions

View file

@ -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)

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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)
}

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}