diff --git a/core/blockchain.go b/core/blockchain.go index 2cfb1d8f6b15..ed934b341969 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -30,6 +30,7 @@ import ( lru "github.com/hashicorp/golang-lru" "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/mclock" "github.com/scroll-tech/go-ethereum/common/prque" "github.com/scroll-tech/go-ethereum/consensus" @@ -84,13 +85,14 @@ var ( ) const ( - bodyCacheLimit = 256 - blockCacheLimit = 256 - receiptsCacheLimit = 32 - txLookupCacheLimit = 1024 - maxFutureBlocks = 256 - maxTimeFutureBlocks = 30 - TriesInMemory = 128 + bodyCacheLimit = 256 + blockCacheLimit = 256 + receiptsCacheLimit = 32 + txLookupCacheLimit = 1024 + maxFutureBlocks = 256 + maxTimeFutureBlocks = 30 + TriesInMemory = 128 + blockResultCacheLimit = 128 // BlockChainVersion ensures that an incompatible database forces a resync from scratch. // @@ -191,13 +193,14 @@ type BlockChain struct { currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) - stateCache state.Database // State database to reuse between imports (contains state cache) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - receiptsCache *lru.Cache // Cache for the most recent receipts per block - blockCache *lru.Cache // Cache for the most recent entire blocks - txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. - futureBlocks *lru.Cache // future blocks are blocks added for later processing + stateCache state.Database // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + receiptsCache *lru.Cache // Cache for the most recent receipts per block + blockCache *lru.Cache // Cache for the most recent entire blocks + txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. + futureBlocks *lru.Cache // future blocks are blocks added for later processing + blockResultCache *lru.Cache // Cache for the most recent block results. wg sync.WaitGroup // quit chan struct{} // shutdown signal, closed in Stop. @@ -226,6 +229,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par blockCache, _ := lru.New(blockCacheLimit) txLookupCache, _ := lru.New(txLookupCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) + blockResultCache, _ := lru.New(blockResultCacheLimit) bc := &BlockChain{ chainConfig: chainConfig, @@ -237,17 +241,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par Journal: cacheConfig.TrieCleanJournal, Preimages: cacheConfig.Preimages, }), - quit: make(chan struct{}), - chainmu: syncx.NewClosableMutex(), - shouldPreserve: shouldPreserve, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - txLookupCache: txLookupCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, + quit: make(chan struct{}), + chainmu: syncx.NewClosableMutex(), + shouldPreserve: shouldPreserve, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + txLookupCache: txLookupCache, + futureBlocks: futureBlocks, + blockResultCache: blockResultCache, + engine: engine, + vmConfig: vmConfig, } bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) @@ -1215,7 +1220,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd) rawdb.WriteBlock(blockBatch, block) rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts) - rawdb.WriteBlockResult(blockBatch, block.Hash(), blockResult) rawdb.WritePreimages(blockBatch, state.Preimages()) if err := blockBatch.Write(); err != nil { log.Crit("Failed to write block into disk", "err", err) @@ -1314,6 +1318,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } bc.futureBlocks.Remove(block.Hash()) + // Fill blockResult content + if blockResult != nil { + bc.writeBlockResult(state, block, blockResult) + bc.blockResultCache.Add(block.Hash(), blockResult) + } + if status == CanonStatTy { bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs, BlockResult: blockResult}) if len(logs) > 0 { @@ -1333,6 +1343,35 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return status, nil } +// Fill blockResult content +func (bc *BlockChain) writeBlockResult(state *state.StateDB, block *types.Block, blockResult *types.BlockResult) { + blockResult.BlockTrace = types.NewTraceBlock(bc.chainConfig, block) + for i, tx := range block.Transactions() { + evmTrace := blockResult.ExecutionResults[i] + // Get the sender's address. + from, _ := types.Sender(types.MakeSigner(bc.chainConfig, block.Number()), tx) + // Get account's 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)) + for i := range proof { + evmTrace.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())) + // Get tx.to address's code hash. + codeHash := state.GetCodeHash(*tx.To()) + evmTrace.CodeHash = &codeHash + } else if tx.To() == nil { // Contract is created. + evmTrace.ByteCode = hexutil.Encode(tx.Data()) + } + } +} + // addFutureBlock checks if the block is within the max allowed window to get // accepted for future processing, and returns an error if the block is too far // ahead and was not added. diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index fb75e75a73b2..a25b9ce425af 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -158,6 +158,13 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { return bc.GetBlock(hash, *number) } +func (bc *BlockChain) GetBlockResultByHash(blockHash common.Hash) *types.BlockResult { + if blockResult, ok := bc.blockResultCache.Get(blockHash); ok { + return blockResult.(*types.BlockResult) + } + return nil +} + // GetBlockByNumber retrieves a block from the database by number, caching it // (associated with its hash) if found. func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { diff --git a/core/evm.go b/core/evm.go index 382032386966..c5d71a316f3c 100644 --- a/core/evm.go +++ b/core/evm.go @@ -68,6 +68,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common func NewEVMTxContext(msg Message) vm.TxContext { return vm.TxContext{ Origin: msg.From(), + To: msg.To(), GasPrice: new(big.Int).Set(msg.GasPrice()), } } diff --git a/core/rawdb/l2trace.go b/core/rawdb/l2trace.go deleted file mode 100644 index ee82d02b4b02..000000000000 --- a/core/rawdb/l2trace.go +++ /dev/null @@ -1,39 +0,0 @@ -package rawdb - -import ( - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/ethdb" - "github.com/scroll-tech/go-ethereum/log" - "github.com/scroll-tech/go-ethereum/rlp" -) - -// ReadBlockResult retrieves all data required by roller. -func ReadBlockResult(db ethdb.Reader, hash common.Hash) *types.BlockResult { - data, _ := db.Get(blockResultKey(hash)) - if len(data) == 0 { - return nil - } - var blockResult types.BlockResult - if err := rlp.DecodeBytes(data, &blockResult); err != nil { - log.Error("Failed to decode BlockResult", "err", err) - return nil - } - return &blockResult -} - -// WriteBlockResult stores blockResult into leveldb. -func WriteBlockResult(db ethdb.KeyValueWriter, hash common.Hash, blockResult *types.BlockResult) { - bytes, err := rlp.EncodeToBytes(blockResult) - if err != nil { - log.Crit("Failed to RLP encode BlockResult", "err", err) - } - db.Put(blockResultKey(hash), bytes) -} - -// DeleteBlockResult removes blockResult with a block hash. -func DeleteBlockResult(db ethdb.KeyValueWriter, hash common.Hash) { - if err := db.Delete(blockResultKey(hash)); err != nil { - log.Crit("Failed to delete BlockResult", "err", err) - } -} diff --git a/core/rawdb/l2trace_test.go b/core/rawdb/l2trace_test.go deleted file mode 100644 index d9ccf91ffaf8..000000000000 --- a/core/rawdb/l2trace_test.go +++ /dev/null @@ -1,355 +0,0 @@ -package rawdb - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "testing" - - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/rlp" -) - -func TestBlockEvmTracesStorage(t *testing.T) { - db := NewMemoryDatabase() - - data1 := []byte(`{ - "gas": 1, - "failed": false, - "returnValue": "", - "structLogs": [ - { - "pc": 0, - "op": "PUSH1", - "gas": 1000000, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 2, - "op": "SLOAD", - "gas": 999997, - "gasCost": 2100, - "depth": 1, - "stack": [ - "0x1" - ], - "memory": [], - "storage": { - "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000000" - } - }, - { - "pc": 3, - "op": "POP", - "gas": 997897, - "gasCost": 2, - "depth": 1, - "stack": [ - "0x0" - ], - "memory": [] - }, - { - "pc": 4, - "op": "PUSH1", - "gas": 997895, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 6, - "op": "PUSH1", - "gas": 997892, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x11" - ], - "memory": [] - }, - { - "pc": 8, - "op": "SSTORE", - "gas": 997889, - "gasCost": 20000, - "depth": 1, - "stack": [ - "0x11", - "0x1" - ], - "memory": [], - "storage": { - "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011" - } - }, - { - "pc": 9, - "op": "PUSH1", - "gas": 977889, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 11, - "op": "PUSH1", - "gas": 977886, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x11" - ], - "memory": [] - }, - { - "pc": 13, - "op": "SSTORE", - "gas": 977883, - "gasCost": 22100, - "depth": 1, - "stack": [ - "0x11", - "0x2" - ], - "memory": [], - "storage": { - "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011", - "0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011" - } - }, - { - "pc": 14, - "op": "PUSH1", - "gas": 955783, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 16, - "op": "PUSH1", - "gas": 955780, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x11" - ], - "memory": [] - }, - { - "pc": 18, - "op": "SSTORE", - "gas": 955777, - "gasCost": 100, - "depth": 1, - "stack": [ - "0x11", - "0x2" - ], - "memory": [], - "storage": { - "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011", - "0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011" - } - }, - { - "pc": 19, - "op": "PUSH1", - "gas": 955677, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 21, - "op": "SLOAD", - "gas": 955674, - "gasCost": 100, - "depth": 1, - "stack": [ - "0x2" - ], - "memory": [], - "storage": { - "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011", - "0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011" - } - }, - { - "pc": 22, - "op": "PUSH1", - "gas": 955574, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x11" - ], - "memory": [] - }, - { - "pc": 24, - "op": "SLOAD", - "gas": 955571, - "gasCost": 100, - "depth": 1, - "stack": [ - "0x11", - "0x1" - ], - "memory": [], - "storage": { - "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011", - "0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011" - } - }, - { - "pc": 25, - "op": "STOP", - "gas": 955471, - "gasCost": 0, - "depth": 1, - "stack": [ - "0x11", - "0x11" - ], - "memory": [] - } - ] -}`) - evmTrace1 := &types.ExecutionResult{ReturnValue: "0xaaa"} - if err := json.Unmarshal(data1, evmTrace1); err != nil { - t.Fatalf(err.Error()) - } - - data2 := []byte(`{ - "gas": 1, - "failed": false, - "returnValue": "000000000000000000000000000000000000000000000000000000000000000a", - "structLogs": [ - { - "pc": 0, - "op": "PUSH1", - "gas": 1000000, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 2, - "op": "PUSH1", - "gas": 999997, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa" - ], - "memory": [] - }, - { - "pc": 4, - "op": "MSTORE", - "gas": 999994, - "gasCost": 6, - "depth": 1, - "stack": [ - "0xa", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000" - ] - }, - { - "pc": 5, - "op": "PUSH1", - "gas": 999988, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [ - "000000000000000000000000000000000000000000000000000000000000000a" - ] - }, - { - "pc": 7, - "op": "PUSH1", - "gas": 999985, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x20" - ], - "memory": [ - "000000000000000000000000000000000000000000000000000000000000000a" - ] - }, - { - "pc": 9, - "op": "RETURN", - "gas": 999982, - "gasCost": 0, - "depth": 1, - "stack": [ - "0x20", - "0x0" - ], - "memory": [ - "000000000000000000000000000000000000000000000000000000000000000a" - ] - } - ] -}`) - evmTrace2 := &types.ExecutionResult{ReturnValue: "0xbbb"} - if err := json.Unmarshal(data2, evmTrace2); err != nil { - t.Fatalf(err.Error()) - } - - evmTraces := []*types.ExecutionResult{evmTrace1, evmTrace2} - hash := common.BytesToHash([]byte{0x03, 0x04}) - // Insert the blockResult into the database and check presence. - WriteBlockResult(db, hash, &types.BlockResult{ExecutionResults: evmTraces}) - // Read blockResult from db. - if blockResult := ReadBlockResult(db, hash); len(blockResult.ExecutionResults) == 0 { - t.Fatalf("No evmTraces returned") - } else { - if err := checkEvmTracesRLP(blockResult.ExecutionResults, evmTraces); err != nil { - t.Fatalf(err.Error()) - } - } - // Delete blockResult by blockHash. - DeleteBlockResult(db, hash) - if blockResult := ReadBlockResult(db, hash); blockResult != nil && len(blockResult.ExecutionResults) != 0 { - t.Fatalf("The evmTrace list should be empty.") - } -} - -func checkEvmTracesRLP(have, want []*types.ExecutionResult) error { - if len(have) != len(want) { - return fmt.Errorf("evmTraces sizes mismatch: have: %d, want: %d", len(have), len(want)) - } - for i := 0; i < len(want); i++ { - rlpHave, err := rlp.EncodeToBytes(have[i]) - if err != nil { - return err - } - rlpWant, err := rlp.EncodeToBytes(want[i]) - if err != nil { - return err - } - if !bytes.Equal(rlpHave, rlpWant) { - return fmt.Errorf("evmTrace #%d: evmTrace mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant)) - } - } - return nil -} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index eb9e2808ffb0..73dc69ea3122 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -89,7 +89,6 @@ var ( SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value CodePrefix = []byte("c") // CodePrefix + code hash -> account code - blockResultPrefix = []byte("T") // blockResultPrefix + hash -> blockResult PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db @@ -231,8 +230,3 @@ func IsCodeKey(key []byte) (bool, []byte) { func configKey(hash common.Hash) []byte { return append(configPrefix, hash.Bytes()...) } - -// blockResultKey = blockResultPrefix + hash -func blockResultKey(hash common.Hash) []byte { - return append(blockResultPrefix, hash.Bytes()...) -} diff --git a/core/types/l2trace.go b/core/types/l2trace.go index 4537a48cac03..4569180f94fa 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -1,165 +1,80 @@ package types import ( - "io" - "sort" - "strings" - - "github.com/scroll-tech/go-ethereum/rlp" + "github.com/scroll-tech/go-ethereum/common" ) // BlockResult contains block execution traces and results required for rollers. type BlockResult struct { + BlockTrace *BlockTrace `json:"blockTrace"` ExecutionResults []*ExecutionResult `json:"executionResults"` } -type rlpBlockResult struct { - ExecutionResults []*ExecutionResult -} - -func (b *BlockResult) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &rlpBlockResult{ - ExecutionResults: b.ExecutionResults, - }) -} - -func (b *BlockResult) DecodeRLP(s *rlp.Stream) error { - var dec rlpBlockResult - err := s.Decode(&dec) - if err == nil { - b.ExecutionResults = dec.ExecutionResults - } - return err -} - // ExecutionResult groups all structured logs emitted by the EVM // while replaying a transaction in debug mode as well as transaction // execution status, the amount of gas used and the return value type ExecutionResult struct { - Gas uint64 `json:"gas"` - Failed bool `json:"failed"` - ReturnValue string `json:"returnValue,omitempty"` - StructLogs []StructLogRes `json:"structLogs"` -} - -type rlpExecutionResult struct { - Gas uint64 - Failed bool - ReturnValue string - StructLogs []StructLogRes -} - -func (e *ExecutionResult) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, rlpExecutionResult{ - Gas: e.Gas, - Failed: e.Failed, - ReturnValue: e.ReturnValue, - StructLogs: e.StructLogs, - }) -} - -func (e *ExecutionResult) DecodeRLP(s *rlp.Stream) error { - var dec rlpExecutionResult - err := s.Decode(&dec) - if err == nil { - e.Gas, e.Failed, e.ReturnValue, e.StructLogs = dec.Gas, dec.Failed, dec.ReturnValue, dec.StructLogs - } - return err + Gas uint64 `json:"gas"` + Failed bool `json:"failed"` + ReturnValue string `json:"returnValue,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"` + StructLogs []StructLogRes `json:"structLogs"` } // StructLogRes stores a structured log emitted by the EVM while replaying a // transaction in debug mode type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error string `json:"error,omitempty"` - Stack *[]string `json:"stack,omitempty"` - Memory *[]string `json:"memory,omitempty"` - Storage *map[string]string `json:"storage,omitempty"` + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error string `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` + ExtraData *ExtraData `json:"extraData,omitempty"` } -type rlpStructLogRes struct { - Pc uint64 - Op string - Gas uint64 - GasCost uint64 - Depth uint - Error string - Stack []string - Memory []string - Storage []string +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_hash, stack.nth_last(1) address’s code_hash] + CodeHashList []common.Hash `json:"codeHashList,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] + // CALL | CALLCODE: [caller contract address’s accountProof, stack.nth_last(1) address’s accountProof] + ProofList [][]string `json:"proofList,omitempty"` } -// EncodeRLP implements rlp.Encoder. -func (r *StructLogRes) EncodeRLP(w io.Writer) error { - data := rlpStructLogRes{ - Pc: r.Pc, - Op: r.Op, - Gas: r.Gas, - GasCost: r.GasCost, - Depth: uint(r.Depth), - Error: r.Error, +// NewExtraData create, init and return ExtraData +func NewExtraData() *ExtraData { + return &ExtraData{ + CodeHashList: make([]common.Hash, 0), + ProofList: make([][]string, 0), } - if r.Stack != nil { - data.Stack = make([]string, len(*r.Stack)) - for i, val := range *r.Stack { - data.Stack[i] = val - } - } - if r.Memory != nil { - data.Memory = make([]string, len(*r.Memory)) - for i, val := range *r.Memory { - data.Memory[i] = val - } - } - if r.Storage != nil { - keys := make([]string, 0, len(*r.Storage)) - for key := range *r.Storage { - keys = append(keys, key) - } - sort.Slice(keys, func(i, j int) bool { - return strings.Compare(keys[i], keys[j]) >= 0 - }) - data.Storage = make([]string, 0, len(*r.Storage)*2) - for _, key := range keys { - data.Storage = append(data.Storage, []string{key, (*r.Storage)[key]}...) - } - } - return rlp.Encode(w, data) } -// DecodeRLP implements rlp.Decoder. -func (r *StructLogRes) DecodeRLP(s *rlp.Stream) error { - var dec rlpStructLogRes - err := s.Decode(&dec) - if err != nil { - return err - } - r.Pc, r.Op, r.Gas, r.GasCost, r.Depth, r.Error = dec.Pc, dec.Op, dec.Gas, dec.GasCost, int(dec.Depth), dec.Error - if len(dec.Stack) != 0 { - stack := make([]string, len(dec.Stack)) - for i, val := range dec.Stack { - stack[i] = val - } - r.Stack = &stack +// SealExtraData doesn't show empty fields. +func (e *ExtraData) SealExtraData() *ExtraData { + if len(e.CodeHashList) == 0 { + e.CodeHashList = nil } - if len(dec.Memory) != 0 { - memory := make([]string, len(dec.Memory)) - for i, val := range dec.Memory { - memory[i] = val - } - r.Memory = &memory + if len(e.ProofList) == 0 { + e.ProofList = nil } - if len(dec.Storage) != 0 { - storage := make(map[string]string, len(dec.Storage)*2) - for i := 0; i < len(dec.Storage); i += 2 { - key, val := dec.Storage[i], dec.Storage[i+1] - storage[key] = val - } - r.Storage = &storage + if e.From == nil && e.Nonce == nil && e.CodeHashList == nil && e.ProofList == nil { + return nil } - return nil + return e } diff --git a/core/types/l2trace_block.go b/core/types/l2trace_block.go new file mode 100644 index 000000000000..e1bde0ffbaca --- /dev/null +++ b/core/types/l2trace_block.go @@ -0,0 +1,78 @@ +package types + +import ( + "math/big" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" + "github.com/scroll-tech/go-ethereum/params" +) + +type BlockTrace struct { + Number *big.Int `json:"number"` + Hash common.Hash `json:"hash"` + GasLimit uint64 `json:"gasLimit"` + Difficulty *big.Int `json:"difficulty"` + BaseFee *big.Int `json:"baseFee"` + Coinbase common.Address `json:"coinbase"` + Time uint64 `json:"time"` + Transaction []*TransactionTrace `json:"transaction"` +} + +type TransactionTrace struct { + Type uint8 `json:"type"` + Nonce uint64 `json:"nonce"` + Gas uint64 `json:"gas"` + GasPrice *big.Int `json:"gasPrice"` + From common.Address `json:"from"` + To *common.Address `json:"to"` + ChainId *big.Int `json:"chainId"` + Value *big.Int `json:"value"` + Data string `json:"data"` + IsCreate bool `json:"isCreate"` + V *big.Int `json:"v"` + R *big.Int `json:"r"` + S *big.Int `json:"s"` +} + +// NewTraceBlock supports necessary fields for roller. +func NewTraceBlock(config *params.ChainConfig, block *Block) *BlockTrace { + txs := make([]*TransactionTrace, block.Transactions().Len()) + for i, tx := range block.Transactions() { + txs[i] = newTraceTransaction(tx, block.NumberU64(), config) + } + return &BlockTrace{ + Number: block.Number(), + Hash: block.Hash(), + GasLimit: block.GasLimit(), + Difficulty: block.Difficulty(), + BaseFee: block.BaseFee(), + Coinbase: block.Coinbase(), + Time: block.Time(), + Transaction: txs, + } +} + +// newTraceTransaction returns a transaction that will serialize to the trace +// representation, with the given location metadata set (if available). +func newTraceTransaction(tx *Transaction, blockNumber uint64, config *params.ChainConfig) *TransactionTrace { + signer := MakeSigner(config, big.NewInt(0).SetUint64(blockNumber)) + from, _ := Sender(signer, tx) + v, r, s := tx.RawSignatureValues() + result := &TransactionTrace{ + Type: tx.Type(), + Nonce: tx.Nonce(), + ChainId: tx.ChainId(), + From: from, + Gas: tx.Gas(), + GasPrice: tx.GasPrice(), + To: tx.To(), + Value: tx.Value(), + Data: hexutil.Encode(tx.Data()), + IsCreate: tx.To() == nil, + V: v, + R: r, + S: s, + } + return result +} diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go index 84a8c7f03791..edae720f0559 100644 --- a/core/vm/access_list_tracer.go +++ b/core/vm/access_list_tracer.go @@ -161,6 +161,10 @@ func (a *AccessListTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64, } } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (*AccessListTracer) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +} + func (*AccessListTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { } diff --git a/core/vm/evm.go b/core/vm/evm.go index 12e6847f56a8..6ae64cefd656 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -81,8 +81,9 @@ type BlockContext struct { // All fields can change between transactions. type TxContext struct { // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE + Origin common.Address // Provides information for ORIGIN + To *common.Address // Provides information for trace + GasPrice *big.Int // Provides information for GASPRICE } // EVM is the Ethereum Virtual Machine base object and provides diff --git a/core/vm/interface.go b/core/vm/interface.go index e028eabdae7e..c7259e4fb5fe 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -47,6 +47,10 @@ type StateDB interface { GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) + GetProof(addr common.Address) ([][]byte, error) + GetProofByHash(addrHash common.Hash) ([][]byte, error) + GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) + Suicide(common.Address) bool HasSuicided(common.Address) bool diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 8562c5e7d456..f1512563fc1b 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -265,6 +265,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( in.returnData = res } + if in.cfg.Debug { + in.cfg.Tracer.CaptureStateAfter(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + } + switch { case err != nil: return nil, err diff --git a/core/vm/logger.go b/core/vm/logger.go index 196cf0aefd6a..7570c13b19f8 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -29,6 +29,7 @@ import ( "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/math" "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/params" ) @@ -72,6 +73,7 @@ type StructLog struct { Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` + ExtraData *types.ExtraData `json:"extraData"` Err error `json:"-"` } @@ -106,6 +108,7 @@ func (s *StructLog) ErrorString() string { type EVMLogger interface { CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) + CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) CaptureExit(output []byte, gasUsed uint64, err error) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) @@ -177,7 +180,10 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop } } // Copy a snapshot of the current storage to a new container - var storage Storage + var ( + storage Storage + extraData *types.ExtraData + ) if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) { // initialise new changed values storage container for this contract // if not present. @@ -200,6 +206,11 @@ 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) + } } } var rdata []byte @@ -207,8 +218,32 @@ 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(), err} + 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 + } + + 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) + } + } + + 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) } @@ -345,6 +380,10 @@ func (t *mdLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *S } } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (t *mdLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +} + func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } @@ -371,27 +410,31 @@ func FormatLogs(logs []StructLog) []types.StructLogRes { Depth: trace.Depth, Error: trace.ErrorString(), } - if trace.Stack != nil { + if len(trace.Stack) != 0 { stack := make([]string, len(trace.Stack)) for i, stackValue := range trace.Stack { stack[i] = stackValue.Hex() } formatted[index].Stack = &stack } - if trace.Memory != nil { + if len(trace.Memory) != 0 { memory := make([]string, 0, (len(trace.Memory)+31)/32) for i := 0; i+32 <= len(trace.Memory); i += 32 { - memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + bytes := new(big.Int).SetBytes(trace.Memory[i : i+32]).Bytes() + memory = append(memory, hexutil.Encode(bytes)) } formatted[index].Memory = &memory } - if trace.Storage != nil { + if len(trace.Storage) != 0 { storage := make(map[string]string) for i, storageValue := range trace.Storage { storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) } formatted[index].Storage = &storage } + if trace.ExtraData != nil { + formatted[index].ExtraData = trace.ExtraData.SealExtraData() + } } return formatted } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 1537880e2f8f..881076486b0e 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -75,6 +75,10 @@ func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope l.encoder.Encode(log) } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (l *JSONLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +} + // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { type endLog struct { diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index f9981560d8d3..6bc37ef50202 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -21,7 +21,9 @@ import ( "testing" "github.com/holiman/uint256" + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/params" ) @@ -42,6 +44,14 @@ func (d *dummyContractRef) SetBalance(*big.Int) {} func (d *dummyContractRef) SetNonce(uint64) {} func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } +// makeTestState create a sample test state to test node-wise reconstruction. +func makeTestState() *state.StateDB { + // Create an empty state + db := state.NewDatabase(rawdb.NewMemoryDatabase()) + stateDb, _ := state.New(common.Hash{}, db, nil) + return stateDb +} + type dummyStatedb struct { state.StateDB } @@ -50,7 +60,7 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 } func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, makeTestState(), params.TestChainConfig, Config{}) logger = NewStructLogger(nil) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) scope = &ScopeContext{ diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go new file mode 100644 index 000000000000..9984d9d7d30a --- /dev/null +++ b/core/vm/logger_trace.go @@ -0,0 +1,146 @@ +package vm + +import ( + "errors" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" + "github.com/scroll-tech/go-ethereum/core/types" +) + +type traceFunc func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error + +var ( + // OpcodeExecs the map to load opcodes' trace funcs. + OpcodeExecs = map[OpCode][]traceFunc{ + CALL: {traceToAddressCodeHash, traceLastNAddressCodeHash(1), traceCallerProof, traceLastNAddressProof(1)}, + CALLCODE: {traceToAddressCodeHash, traceLastNAddressCodeHash(1), traceCallerProof, traceLastNAddressProof(1)}, + DELEGATECALL: {traceToAddressCodeHash, traceLastNAddressCodeHash(1)}, + STATICCALL: {traceToAddressCodeHash, traceLastNAddressCodeHash(1)}, + CREATE: {traceSenderAddress, traceCreatedContractProof, traceNonce}, + CREATE2: {traceSenderAddress, traceCreatedContractProof}, + SSTORE: {traceStorageProof}, + SLOAD: {traceStorageProof}, + SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)}, + SELFBALANCE: {traceContractProof}, + BALANCE: {traceLastNAddressProof(0)}, + EXTCODEHASH: {traceLastNAddressProof(0)}, + } +) + +// traceToAddressCodeHash gets tx.to address’s code_hash +func traceToAddressCodeHash(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { + if l.env.To == nil { + return nil + } + codeHash := l.env.StateDB.GetCodeHash(*l.env.To) + extraData.CodeHashList = append(extraData.CodeHashList, codeHash) + return nil +} + +// traceLastNAddressCodeHash +func traceLastNAddressCodeHash(n int) traceFunc { + return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { + stack := scope.Stack + if stack.len() <= n { + return nil + } + address := common.Address(stack.data[stack.len()-1-n].Bytes20()) + codeHash := l.env.StateDB.GetCodeHash(address) + extraData.CodeHashList = append(extraData.CodeHashList, codeHash) + return nil + } +} + +// 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) + if err == nil { + extraData.ProofList = append(extraData.ProofList, encodeProof(storageProof)) + } + return err +} + +// 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()) + if err == nil { + extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + } + return err +} + +/// traceCreatedContractProof get created contract address’s accountProof +func traceCreatedContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { + stack := scope.Stack + if stack.len() < 1 { + return nil + } + stackvalue := stack.peek() + if stackvalue.IsZero() { + return errors.New("can't get created contract address from stack") + } + address := common.BytesToAddress(stackvalue.Bytes()) + proof, err := l.env.StateDB.GetProof(address) + if err == nil { + extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + } + return err +} + +// traceLastNAddressProof returns func about the last N's address proof. +func traceLastNAddressProof(n int) traceFunc { + return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error { + stack := scope.Stack + if stack.len() <= n { + return nil + } + + address := common.Address(stack.data[stack.len()-1-n].Bytes20()) + proof, err := l.env.StateDB.GetProof(address) + if err == nil { + extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + } + return err + } +} + +// 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) + if err == nil { + extraData.ProofList = append(extraData.ProofList, encodeProof(proof)) + } + return err +} + +func encodeProof(proof [][]byte) (res []string) { + if len(proof) == 0 { + return nil + } + for _, node := range proof { + res = append(res, hexutil.Encode(node)) + } + return +} diff --git a/eth/api.go b/eth/api.go index 41dcb0683be4..5dfedf943a7e 100644 --- a/eth/api.go +++ b/eth/api.go @@ -618,7 +618,10 @@ func NewPublicTraceAPI(eth *Ethereum) *PublicTraceAPI { return &PublicTraceAPI{eth} } -// BlockResultByHash returns the blockResult by blockHash. -func (api *PublicTraceAPI) BlockResultByHash(blockHash common.Hash) (*types.BlockResult, error) { - return rawdb.ReadBlockResult(api.e.chainDb, blockHash), nil +// GetBlockResultByHash returns the blockResult by blockHash. +func (api *PublicTraceAPI) GetBlockResultByHash(blockHash common.Hash) (*types.BlockResult, error) { + if blockResult := api.e.blockchain.GetBlockResultByHash(blockHash); blockResult != nil { + return blockResult, nil + } + return nil, fmt.Errorf("No block result found") } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index c9ca0a6e110f..8099206ec5c9 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -371,8 +371,10 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) for _, f := range filters[BlocksSubscription] { f.headers <- ev.Block.Header() } - for _, f := range filters[BlockResultsSubscription] { - f.blockResults <- ev.BlockResult + if ev.BlockResult != nil { + for _, f := range filters[BlockResultsSubscription] { + f.blockResults <- ev.BlockResult + } } if es.lightMode && len(filters[LogsSubscription]) > 0 { es.lightFilterNewHead(ev.Block.Header(), func(header *types.Header, remove bool) { diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go index 6da0b02280ff..c98e6b66008a 100644 --- a/eth/tracers/js/tracer.go +++ b/eth/tracers/js/tracer.go @@ -746,6 +746,10 @@ func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco } } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (jst *jsTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + // CaptureFault implements the Tracer interface to trace an execution fault func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { if jst.err != nil { diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index f332102e20b7..8f94bb975134 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -96,6 +96,10 @@ func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to commo func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (t *fourByteTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index abb391e07764..44be8c5690db 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -96,6 +96,10 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (t *callTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + // CaptureFault implements the EVMLogger interface to trace an execution fault. func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { } diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go index 381e29efd4f3..d08944fa6d78 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/native/noop.go @@ -51,6 +51,10 @@ func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, func (t *noopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } +// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. +func (t *noopTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + // CaptureFault implements the EVMLogger interface to trace an execution fault. func (t *noopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 040962dcc442..1c93412ff5e5 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -325,10 +325,10 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) return ec.c.EthSubscribe(ctx, ch, "newHeads") } -// BlockResultByHash returns the blockResult. -func (ec *Client) BlockResultByHash(ctx context.Context, blockHash common.Hash) (*types.BlockResult, error) { +// GetBlockResultByHash returns the blockResult. +func (ec *Client) GetBlockResultByHash(ctx context.Context, blockHash common.Hash) (*types.BlockResult, error) { var blockResult types.BlockResult - if err := ec.c.CallContext(ctx, &blockResult, "eth_blockResultByHash", blockHash); err != nil { + if err := ec.c.CallContext(ctx, &blockResult, "eth_getBlockResultByHash", blockHash); err != nil { return nil, err } return &blockResult, nil diff --git a/miner/worker.go b/miner/worker.go index 855a8ba898bb..35ce903670db 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -794,7 +794,7 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres w.current.receipts = append(w.current.receipts, receipt) w.current.executionResults = append(w.current.executionResults, &types.ExecutionResult{ Gas: receipt.GasUsed, - Failed: receipt.Status == types.ReceiptStatusSuccessful, + Failed: receipt.Status != types.ReceiptStatusSuccessful, ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue), StructLogs: vm.FormatLogs(tracer.StructLogs()), }) diff --git a/miner/worker_test.go b/miner/worker_test.go index 49324cc92edc..8b1a49006a9c 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -236,7 +236,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) { b.genesis.MustCommit(db2) chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{ Debug: true, - Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true})}, nil, nil) + Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil) defer chain.Stop() // Ignore empty commit here for less noise.