diff --git a/core/state/statedb_arbitrum.go b/core/state/statedb_arbitrum.go index a6550ea2dbf6..d983b4b2bce3 100644 --- a/core/state/statedb_arbitrum.go +++ b/core/state/statedb_arbitrum.go @@ -24,7 +24,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" ) func (s *StateDB) Deterministic() bool { @@ -78,9 +80,15 @@ func (s *StateDB) RecordProgram(program common.Address, version uint32) { if _, ok := s.userWasms[call]; ok { return } + rawCode := s.GetCode(program) + compressedWasm, err := vm.StripStylusPrefix(rawCode) + if err != nil { + log.Error("Could not strip stylus program prefix from raw code: %v", err) + return + } s.userWasms[call] = &UserWasm{ NoncanonicalHash: s.NoncanonicalProgramHash(program, version), - CompressedWasm: s.GetCode(program), + CompressedWasm: compressedWasm, } } } diff --git a/core/vm/evm.go b/core/vm/evm.go index a1eb299c67ac..9be0880412a0 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -499,6 +499,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Reject code starting with 0xEF if EIP-3541 is enabled. if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { err = ErrInvalidCode + // Arbitrum: We do not reject Stylus programs and instead store them in the DB + // alongside normal EVM bytecode. + if evm.chainRules.IsArbitrum && IsStylusProgram(ret) { + err = nil + } } // if the contract creation ran successfully and no errors were returned diff --git a/core/vm/evm_arbitrum.go b/core/vm/evm_arbitrum.go index 2baa3caa3fa5..ba75845c770f 100644 --- a/core/vm/evm_arbitrum.go +++ b/core/vm/evm_arbitrum.go @@ -17,12 +17,27 @@ package vm import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) +var ( + // Defines prefix bytes for Stylus WASM program bytecode + // when deployed on-chain via a user-initiated transaction. + // These byte prefixes are meant to conflict with the L1 contract EOF + // validation rules so they can be sufficiently differentiated from EVM bytecode. + // This allows us to store WASM programs as code in the stateDB side-by-side + // with EVM contracts, but match against these prefix bytes when loading code + // to execute the WASMs through Stylus rather than the EVM. + stylusEOFMagic = byte(0xEF) + stylusEOFMagicSuffix = byte(0x00) + stylusEOFVersion = byte(0x00) + stylusEOFSectionHeader = byte(0x00) +) + // Depth returns the current depth func (evm *EVM) Depth() int { return evm.depth @@ -93,3 +108,21 @@ func (p DefaultTxProcessor) GasPriceOp(evm *EVM) *big.Int { } func (p DefaultTxProcessor) FillReceiptInfo(*types.Receipt) {} + +// IsStylusProgram checks if a specified bytecode is a user-submitted WASM program. +// Stylus differentiates WASMs from EVM bytecode via the prefix 0xEF000000 which will safely fail +// to pass through EVM-bytecode EOF validation rules. +func IsStylusProgram(b []byte) bool { + if len(b) < 4 { + return false + } + return b[0] == stylusEOFMagic && b[1] == stylusEOFMagicSuffix && b[2] == stylusEOFVersion && b[3] == stylusEOFSectionHeader +} + +// StripStylusPrefix if the specified input is a stylus program. +func StripStylusPrefix(b []byte) ([]byte, error) { + if !IsStylusProgram(b) { + return nil, errors.New("specified bytecode is not a Stylus program") + } + return b[4:], nil +}