Skip to content

Commit

Permalink
Merge pull request #8 from OffchainLabs/stylus-state-storage
Browse files Browse the repository at this point in the history
Stylus state storage
  • Loading branch information
rachel-bousfield authored Mar 15, 2023
2 parents df1701d + c39800e commit 8e292d3
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 13 deletions.
15 changes: 4 additions & 11 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
ret, err = evm.runInterpreter(contract, input, false)
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
}
Expand Down Expand Up @@ -305,7 +305,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.runInterpreter(contract, input, false)
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
if err != nil {
Expand All @@ -317,13 +317,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
return ret, gas, err
}

func (evm *EVM) runInterpreter(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if evm.chainRules.IsArbitrum && state.IsStylusProgram(contract.Code) {
return evm.ProcessingHook.ExecuteWASM(contract, input, readOnly, evm.TxContext, evm.Context)
}
return evm.interpreter.Run(contract, input, readOnly)
}

// DelegateCall executes the contract associated with the addr with the given input
// as parameters. It reverses the state in case of an execution error.
//
Expand Down Expand Up @@ -361,7 +354,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.runInterpreter(contract, input, false)
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
if err != nil {
Expand Down Expand Up @@ -425,7 +418,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
ret, err = evm.runInterpreter(contract, input, true)
ret, err = evm.interpreter.Run(contract, input, true)
gas = contract.Gas
}
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions core/vm/evm_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type TxProcessingHook interface {
L1BlockHash(blockCtx BlockContext, l1BlocKNumber uint64) (common.Hash, error)
GasPriceOp(evm *EVM) *big.Int
FillReceiptInfo(receipt *types.Receipt)
ExecuteWASM(contract *Contract, input []byte, readOnly bool, txContext TxContext, blockContext BlockContext) ([]byte, error)
ExecuteWASM(scope *ScopeContext, input []byte, interpreter *EVMInterpreter) ([]byte, error)
}

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

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

func (p DefaultTxProcessor) ExecuteWASM(contract *Contract, input []byte, readOnly bool, txContext TxContext, blockContext BlockContext) ([]byte, error) {
func (p DefaultTxProcessor) ExecuteWASM(scope *ScopeContext, input []byte, interpreter *EVMInterpreter) ([]byte, error) {
log.Crit("tried to execute WASM with default processing hook")
return nil, nil
}
8 changes: 8 additions & 0 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/log"
)

Expand Down Expand Up @@ -175,6 +176,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
}()
}

// Arbitrum: handle Stylus programs
if in.evm.chainRules.IsArbitrum && state.IsStylusProgram(contract.Code) {
ret, err = in.evm.ProcessingHook.ExecuteWASM(callContext, input, in)
return
}

// The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
Expand Down
37 changes: 37 additions & 0 deletions core/vm/interpreter_arbitrum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package vm

func (in *EVMInterpreter) Config() *Config {
return &in.cfg
}

func (in *EVMInterpreter) Depth() int {
return in.evm.depth
}

func (in *EVMInterpreter) Evm() *EVM {
return in.evm
}

func (in *EVMInterpreter) ReadOnly() bool {
return in.readOnly
}

func (in *EVMInterpreter) SetReturnData(data []byte) {
in.returnData = data
}
99 changes: 99 additions & 0 deletions core/vm/operations_acl_arbitrum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package vm

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

// Computes the cost of doing a state load in wasm
// Note: the code here is adapted from gasSLoadEIP2929
func WasmStateLoadCost(db StateDB, program common.Address, key common.Hash) uint64 {
// Check slot presence in the access list
if _, slotPresent := db.SlotInAccessList(program, key); !slotPresent {
// If the caller cannot afford the cost, this change will be rolled back
// If he does afford it, we can skip checking the same thing later on, during execution
db.AddSlotToAccessList(program, key)
return params.ColdSloadCostEIP2929
}
return params.WarmStorageReadCostEIP2929
}

// Computes the cost of doing a state store in wasm
// Note: the code here is adapted from makeGasSStoreFunc with the most recent parameters as of The Merge
// Note: the sentry check must be done by the caller
func WasmStateStoreCost(db StateDB, program common.Address, key, value common.Hash) uint64 {
clearingRefund := params.SstoreClearsScheduleRefundEIP3529

cost := uint64(0)
current := db.GetState(program, key)

// Check slot presence in the access list
if addrPresent, slotPresent := db.SlotInAccessList(program, key); !slotPresent {
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
db.AddSlotToAccessList(program, key)
if !addrPresent {
panic(fmt.Sprintf("impossible case: address %v was not present in access list", program))
}
}

if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return cost + params.WarmStorageReadCostEIP2929 // SLOAD_GAS
}
original := db.GetCommittedState(program, key)
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return cost + params.SstoreSetGasEIP2200
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
db.AddRefund(clearingRefund)
}
// EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
db.SubRefund(clearingRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
db.AddRefund(clearingRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
// EIP 2200 Original clause:
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
db.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
db.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
}
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929 // dirty update (2.2)
}

0 comments on commit 8e292d3

Please sign in to comment.