diff --git a/core/blockchain.go b/core/blockchain.go index ed934b341969..f3c669a90eb9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1348,18 +1348,25 @@ func (bc *BlockChain) writeBlockResult(state *state.StateDB, block *types.Block, blockResult.BlockTrace = types.NewTraceBlock(bc.chainConfig, block) for i, tx := range block.Transactions() { evmTrace := blockResult.ExecutionResults[i] - // Get the sender's address. + + // Get sender's address. from, _ := types.Sender(types.MakeSigner(bc.chainConfig, block.Number()), tx) - // Get account's proof. + evmTrace.Sender = &types.AccountProofWrapper{ + Address: from, + Nonce: state.GetNonce(from), + Balance: state.GetBalance(from), + } + // Get sender's account proof. proof, err := state.GetProof(from) if err != nil { log.Error("Failed to get proof", "blockNumber", block.NumberU64(), "address", from.String(), "err", err) } else { - evmTrace.Proof = make([]string, len(proof)) + evmTrace.Sender.Proof = make([]string, len(proof)) for i := range proof { - evmTrace.Proof[i] = hexutil.Encode(proof[i]) + evmTrace.Sender.Proof[i] = hexutil.Encode(proof[i]) } } + // Contract is called if len(tx.Data()) != 0 && tx.To() != nil { evmTrace.ByteCode = hexutil.Encode(state.GetCode(*tx.To())) diff --git a/core/types/l2trace.go b/core/types/l2trace.go index 36e1a043ab90..beea08907b57 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -1,6 +1,8 @@ package types import ( + "math/big" + "github.com/scroll-tech/go-ethereum/common" ) @@ -17,12 +19,12 @@ type ExecutionResult struct { Gas uint64 `json:"gas"` Failed bool `json:"failed"` ReturnValue string `json:"returnValue,omitempty"` + // Sender's account proof. + Sender *AccountProofWrapper `json:"sender,omitempty"` // It's exist only when tx is a contract call. CodeHash *common.Hash `json:"codeHash,omitempty"` // If it is a contract call, the contract code is returned. - ByteCode string `json:"byteCode,omitempty"` - // The account's proof. - Proof []string `json:"proof,omitempty"` + ByteCode string `json:"byteCode,omitempty"` StructLogs []StructLogRes `json:"structLogs"` } @@ -42,26 +44,38 @@ type StructLogRes struct { } type ExtraData struct { - // CREATE | CREATE2: sender address - From *common.Address `json:"from,omitempty"` - // CREATE: sender nonce - Nonce *uint64 `json:"nonce,omitempty"` // CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to address’s code, stack.nth_last(1) address’s code] CodeList [][]byte `json:"codeList,omitempty"` // SSTORE | SLOAD: [storageProof] // SELFDESTRUCT: [contract address’s accountProof, stack.nth_last(0) address’s accountProof] // SELFBALANCE: [contract address’s accountProof] // BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s accountProof] - // CREATE | CREATE2: [created contract address’s accountProof] + // CREATE | CREATE2: [sender's accountProof, created contract address’s accountProof] // CALL | CALLCODE: [caller contract address’s accountProof, stack.nth_last(1) address’s accountProof] - ProofList [][]string `json:"proofList,omitempty"` + ProofList []*AccountProofWrapper `json:"proofList,omitempty"` +} + +type AccountProofWrapper struct { + Address common.Address `json:"address,omitempty"` + Nonce uint64 `json:"nonce,omitempty"` + Balance *big.Int `json:"balance,omitempty"` + Proof []string `json:"proof,omitempty"` + Storage *StorageProofWrapper `json:"storage,omitempty"` // StorageProofWrapper can be empty if irrelated to storage operation +} + +// while key & value can also be retrieved from StructLogRes.Storage, +// we still stored in here for roller's processing convenience. +type StorageProofWrapper struct { + Key string `json:"key,omitempty"` + Value string `json:"value,omitempty"` + Proof []string `json:"proof,omitempty"` } // NewExtraData create, init and return ExtraData func NewExtraData() *ExtraData { return &ExtraData{ CodeList: make([][]byte, 0), - ProofList: make([][]string, 0), + ProofList: make([]*AccountProofWrapper, 0), } } @@ -73,7 +87,7 @@ func (e *ExtraData) SealExtraData() *ExtraData { if len(e.ProofList) == 0 { e.ProofList = nil } - if e.From == nil && e.Nonce == nil && e.CodeList == nil && e.ProofList == nil { + if e.CodeList == nil && e.ProofList == nil { return nil } return e diff --git a/core/vm/logger.go b/core/vm/logger.go index 7570c13b19f8..20ad986f2175 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -198,6 +198,12 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop ) 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) + } + } else if op == SSTORE && stack.len() >= 2 { // capture SSTORE opcodes and record the written entry in the local storage. var ( diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index d97e6c14c3b5..c4633bde1329 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -17,10 +17,10 @@ var ( CALLCODE: {traceToAddressCode, traceLastNAddressCode(1), traceCallerProof, traceLastNAddressProof(1)}, DELEGATECALL: {traceToAddressCode, traceLastNAddressCode(1)}, STATICCALL: {traceToAddressCode, traceLastNAddressCode(1)}, - CREATE: {traceSenderAddress, traceCreatedContractProof, traceNonce}, - CREATE2: {traceSenderAddress, traceCreatedContractProof}, - SSTORE: {traceStorageProof}, - SLOAD: {traceStorageProof}, + 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, instead of state_after + SSTORE: {traceStorageProof}, // record state_after besides state_before SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)}, SELFBALANCE: {traceContractProof}, BALANCE: {traceLastNAddressProof(0)}, @@ -52,30 +52,15 @@ func traceLastNAddressCode(n int) traceFunc { } } -// traceSenderAddress gets sender address -func traceSenderAddress(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { - extraData.From = &l.env.Origin - return nil -} - -// traceNonce gets sender nonce -func traceNonce(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { - nonce := l.env.StateDB.GetNonce(l.env.Origin) - extraData.Nonce = &nonce - return nil -} - // traceStorageProof get contract's storage proof at storage_address func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { if scope.Stack.len() == 0 { return nil } - address := common.Hash(scope.Stack.peek().Bytes32()) - contract := scope.Contract - // Get storage proof. - storageProof, err := l.env.StateDB.GetStorageProof(contract.Address(), address) + key := common.Hash(scope.Stack.peek().Bytes32()) + proof, err := getWrappedProofForStorage(l, scope.Contract.Address(), key) if err == nil { - extraData.ProofList = append(extraData.ProofList, encodeProof(storageProof)) + extraData.ProofList = append(extraData.ProofList, proof) } return err } @@ -83,9 +68,9 @@ func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.Ex // traceContractProof gets the contract's account proof func traceContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { // Get account proof. - proof, err := l.env.StateDB.GetProof(scope.Contract.Address()) + proof, err := getWrappedProofForAddr(l, scope.Contract.Address()) if err == nil { - extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + extraData.ProofList = append(extraData.ProofList, proof) } return err } @@ -101,9 +86,9 @@ func traceCreatedContractProof(l *StructLogger, scope *ScopeContext, extraData * return errors.New("can't get created contract address from stack") } address := common.BytesToAddress(stackvalue.Bytes()) - proof, err := l.env.StateDB.GetProof(address) + proof, err := getWrappedProofForAddr(l, address) if err == nil { - extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + extraData.ProofList = append(extraData.ProofList, proof) } return err } @@ -117,9 +102,9 @@ func traceLastNAddressProof(n int) traceFunc { } address := common.Address(stack.data[stack.len()-1-n].Bytes20()) - proof, err := l.env.StateDB.GetProof(address) + proof, err := getWrappedProofForAddr(l, address) if err == nil { - extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + extraData.ProofList = append(extraData.ProofList, proof) } return err } @@ -128,13 +113,52 @@ func traceLastNAddressProof(n int) traceFunc { // traceCallerProof gets caller address's proof. func traceCallerProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { address := scope.Contract.CallerAddress - proof, err := l.env.StateDB.GetProof(address) + proof, err := getWrappedProofForAddr(l, address) if err == nil { - extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + extraData.ProofList = append(extraData.ProofList, proof) } return err } +// StorageProofWrapper will be empty +func getWrappedProofForAddr(l *StructLogger, address common.Address) (*types.AccountProofWrapper, error) { + proof, err := l.env.StateDB.GetProof(address) + if err != nil { + return nil, err + } + + return &types.AccountProofWrapper{ + Address: address, + Nonce: l.env.StateDB.GetNonce(address), + Balance: l.env.StateDB.GetBalance(address), + Proof: encodeProof(proof), + }, nil +} + +func getWrappedProofForStorage(l *StructLogger, address common.Address, key common.Hash) (*types.AccountProofWrapper, error) { + proof, err := l.env.StateDB.GetProof(address) + if err != nil { + return nil, err + } + + storageProof, err := l.env.StateDB.GetStorageProof(address, key) + if err != nil { + return nil, err + } + + return &types.AccountProofWrapper{ + Address: address, + Nonce: l.env.StateDB.GetNonce(address), + Balance: l.env.StateDB.GetBalance(address), + Proof: encodeProof(proof), + Storage: &types.StorageProofWrapper{ + Key: key.String(), + Value: l.env.StateDB.GetState(address, key).String(), + Proof: encodeProof(storageProof), + }, + }, nil +} + func encodeProof(proof [][]byte) (res []string) { if len(proof) == 0 { return nil