From 750ad5129b98411be928f4bc42d56e1c9ad343ab Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Mon, 28 Mar 2022 15:52:17 +0800 Subject: [PATCH 1/9] Move code of `captureStateAfter` to `captureState`, and refactor some code. --- core/vm/logger.go | 79 ++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index 48e2fd56947e..ceec72d2c831 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -180,44 +180,33 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop stck[i] = item } } - // Copy a snapshot of the current storage to a new container var ( - storage Storage - extraData *types.ExtraData + isStorageOp bool = false + storage Storage + storageKey common.Hash + storageValue common.Hash ) - if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) { - // initialise new changed values storage container for this contract - // if not present. - if l.storage[contract.Address()] == nil { - l.storage[contract.Address()] = make(Storage) - } - // capture SLOAD opcodes and record the read entry in the local storage + if !l.cfg.DisableStorage { if op == SLOAD && stack.len() >= 1 { - var ( - address = common.Hash(stack.data[stack.len()-1].Bytes32()) - value = l.env.StateDB.GetState(contract.Address(), address) - ) - l.storage[contract.Address()][address] = value - storage = l.storage[contract.Address()].Copy() - - extraData = types.NewExtraData() - if err := traceStorageProof(l, scope, extraData); err != nil { - log.Warn("Failed to get proof", "contract address", contract.Address().String(), "key", address.String(), "err", err) - } - + isStorageOp = true + storageKey = common.Hash(stack.data[stack.len()-1].Bytes32()) + storageValue = l.env.StateDB.GetState(contract.Address(), storageKey) } else if op == SSTORE && stack.len() >= 2 { - // capture SSTORE opcodes and record the written entry in the local storage. - var ( - value = common.Hash(stack.data[stack.len()-2].Bytes32()) - address = common.Hash(stack.data[stack.len()-1].Bytes32()) - ) - l.storage[contract.Address()][address] = value - storage = l.storage[contract.Address()].Copy() - - extraData = types.NewExtraData() - if err := traceStorageProof(l, scope, extraData); err != nil { - log.Warn("Failed to get proof", "contract address", contract.Address().String(), "key", address.String(), "err", err) - } + isStorageOp = true + storageKey = common.Hash(stack.data[stack.len()-1].Bytes32()) + storageValue = common.Hash(stack.data[stack.len()-2].Bytes32()) + } + } + extraData := types.NewExtraData() + if isStorageOp { + contractAddress := contract.Address() + if l.storage[contractAddress] == nil { + l.storage[contractAddress] = make(Storage) + } + l.storage[contractAddress][storageKey] = storageValue + storage = l.storage[contractAddress].Copy() + if err := traceStorageProof(l, scope, extraData); err != nil { + log.Warn("Failed to get proof", "contract address", contractAddress.String(), "key", storageKey.String(), "err", err) } } var rdata []byte @@ -225,33 +214,25 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop rdata = make([]byte, len(rData)) copy(rdata, rData) } - // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), extraData, err} - l.logs = append(l.logs, log) -} - -// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. -func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { - // check if already accumulated the specified number of logs - if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { - return - } - + structLog := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), extraData, err} + l.logs = append(l.logs, structLog) execFuncList, ok := OpcodeExecs[op] if !ok { return } - extraData := types.NewExtraData() // execute trace func list. for _, exec := range execFuncList { if err = exec(l, scope, extraData); err != nil { log.Error("Failed to trace data", "opcode", op.String(), "err", err) } } + structLog = StructLog{pc, op, gas, cost, nil, scope.Memory.Len(), nil, nil, nil, depth, l.env.StateDB.GetRefund(), extraData, err} + l.logs = append(l.logs, structLog) +} - log := StructLog{pc, op, gas, cost, nil, scope.Memory.Len(), nil, nil, nil, depth, l.env.StateDB.GetRefund(), extraData, err} - l.logs = append(l.logs, log) +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault From da359dd44909a1dd9fd3b2304f2108d6a0f39d3f Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Mon, 28 Mar 2022 17:25:09 +0800 Subject: [PATCH 2/9] Fix to use the right key in `traceStorageProof`. --- core/vm/logger_trace.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index 67102462d33e..ffa0e19cb46a 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -57,7 +57,8 @@ func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.Ex if scope.Stack.len() == 0 { return nil } - key := common.Hash(scope.Stack.peek().Bytes32()) + stack := scope.Stack + key := common.Hash(stack.data[stack.len()-1].Bytes32()) proof, err := getWrappedProofForStorage(l, scope.Contract.Address(), key) if err == nil { extraData.ProofList = append(extraData.ProofList, proof) From ba305ac182300e19799de683fd6da49725dabc1a Mon Sep 17 00:00:00 2001 From: HAOYUatHZ Date: Mon, 28 Mar 2022 19:13:38 +0800 Subject: [PATCH 3/9] clean up --- core/vm/logger.go | 11 +++-------- core/vm/logger_trace.go | 4 ++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index ceec72d2c831..e46b72eb0dfd 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -205,18 +205,12 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop } l.storage[contractAddress][storageKey] = storageValue storage = l.storage[contractAddress].Copy() - if err := traceStorageProof(l, scope, extraData); err != nil { - log.Warn("Failed to get proof", "contract address", contractAddress.String(), "key", storageKey.String(), "err", err) - } } var rdata []byte if l.cfg.EnableReturnData { rdata = make([]byte, len(rData)) copy(rdata, rData) } - // create a new snapshot of the EVM. - structLog := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), extraData, err} - l.logs = append(l.logs, structLog) execFuncList, ok := OpcodeExecs[op] if !ok { return @@ -227,11 +221,12 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop log.Error("Failed to trace data", "opcode", op.String(), "err", err) } } - structLog = StructLog{pc, op, gas, cost, nil, scope.Memory.Len(), nil, nil, nil, depth, l.env.StateDB.GetRefund(), extraData, err} + // create a new snapshot of the EVM. + structLog := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), extraData, err} l.logs = append(l.logs, structLog) } -// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +// TODO: CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { } diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index ffa0e19cb46a..91b3e144947f 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -19,8 +19,8 @@ var ( STATICCALL: {traceToAddressCode, traceLastNAddressCode(1)}, CREATE: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult CREATE2: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult - SLOAD: {}, // only record state_before in `CaptureState`, instead of state_after here - SSTORE: {traceStorageProof}, // record state_after besides state_before(in `CaptureState`) + SLOAD: {traceStorageProof}, + SSTORE: {traceStorageProof}, SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)}, SELFBALANCE: {traceContractProof}, BALANCE: {traceLastNAddressProof(0)}, From e147a0d766b388371e970cf9ebc71b70e5b805b7 Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Mon, 28 Mar 2022 20:09:56 +0800 Subject: [PATCH 4/9] Update `capatureStateAfter`. --- core/vm/logger.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index e46b72eb0dfd..1d5555e2cf69 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -226,8 +226,30 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop l.logs = append(l.logs, structLog) } -// TODO: CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { + logLen := len(l.logs) + if (l.cfg.Limit != 0 && l.cfg.Limit <= logLen) || l.cfg.DisableStorage || op != SSTORE { + return + } + var lastLog *StructLog = nil + for i := logLen - 1; i >= 0; i-- { + if l.logs[i].Op == SSTORE { + lastLog = &l.logs[i] + break + } + } + if lastLog == nil { + return + } + lastProofList := lastLog.ExtraData.ProofList + lastProofListLen := len(lastProofList) + if lastProofListLen == 0 { + return + } + extraData := types.NewExtraData() + extraData.ProofList = append(extraData.ProofList, lastProofList[lastProofListLen-1]) + log := StructLog{pc, op, gas, cost, nil, 0, nil, nil, nil, depth, l.env.StateDB.GetRefund(), extraData, err} + l.logs = append(l.logs, log) } // CaptureFault implements the EVMLogger interface to trace an execution fault From f9039b21fd1a0d1b2ec4d3b836354e00a5b69dd7 Mon Sep 17 00:00:00 2001 From: HAOYUatHZ Date: Mon, 28 Mar 2022 21:22:51 +0800 Subject: [PATCH 5/9] fix --- core/vm/logger.go | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index 1d5555e2cf69..c5b21ccc39b7 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -227,29 +227,32 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop } func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { - logLen := len(l.logs) - if (l.cfg.Limit != 0 && l.cfg.Limit <= logLen) || l.cfg.DisableStorage || op != SSTORE { - return - } - var lastLog *StructLog = nil - for i := logLen - 1; i >= 0; i-- { - if l.logs[i].Op == SSTORE { - lastLog = &l.logs[i] - break + if !l.cfg.DisableStorage && op == SLOAD { + logLen := len(l.logs) + if logLen <= 0 { + log.Error("Failed to trace after_state for sstore", "err", "empty length log") + return } + + lastLog := l.logs[logLen-1] + if lastLog.Op != SLOAD { + log.Error("Failed to trace after_state for sstore", "err", "op mismatch") + return + } + if lastLog.ExtraData == nil || len(lastLog.ExtraData.ProofList) == 0 { + log.Error("Failed to trace after_state for sstore", "err", "empty before_state ExtraData") + return + } + + contractAddress := scope.Contract.Address() + var storageKey common.Hash // TODO: how to get this? + proof, err := getWrappedProofForStorage(l, contractAddress, storageKey) + if err != nil { + log.Error("Failed to trace after_state storage_proof for sstore", "err", err) + } + + l.logs[logLen-1].ExtraData.ProofList = append(lastLog.ExtraData.ProofList, proof) } - if lastLog == nil { - return - } - lastProofList := lastLog.ExtraData.ProofList - lastProofListLen := len(lastProofList) - if lastProofListLen == 0 { - return - } - extraData := types.NewExtraData() - extraData.ProofList = append(extraData.ProofList, lastProofList[lastProofListLen-1]) - log := StructLog{pc, op, gas, cost, nil, 0, nil, nil, nil, depth, l.env.StateDB.GetRefund(), extraData, err} - l.logs = append(l.logs, log) } // CaptureFault implements the EVMLogger interface to trace an execution fault From 98553a1150d76a5d473e4eac00cd8d71204c8388 Mon Sep 17 00:00:00 2001 From: HAOYUatHZ Date: Mon, 28 Mar 2022 21:50:09 +0800 Subject: [PATCH 6/9] fix --- core/vm/logger.go | 18 +++++++++++------- core/vm/logger_trace.go | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index c5b21ccc39b7..349c878bc99a 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -181,30 +181,34 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop } } var ( - isStorageOp bool = false - storage Storage - storageKey common.Hash - storageValue common.Hash + recordStorageDetail bool = false + storage Storage + storageKey common.Hash + storageValue common.Hash ) if !l.cfg.DisableStorage { if op == SLOAD && stack.len() >= 1 { - isStorageOp = true + recordStorageDetail = true storageKey = common.Hash(stack.data[stack.len()-1].Bytes32()) storageValue = l.env.StateDB.GetState(contract.Address(), storageKey) } else if op == SSTORE && stack.len() >= 2 { - isStorageOp = true + recordStorageDetail = true storageKey = common.Hash(stack.data[stack.len()-1].Bytes32()) storageValue = common.Hash(stack.data[stack.len()-2].Bytes32()) } } extraData := types.NewExtraData() - if isStorageOp { + if recordStorageDetail { contractAddress := contract.Address() if l.storage[contractAddress] == nil { l.storage[contractAddress] = make(Storage) } l.storage[contractAddress][storageKey] = storageValue storage = l.storage[contractAddress].Copy() + + if err := traceContractProof(l, scope, extraData); err != nil { + log.Error("Failed to trace data", "opcode", op.String(), "err", err) + } } var rdata []byte if l.cfg.EnableReturnData { diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index 91b3e144947f..ead018b98ad5 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -19,8 +19,8 @@ var ( STATICCALL: {traceToAddressCode, traceLastNAddressCode(1)}, CREATE: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult CREATE2: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult - SLOAD: {traceStorageProof}, - SSTORE: {traceStorageProof}, + SLOAD: {}, // record storage_proof in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag + SSTORE: {}, // record storage_proof in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)}, SELFBALANCE: {traceContractProof}, BALANCE: {traceLastNAddressProof(0)}, From 0fbefb19c54a2426094ad045bcd13a40e9ed7d3f Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Mon, 28 Mar 2022 22:13:14 +0800 Subject: [PATCH 7/9] Get storage key from stack of last log. --- core/vm/logger.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index 349c878bc99a..d5c9832a535c 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -206,7 +206,7 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop l.storage[contractAddress][storageKey] = storageValue storage = l.storage[contractAddress].Copy() - if err := traceContractProof(l, scope, extraData); err != nil { + if err := traceStorageProof(l, scope, extraData); err != nil { log.Error("Failed to trace data", "opcode", op.String(), "err", err) } } @@ -231,7 +231,7 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop } func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { - if !l.cfg.DisableStorage && op == SLOAD { + if !l.cfg.DisableStorage && op == SSTORE { logLen := len(l.logs) if logLen <= 0 { log.Error("Failed to trace after_state for sstore", "err", "empty length log") @@ -239,7 +239,7 @@ func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, } lastLog := l.logs[logLen-1] - if lastLog.Op != SLOAD { + if lastLog.Op != SSTORE { log.Error("Failed to trace after_state for sstore", "err", "op mismatch") return } @@ -249,7 +249,7 @@ func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, } contractAddress := scope.Contract.Address() - var storageKey common.Hash // TODO: how to get this? + storageKey := common.Hash(lastLog.Stack[len(lastLog.Stack)-1].Bytes32()) proof, err := getWrappedProofForStorage(l, contractAddress, storageKey) if err != nil { log.Error("Failed to trace after_state storage_proof for sstore", "err", err) From fb611e25070baff26cfb37e18c0b91bf8199f8c7 Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Mon, 28 Mar 2022 22:16:23 +0800 Subject: [PATCH 8/9] Revert wrong fix. --- core/vm/logger_trace.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index ead018b98ad5..dfb75de4b349 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -57,8 +57,7 @@ func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.Ex if scope.Stack.len() == 0 { return nil } - stack := scope.Stack - key := common.Hash(stack.data[stack.len()-1].Bytes32()) + key := common.Hash(scope.Stack.peek().Bytes32()) proof, err := getWrappedProofForStorage(l, scope.Contract.Address(), key) if err == nil { extraData.ProofList = append(extraData.ProofList, proof) From d370332ea25f5f5dee3f3127e16521f2e498f779 Mon Sep 17 00:00:00 2001 From: HAOYUatHZ Date: Mon, 28 Mar 2022 22:47:22 +0800 Subject: [PATCH 9/9] minor --- core/vm/logger.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/vm/logger.go b/core/vm/logger.go index d5c9832a535c..938896748fb1 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -249,6 +249,10 @@ func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, } contractAddress := scope.Contract.Address() + if len(lastLog.Stack) <= 0 { + log.Error("Failed to trace after_state for sstore", "err", "empty stack for last log") + return + } storageKey := common.Hash(lastLog.Stack[len(lastLog.Stack)-1].Bytes32()) proof, err := getWrappedProofForStorage(l, contractAddress, storageKey) if err != nil {