diff --git a/jsonrpc/eth_endpoint.go b/jsonrpc/eth_endpoint.go index d08f54ee5d..21a0afcab5 100644 --- a/jsonrpc/eth_endpoint.go +++ b/jsonrpc/eth_endpoint.go @@ -98,7 +98,8 @@ type Eth struct { var ( ErrInsufficientFunds = errors.New("insufficient funds for execution") - EmptyCodeHash = hex.EncodeToHex([]byte{197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, + //Empty code hash is 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + EmptyCodeHash = hex.EncodeToHex([]byte{197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}) ) @@ -732,6 +733,14 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { return nil, err } + // Get the previous block header + var previousBlockNumber = BlockNumber(header.Number) - 1 + + previousHeader, err := GetHeaderFromBlockNumberOrHash(BlockNumberOrHash{BlockNumber: &previousBlockNumber}, e.store) + if err != nil { + return nil, err + } + fullBlock, valid := e.store.GetBlockByHash(header.Hash, true) if !valid { return nil, fmt.Errorf("failed to get block by hash %s", header.Hash) @@ -743,7 +752,7 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { return nil, err } - // Trace the block + // Configure the tracer tracer, _, err := NewTracer(&TraceConfig{ EnableMemory: true, EnableReturnData: true, @@ -774,8 +783,9 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { for _, accountStr := range accountsStr { accountAddress := types.StringToAddress(accountStr) - // Get the full account nonce, balance, state root and code hash - acc, err := e.store.GetAccount(header.StateRoot, accountAddress) + // Get the full account nonce, balance, state root and code hash of the state before this + // block is executed + acc, err := e.store.GetAccount(previousHeader.StateRoot, accountAddress) if err != nil { return nil, err } @@ -788,21 +798,23 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { } } - // Get storage data for all accounts from this block + // Learn of the storage changes in this block storages := make([]prover.Storage, 0) - storageChanges, err := prover.ParseTraceForStorageChanges(tracesJSON) + storageChanges, err := prover.ParseTraceForStorageAccess(tracesJSON) if err != nil { return nil, err } + // Get the starting state storage merkle proof for each account and each slot + // that will be changed in this block execution for account, accountData := range accounts { if accountData.CodeHash != EmptyCodeHash { - storageUpdates := make([]prover.StorageUpdate, 0) + storageAccesses := make([]prover.StorageAccess, 0) - // Account has code + // Account is smart contract (has code) for _, storageChange := range storageChanges[account] { - storageMerkleProof, err := e.store.GetStorageProof(header.StateRoot, + storageMerkleProof, err := e.store.GetStorageProof(previousHeader.StateRoot, types.StringToAddress(account), storageChange.Slot) if err != nil { return nil, err @@ -813,8 +825,7 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { ss[i] = hex.EncodeToHex(s) } - storageUpdates = append(storageUpdates, prover.StorageUpdate{ - Value: storageChange.Value.String(), + storageAccesses = append(storageAccesses, prover.StorageAccess{ Slot: storageChange.Slot.String(), MerkleProof: ss, }) @@ -823,13 +834,17 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { storages = append(storages, prover.Storage{ Account: account, StorageRoot: accountData.Root, - Storage: storageUpdates, + Storage: storageAccesses, }) } } - // All transactions in the block - transactions := fullBlock.Transactions + // All transactions in this block + + transactions := make([]string, 0) + for _, transaction := range fullBlock.Transactions { + transactions = append(transactions, hex.EncodeToHex(transaction.MarshalRLP())) + } // Receipts from this block receipts, err := e.store.GetReceiptsByHash(header.Hash) @@ -842,13 +857,16 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { for account, accountData := range accounts { if accountData.CodeHash != EmptyCodeHash { - contractCode, err := e.store.GetCode(header.StateRoot, types.StringToAddress(account)) + contractCode, err := e.store.GetCode(previousHeader.StateRoot, types.StringToAddress(account)) if err != nil { return nil, err } codeHash := crypto.Keccak256(contractCode) contractCodes[hex.EncodeToHex(codeHash)] = hex.EncodeToHex(contractCode) + } else { + // Add empty code hash + contractCodes[EmptyCodeHash] = "0x" } } @@ -856,7 +874,7 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { // Get state Merkle proofs for all accounts for account := range accounts { - accountProof, err := e.store.GetAccountProof(header.StateRoot, types.StringToAddress(account)) + accountProof, err := e.store.GetAccountProof(previousHeader.StateRoot, types.StringToAddress(account)) if err != nil { return nil, err } @@ -872,13 +890,38 @@ func (e *Eth) GetProverData(block BlockNumberOrHash) (interface{}, error) { }) } + // Add zero account (block beneficiary) to the state + zeroAccount := "0x0000000000000000000000000000000000000000" + + zeroAccountProof, err := e.store.GetAccountProof(previousHeader.StateRoot, types.StringToAddress(zeroAccount)) + if err != nil { + return nil, err + } + + zeroAccountProofArray := make([]string, 0) + for _, proof := range zeroAccountProof { + zeroAccountProofArray = append(zeroAccountProofArray, hex.EncodeToHex(proof)) + } + + state = append(state, prover.ProverAccountProof{ + Account: zeroAccount, + MerkleProof: zeroAccountProofArray, + }) + + chainID, err := e.ChainId() + if err != nil { + return nil, err + } + return &prover.ProverData{ - BlockHeader: *header, - Accounts: accounts, - Storage: storages, - Transactions: transactions, - Receipts: receipts, - ContractCodes: contractCodes, - State: state, + ChainID: chainID, + BlockHeader: *header, + PreviousBlockHeader: *previousHeader, + Accounts: accounts, + PreviousStorage: storages, + Transactions: transactions, + Receipts: receipts, + ContractCodes: contractCodes, + PreviousState: state, }, nil } diff --git a/prover/prover.go b/prover/prover.go index 0aac644ec1..ad42f5b53e 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -9,16 +9,15 @@ import ( "github.com/0xPolygon/polygon-edge/types" ) -type StorageUpdate struct { +type StorageAccess struct { Slot string - Value string MerkleProof []string } type Storage struct { Account string StorageRoot string - Storage []StorageUpdate + Storage []StorageAccess } type ProverAccount struct { @@ -34,13 +33,15 @@ type ProverAccountProof struct { } type ProverData struct { - BlockHeader types.Header - Accounts interface{} - Storage interface{} - Transactions interface{} - Receipts interface{} - ContractCodes interface{} - State interface{} + ChainID interface{} + BlockHeader types.Header + PreviousBlockHeader types.Header + Accounts interface{} + PreviousStorage interface{} + Transactions interface{} + Receipts interface{} + ContractCodes interface{} + PreviousState interface{} } func ParseBlockAccounts(block *types.Block) ([]string, error) { @@ -77,8 +78,8 @@ func ParseContractCodeForAccounts(tracesJSON []interface{}) ([]string, error) { return result, nil } -func ParseTraceForStorageChanges(tracesJSON []interface{}) (map[string][]structtracer.StorageUpdate, error) { - var storageChanges = make(map[string][]structtracer.StorageUpdate) +func ParseTraceForStorageAccess(tracesJSON []interface{}) (map[string][]structtracer.StorageAccess, error) { + var storageChanges = make(map[string][]structtracer.StorageAccess) for _, traceJSON := range tracesJSON { trace, ok := traceJSON.(*structtracer.StructTraceResult) @@ -87,7 +88,9 @@ func ParseTraceForStorageChanges(tracesJSON []interface{}) (map[string][]structt } for account, storage := range trace.StorageUpdates { - storageChanges[account.String()] = append(storageChanges[account.String()], storage...) + for storageAccess := range storage { + storageChanges[account.String()] = append(storageChanges[account.String()], storageAccess) + } } } diff --git a/state/runtime/tracer/structtracer/tracer.go b/state/runtime/tracer/structtracer/tracer.go index 0ed9f68b90..6b7e41208a 100644 --- a/state/runtime/tracer/structtracer/tracer.go +++ b/state/runtime/tracer/structtracer/tracer.go @@ -43,9 +43,8 @@ func (l *StructLog) ErrorString() string { return "" } -type StorageUpdate struct { - Slot types.Hash `json:"slot"` - Value types.Hash `json:"value"` +type StorageAccess struct { + Slot types.Hash `json:"slot"` } type StructTracer struct { @@ -65,13 +64,15 @@ type StructTracer struct { storage []map[types.Address]map[types.Hash]types.Hash currentMemory []([]byte) currentStack []([]*big.Int) - storageUpdates [][]StorageUpdate - accountStorageUpdates map[types.Address][]StorageUpdate + storageAccess []map[StorageAccess]bool + accountStorageUpdates map[types.Address]map[StorageAccess]bool } func NewStructTracer(config Config) *StructTracer { storage := make([](map[types.Address]map[types.Hash]types.Hash), 1) storage[0] = make(map[types.Address]map[types.Hash]types.Hash) + storageAccess := make([]map[StorageAccess]bool, 1) + storageAccess[0] = make(map[StorageAccess]bool) return &StructTracer{ Config: config, @@ -79,8 +80,8 @@ func NewStructTracer(config Config) *StructTracer { storage: storage, currentMemory: make([]([]byte), 1), currentStack: make([]([]*big.Int), 1), - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + storageAccess: storageAccess, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), } } @@ -224,7 +225,7 @@ func (t *StructTracer) captureStorage( ) { if opCode == evm.CALL || opCode == evm.STATICCALL { t.storage = append(t.storage, make(map[types.Address]map[types.Hash]types.Hash)) - t.storageUpdates = append(t.storageUpdates, make([]StorageUpdate, 0)) + t.storageAccess = append(t.storageAccess, make(map[StorageAccess]bool)) } if !t.Config.EnableStorage || (opCode != evm.SLOAD && opCode != evm.SSTORE) { @@ -247,6 +248,10 @@ func (t *StructTracer) captureStorage( slot := types.BytesToHash(stack[sp-1].Bytes()) value := host.GetStorage(contractAddress, slot) + t.storageAccess[len(t.storageAccess)-1][StorageAccess{ + Slot: slot, + }] = true + (*storage)[contractAddress][slot] = value case evm.SSTORE: @@ -263,10 +268,9 @@ func (t *StructTracer) captureStorage( (*storage)[contractAddress][slot] = value - t.storageUpdates[len(t.storageUpdates)-1] = append(t.storageUpdates[len(t.storageUpdates)-1], StorageUpdate{ - Slot: slot, - Value: value, - }) + t.storageAccess[len(t.storageAccess)-1][StorageAccess{ + Slot: slot, + }] = true } } @@ -320,15 +324,10 @@ func (t *StructTracer) ExecuteState( if opCode == evm.OpCode(evm.CALL).String() || opCode == evm.OpCode(evm.STATICCALL).String() { contract := types.BytesToAddress(stack[len(stack)-2].Bytes()) - if t.accountStorageUpdates[contract] != nil { - t.accountStorageUpdates[contract] = append(t.accountStorageUpdates[contract], - t.storageUpdates[len(t.storageUpdates)-1]...) - } else { - t.accountStorageUpdates[contract] = t.storageUpdates[len(t.storageUpdates)-1] - } + t.accountStorageUpdates[contract] = t.storageAccess[len(t.storageAccess)-1] t.storage = t.storage[:len(t.storage)-1] - t.storageUpdates = t.storageUpdates[:len(t.storageUpdates)-1] + t.storageAccess = t.storageAccess[:len(t.storageAccess)-1] } contractStorage, ok := t.storage[len(t.storage)-1][contractAddress] @@ -364,12 +363,12 @@ func (t *StructTracer) ExecuteState( } type StructTraceResult struct { - Account string `json:"account"` - Failed bool `json:"failed"` - StorageUpdates map[types.Address][]StorageUpdate `json:"storageUpdates"` - Gas uint64 `json:"gas"` - ReturnValue string `json:"returnValue"` - StructLogs []StructLogRes `json:"structLogs"` + Account string `json:"account"` + Failed bool `json:"failed"` + StorageUpdates map[types.Address]map[StorageAccess]bool `json:"storageUpdates"` + Gas uint64 `json:"gas"` + ReturnValue string `json:"returnValue"` + StructLogs []StructLogRes `json:"structLogs"` } type StructLogRes struct { @@ -398,7 +397,7 @@ func (t *StructTracer) GetResult() (interface{}, error) { returnValue = fmt.Sprintf("%x", t.output) } - t.accountStorageUpdates[t.contractAddress] = t.storageUpdates[len(t.storageUpdates)-1] + t.accountStorageUpdates[t.contractAddress] = t.storageAccess[len(t.storageAccess)-1] storageUpdates := t.accountStorageUpdates return &StructTraceResult{ diff --git a/state/runtime/tracer/structtracer/tracer_test.go b/state/runtime/tracer/structtracer/tracer_test.go index 5c1f82fb38..2c31053f9d 100644 --- a/state/runtime/tracer/structtracer/tracer_test.go +++ b/state/runtime/tracer/structtracer/tracer_test.go @@ -191,11 +191,13 @@ func TestStructTracerTxStart(t *testing.T) { storage: []map[types.Address]map[types.Hash]types.Hash{ make(map[types.Address]map[types.Hash]types.Hash), }, - gasLimit: gasLimit, - currentMemory: make([]([]byte), 1), - currentStack: make([]([]*big.Int), 1), - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + gasLimit: gasLimit, + currentMemory: make([]([]byte), 1), + currentStack: make([]([]*big.Int), 1), + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, tracer, ) @@ -221,12 +223,14 @@ func TestStructTracerTxEnd(t *testing.T) { storage: []map[types.Address]map[types.Hash]types.Hash{ make(map[types.Address]map[types.Hash]types.Hash), }, - gasLimit: gasLimit, - consumedGas: gasLimit - gasLeft, - currentMemory: make([]([]byte), 1), - currentStack: make([]([]*big.Int), 1), - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + gasLimit: gasLimit, + consumedGas: gasLimit - gasLeft, + currentMemory: make([]([]byte), 1), + currentStack: make([]([]*big.Int), 1), + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, tracer, ) @@ -280,12 +284,14 @@ func TestStructTracerCallEnd(t *testing.T) { storage: []map[types.Address]map[types.Hash]types.Hash{ make(map[types.Address]map[types.Hash]types.Hash), }, - output: output, - err: err, - currentMemory: make([]([]byte), 1), - currentStack: make([]([]*big.Int), 1), - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + output: output, + err: err, + currentMemory: make([]([]byte), 1), + currentStack: make([]([]*big.Int), 1), + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, }, { @@ -298,10 +304,12 @@ func TestStructTracerCallEnd(t *testing.T) { storage: []map[types.Address]map[types.Hash]types.Hash{ make(map[types.Address]map[types.Hash]types.Hash), }, - currentMemory: make([]([]byte), 1), - currentStack: make([]([]*big.Int), 1), - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + currentMemory: make([]([]byte), 1), + currentStack: make([]([]*big.Int), 1), + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, }, } @@ -416,6 +424,9 @@ func TestStructTracerCaptureState(t *testing.T) { storage: []map[types.Address]map[types.Hash]types.Hash{ make(map[types.Address]map[types.Hash]types.Hash), }, + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, }, memory: memory, stack: stack, @@ -444,6 +455,11 @@ func TestStructTracerCaptureState(t *testing.T) { }, }, }, + storageAccess: []map[StorageAccess]bool{ + { + {types.BytesToHash(big.NewInt(2).Bytes())}: true, + }, + }, }, expectedVMState: &mockState{}, }, @@ -456,8 +472,10 @@ func TestStructTracerCaptureState(t *testing.T) { storage: []map[types.Address]map[types.Hash]types.Hash{ make(map[types.Address]map[types.Hash]types.Hash), }, - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, memory: memory, stack: stack, @@ -479,12 +497,12 @@ func TestStructTracerCaptureState(t *testing.T) { }, }, }, - storageUpdates: [][]StorageUpdate{ + storageAccess: []map[StorageAccess]bool{ { - StorageUpdate{types.BytesToHash(big.NewInt(2).Bytes()), types.BytesToHash(big.NewInt(1).Bytes())}, + {types.BytesToHash(big.NewInt(2).Bytes())}: true, }, }, - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, expectedVMState: &mockState{}, }, @@ -932,20 +950,22 @@ func TestStructTracerGetResult(t *testing.T) { { name: "should return result", tracer: &StructTracer{ - Config: testEmptyConfig, - logs: logs, - consumedGas: consumedGas, - output: returnData, - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + Config: testEmptyConfig, + logs: logs, + consumedGas: consumedGas, + output: returnData, + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, expected: &StructTraceResult{ Account: "0x0000000000000000000000000000000000000000", Failed: false, Gas: consumedGas, ReturnValue: hex.EncodeToString(returnData), - StorageUpdates: map[types.Address][]StorageUpdate{ - {}: nil, + StorageUpdates: map[types.Address]map[StorageAccess]bool{ + {}: make(map[StorageAccess]bool), }, StructLogs: []StructLogRes{ { @@ -985,15 +1005,15 @@ func TestStructTracerGetResult(t *testing.T) { consumedGas: consumedGas, output: returnData, err: err, - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + storageAccess: make([]map[StorageAccess]bool, 1), + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, expected: &StructTraceResult{ Failed: true, Gas: consumedGas, ReturnValue: "", Account: "0x0000000000000000000000000000000000000000", - StorageUpdates: map[types.Address][]StorageUpdate{ + StorageUpdates: map[types.Address]map[StorageAccess]bool{ {}: nil, }, StructLogs: []StructLogRes{ @@ -1029,11 +1049,13 @@ func TestStructTracerGetResult(t *testing.T) { { name: "should return error", tracer: &StructTracer{ - Config: testEmptyConfig, - reason: reason, - logs: logs, - storageUpdates: make([][]StorageUpdate, 1), - accountStorageUpdates: make(map[types.Address][]StorageUpdate), + Config: testEmptyConfig, + reason: reason, + logs: logs, + storageAccess: []map[StorageAccess]bool{ + make(map[StorageAccess]bool), + }, + accountStorageUpdates: make(map[types.Address]map[StorageAccess]bool), }, expected: nil, err: reason,