Skip to content

Commit b49cf09

Browse files
Merge pull request #10 from OffchainLabs/stylus-state-storage
Stylus state storage (master merge)
2 parents 2751178 + 55c15e9 commit b49cf09

File tree

5 files changed

+150
-13
lines changed

5 files changed

+150
-13
lines changed

core/vm/evm.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
240240
// The depth-check is already done, and precompiles handled above
241241
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
242242
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
243-
ret, err = evm.runInterpreter(contract, input, false)
243+
ret, err = evm.interpreter.Run(contract, input, false)
244244
gas = contract.Gas
245245
}
246246
}
@@ -305,7 +305,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
305305
// The contract is a scoped environment for this execution context only.
306306
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
307307
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
308-
ret, err = evm.runInterpreter(contract, input, false)
308+
ret, err = evm.interpreter.Run(contract, input, false)
309309
gas = contract.Gas
310310
}
311311
if err != nil {
@@ -317,13 +317,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
317317
return ret, gas, err
318318
}
319319

320-
func (evm *EVM) runInterpreter(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
321-
if evm.chainRules.IsArbitrum && state.IsStylusProgram(contract.Code) {
322-
return evm.ProcessingHook.ExecuteWASM(contract, input, readOnly, evm.TxContext, evm.Context)
323-
}
324-
return evm.interpreter.Run(contract, input, readOnly)
325-
}
326-
327320
// DelegateCall executes the contract associated with the addr with the given input
328321
// as parameters. It reverses the state in case of an execution error.
329322
//
@@ -361,7 +354,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
361354
// Initialise a new contract and make initialise the delegate values
362355
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
363356
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
364-
ret, err = evm.runInterpreter(contract, input, false)
357+
ret, err = evm.interpreter.Run(contract, input, false)
365358
gas = contract.Gas
366359
}
367360
if err != nil {
@@ -425,7 +418,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
425418
// When an error was returned by the EVM or when setting the creation code
426419
// above we revert to the snapshot and consume any gas remaining. Additionally
427420
// when we're in Homestead this also counts for code storage gas errors.
428-
ret, err = evm.runInterpreter(contract, input, true)
421+
ret, err = evm.interpreter.Run(contract, input, true)
429422
gas = contract.Gas
430423
}
431424
if err != nil {

core/vm/evm_arbitrum.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type TxProcessingHook interface {
5151
L1BlockHash(blockCtx BlockContext, l1BlocKNumber uint64) (common.Hash, error)
5252
GasPriceOp(evm *EVM) *big.Int
5353
FillReceiptInfo(receipt *types.Receipt)
54-
ExecuteWASM(contract *Contract, input []byte, readOnly bool, txContext TxContext, blockContext BlockContext) ([]byte, error)
54+
ExecuteWASM(scope *ScopeContext, input []byte, interpreter *EVMInterpreter) ([]byte, error)
5555
}
5656

5757
type DefaultTxProcessor struct {
@@ -96,7 +96,7 @@ func (p DefaultTxProcessor) GasPriceOp(evm *EVM) *big.Int {
9696

9797
func (p DefaultTxProcessor) FillReceiptInfo(*types.Receipt) {}
9898

99-
func (p DefaultTxProcessor) ExecuteWASM(contract *Contract, input []byte, readOnly bool, txContext TxContext, blockContext BlockContext) ([]byte, error) {
99+
func (p DefaultTxProcessor) ExecuteWASM(scope *ScopeContext, input []byte, interpreter *EVMInterpreter) ([]byte, error) {
100100
log.Crit("tried to execute WASM with default processing hook")
101101
return nil, nil
102102
}

core/vm/interpreter.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/ethereum/go-ethereum/common"
2323
"github.com/ethereum/go-ethereum/common/math"
24+
"github.com/ethereum/go-ethereum/core/state"
2425
"github.com/ethereum/go-ethereum/log"
2526
)
2627

@@ -175,6 +176,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
175176
}
176177
}()
177178
}
179+
180+
// Arbitrum: handle Stylus programs
181+
if in.evm.chainRules.IsArbitrum && state.IsStylusProgram(contract.Code) {
182+
ret, err = in.evm.ProcessingHook.ExecuteWASM(callContext, input, in)
183+
return
184+
}
185+
178186
// The Interpreter main run loop (contextual). This loop runs until either an
179187
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
180188
// the execution of one of the operations or until the done flag is set by the

core/vm/interpreter_arbitrum.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2014 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package vm
18+
19+
func (in *EVMInterpreter) Config() *Config {
20+
return &in.cfg
21+
}
22+
23+
func (in *EVMInterpreter) Depth() int {
24+
return in.evm.depth
25+
}
26+
27+
func (in *EVMInterpreter) Evm() *EVM {
28+
return in.evm
29+
}
30+
31+
func (in *EVMInterpreter) ReadOnly() bool {
32+
return in.readOnly
33+
}
34+
35+
func (in *EVMInterpreter) SetReturnData(data []byte) {
36+
in.returnData = data
37+
}

core/vm/operations_acl_arbitrum.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2020 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package vm
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/ethereum/go-ethereum/common"
23+
"github.com/ethereum/go-ethereum/params"
24+
)
25+
26+
// Computes the cost of doing a state load in wasm
27+
// Note: the code here is adapted from gasSLoadEIP2929
28+
func WasmStateLoadCost(db StateDB, program common.Address, key common.Hash) uint64 {
29+
// Check slot presence in the access list
30+
if _, slotPresent := db.SlotInAccessList(program, key); !slotPresent {
31+
// If the caller cannot afford the cost, this change will be rolled back
32+
// If he does afford it, we can skip checking the same thing later on, during execution
33+
db.AddSlotToAccessList(program, key)
34+
return params.ColdSloadCostEIP2929
35+
}
36+
return params.WarmStorageReadCostEIP2929
37+
}
38+
39+
// Computes the cost of doing a state store in wasm
40+
// Note: the code here is adapted from makeGasSStoreFunc with the most recent parameters as of The Merge
41+
// Note: the sentry check must be done by the caller
42+
func WasmStateStoreCost(db StateDB, program common.Address, key, value common.Hash) uint64 {
43+
clearingRefund := params.SstoreClearsScheduleRefundEIP3529
44+
45+
cost := uint64(0)
46+
current := db.GetState(program, key)
47+
48+
// Check slot presence in the access list
49+
if addrPresent, slotPresent := db.SlotInAccessList(program, key); !slotPresent {
50+
cost = params.ColdSloadCostEIP2929
51+
// If the caller cannot afford the cost, this change will be rolled back
52+
db.AddSlotToAccessList(program, key)
53+
if !addrPresent {
54+
panic(fmt.Sprintf("impossible case: address %v was not present in access list", program))
55+
}
56+
}
57+
58+
if current == value { // noop (1)
59+
// EIP 2200 original clause:
60+
// return params.SloadGasEIP2200, nil
61+
return cost + params.WarmStorageReadCostEIP2929 // SLOAD_GAS
62+
}
63+
original := db.GetCommittedState(program, key)
64+
if original == current {
65+
if original == (common.Hash{}) { // create slot (2.1.1)
66+
return cost + params.SstoreSetGasEIP2200
67+
}
68+
if value == (common.Hash{}) { // delete slot (2.1.2b)
69+
db.AddRefund(clearingRefund)
70+
}
71+
// EIP-2200 original clause:
72+
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
73+
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) // write existing slot (2.1.2)
74+
}
75+
if original != (common.Hash{}) {
76+
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
77+
db.SubRefund(clearingRefund)
78+
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
79+
db.AddRefund(clearingRefund)
80+
}
81+
}
82+
if original == value {
83+
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
84+
// EIP 2200 Original clause:
85+
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
86+
db.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
87+
} else { // reset to original existing slot (2.2.2.2)
88+
// EIP 2200 Original clause:
89+
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
90+
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
91+
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
92+
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
93+
db.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
94+
}
95+
}
96+
// EIP-2200 original clause:
97+
//return params.SloadGasEIP2200, nil // dirty update (2.2)
98+
return cost + params.WarmStorageReadCostEIP2929 // dirty update (2.2)
99+
}

0 commit comments

Comments
 (0)