diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 52c83af9b845..ad8ff107eddb 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -75,6 +75,11 @@ jobs: EXCLUDE_REGEX: "ava-labs/libevm/(accounts/usbwallet/trezor)$" runs-on: ubuntu-latest steps: + - name: Install solc + uses: arr4n/setup-solc@v0.3.0 + with: + version: 0.8.28 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 diff --git a/accounts/abi/selector.libevm.go b/accounts/abi/selector.libevm.go new file mode 100644 index 000000000000..e7b0ddfd03b6 --- /dev/null +++ b/accounts/abi/selector.libevm.go @@ -0,0 +1,75 @@ +// Copyright 2024 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +package abi + +import ( + "encoding/binary" + "fmt" +) + +// SelectorByteLen is the number of bytes in an ABI function selector. +const SelectorByteLen = 4 + +// A Selector is an ABI function selector. It is a uint32 instead of a [4]byte +// to allow for simpler hex literals. +type Selector uint32 + +// String returns a hex encoding of `s`. +func (s Selector) String() string { + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, uint32(s)) + return fmt.Sprintf("%#x", b) +} + +// ExtractSelector returns the first 4 bytes of the slice as a Selector. It +// assumes that its input is of sufficient length. +func ExtractSelector(data []byte) Selector { + return Selector(binary.BigEndian.Uint32(data[:4])) +} + +// Selector returns the Method's ID as a Selector. +func (m Method) Selector() Selector { + return ExtractSelector(m.ID) +} + +// MethodsBySelector maps 4-byte ABI selectors to the corresponding method +// representation. The key MUST be equivalent to the value's [Method.Selector]. +type MethodsBySelector map[Selector]Method + +// MethodsBySelector returns the [Method]s keyed by their Selectors. +func (a *ABI) MethodsBySelector() MethodsBySelector { + ms := make(MethodsBySelector) + for _, m := range a.Methods { + ms[m.Selector()] = m + } + return ms +} + +// FindSelector extracts the Selector from `data` and, if it exists in `m`, +// returns it. The returned boolean functions as for regular map lookups. Unlike +// [ExtractSelector], FindSelector confirms that `data` has at least 4 bytes, +// treating invalid inputs as not found. +func (m MethodsBySelector) FindSelector(data []byte) (Selector, bool) { + if len(data) < SelectorByteLen { + return 0, false + } + sel := ExtractSelector(data) + if _, ok := m[sel]; !ok { + return 0, false + } + return sel, true +} diff --git a/core/state_transition.libevm_test.go b/core/state_transition.libevm_test.go index e0c390af3b77..5ee52ff6abcf 100644 --- a/core/state_transition.libevm_test.go +++ b/core/state_transition.libevm_test.go @@ -499,7 +499,11 @@ func TestGasRefunds(t *testing.T) { DisableGasRefunds: !tt.shouldRefund, PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{ refunder: vm.NewStatefulPrecompile(func(env vm.PrecompileEnvironment, _ []byte) ([]byte, error) { - env.StateDB().AddRefund(refund) + sdb, err := env.StateDB() + if err != nil { + return nil, err + } + sdb.AddRefund(refund) return nil, nil }), }, diff --git a/core/vm/contracts.libevm.go b/core/vm/contracts.libevm.go index e509f5c1a6cc..7ef3f300e5c9 100644 --- a/core/vm/contracts.libevm.go +++ b/core/vm/contracts.libevm.go @@ -17,8 +17,10 @@ package vm import ( + "errors" "fmt" "math/big" + "slices" "github.com/holiman/uint256" "golang.org/x/exp/slog" @@ -94,10 +96,10 @@ type evmCallArgs struct { type CallType OpCode const ( - Call = CallType(CALL) - CallCode = CallType(CALLCODE) - DelegateCall = CallType(DELEGATECALL) - StaticCall = CallType(STATICCALL) + Call CallType = CallType(CALL) + CallCode CallType = CallType(CALLCODE) + DelegateCall CallType = CallType(DELEGATECALL) + StaticCall CallType = CallType(STATICCALL) ) func (t CallType) isValid() bool { @@ -131,6 +133,32 @@ func (t CallType) OpCode() OpCode { return INVALID } +// StateMutability describes the available state access. +type StateMutability uint + +const ( + // Pure is a Solidity concept disallowing all access, read or write, to + // state. + Pure StateMutability = iota + 1 + // ReadOnlyState is equivalent to Solidity's "view". + ReadOnlyState + // MutableState can be both read from and written to. + MutableState +) + +// String returns a human-readable representation of the StateMutability. +func (m StateMutability) String() string { + switch m { + case MutableState: + return "mutable" + case ReadOnlyState: + return "read-only" + case Pure: + return "no state access" + } + return fmt.Sprintf("unknown %T(%[1]d)", m) +} + // run runs the [PrecompiledContract], differentiating between stateful and // regular types, updating `args.gasRemaining` in the stateful case. func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, err error) { @@ -153,8 +181,11 @@ func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, e defer func() { in.readOnly = false }() } - ret, err = sp(env, input) + ret, err = sp(env, slices.Clone(input)) args.gasRemaining = env.Gas() + if r := new(RevertError); errors.As(err, r) { + return r.Bytes(), r.Unwrap() + } return ret, err } @@ -164,6 +195,16 @@ func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, e // Instead of receiving and returning gas arguments, stateful precompiles use // the respective methods on [PrecompileEnvironment]. If a call to UseGas() // returns false, a stateful precompile SHOULD return [ErrOutOfGas]. +// +// Failures that are expected to occur in production SHOULD return a raw, +// non-wrapped [ErrExecutionReverted], while failures that need to be detected in +// development MAY return an arbitrary error. The former will bubble up through +// execution contexts as a revert while the latter will consume all gas. This +// usage mirrors Solidity's `require` and `assert` respectively; see [Solidity +// docs] for more information. +// +// [Solidity docs]: +// https://docs.soliditylang.org/en/v0.8.37/control-structures.html#panic-via-assert-and-error-via-require type PrecompiledStatefulContract func(env PrecompileEnvironment, input []byte) (ret []byte, err error) // NewStatefulPrecompile constructs a new PrecompiledContract that can be used @@ -197,14 +238,32 @@ func (p statefulPrecompile) Run([]byte) ([]byte, error) { type PrecompileEnvironment interface { ChainConfig() *params.ChainConfig Rules() params.Rules - // StateDB will be non-nil i.f.f !ReadOnly(). - StateDB() StateDB - // ReadOnlyState will always be non-nil. - ReadOnlyState() libevm.StateReader + // StateDB returns a non-nil [StateDB] i.f.f StateMutability() returns + // [MutableState], otherwise it returns [ErrWriteProtection]. + StateDB() (StateDB, error) + // ReadOnlyState returns a non-nil [libevm.StateReader] i.f.f. + // StateMutability() does not return [Pure]. The boolean indicates whether + // the reader is non-nil and acts only to nudge the call site against + // nil-pointer usage. + + ReadOnlyState() (libevm.StateReader, bool) + + // StateMutability can infer [MutableState] vs [ReadOnlyState] based on EVM + // context, but [Pure] is a Solidity concept that is enforced by user code. + StateMutability() StateMutability + // AsReadOnly returns a copy of the current environment for which + // StateMutability() is at most [ReadOnlyState]; i.e. if mutability is + // already limited to [Pure], AsReadOnly() will not expand access. It can be + // used as a guard against accidental writes when a read-only function is + // invoked with EVM call() instead of staticcall(). + AsReadOnly() PrecompileEnvironment + // AsPure returns a copy of the current environment that has no access to + // state; i.e. StateMutability() returns [Pure]. All calls to both StateDB() + // and ReadOnlyState() will return nil. + AsPure() PrecompileEnvironment IncomingCallType() CallType Addresses() *libevm.AddressContext - ReadOnly() bool // Equivalent to respective methods on [Contract]. Gas() uint64 UseGas(uint64) (hasEnoughGas bool) @@ -276,3 +335,29 @@ var ( (*EVM)(nil).StaticCall, } ) + +// A RevertError couples [ErrExecutionReverted] with the EVM return buffer; it +// is not used by vanilla geth. +// +// It exists for a libevm `precompilegen` method, whose return value is the +// typed, ABI-packed output and thus has no channel for a raw revert buffer. +// Returning a RevertError lets the method revert with raw bytes, bypassing the +// regular argument packing. +// +// Although any [PrecompiledStatefulContract] MAY return a RevertError, it +// already returns a ([]byte, error) pair so using a RevertError is both +// redundant and ambiguous: the returned []byte is discarded and replaced by +// [RevertError.Bytes]. Such contracts SHOULD instead return the raw buffer with +// a plain [ErrExecutionReverted]. +type RevertError []byte + +// Error is equivalent to the respective method on [ErrExecutionReverted]. +func (e RevertError) Error() string { return ErrExecutionReverted.Error() } + +// Bytes returns the return buffer with which an EVM context reverted. +func (e RevertError) Bytes() []byte { return []byte(e) } + +// Unwrap returns [ErrExecutionReverted]. +func (e RevertError) Unwrap() error { + return ErrExecutionReverted +} diff --git a/core/vm/contracts.libevm_test.go b/core/vm/contracts.libevm_test.go index 5cbd19d15daf..68674959336b 100644 --- a/core/vm/contracts.libevm_test.go +++ b/core/vm/contracts.libevm_test.go @@ -26,6 +26,7 @@ import ( "strings" "testing" + "github.com/arr4n/shed/testerr" "github.com/google/go-cmp/cmp" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" @@ -111,7 +112,7 @@ type statefulPrecompileOutput struct { Addresses *libevm.AddressContext StateValue common.Hash CallValue *uint256.Int - ReadOnly bool + Mutability vm.StateMutability BlockNumber, Difficulty *big.Int BlockTime uint64 Input []byte @@ -158,20 +159,21 @@ func TestNewStatefulPrecompile(t *testing.T) { gasCost := rng.Uint64n(gasLimit) run := func(env vm.PrecompileEnvironment, input []byte, suppliedGas uint64) ([]byte, uint64, error) { - if got, want := env.StateDB() != nil, !env.ReadOnly(); got != want { - return nil, 0, fmt.Errorf("PrecompileEnvironment().StateDB() must be non-nil i.f.f. not read-only; got non-nil? %t; want %t", got, want) - } hdr, err := env.BlockHeader() if err != nil { return nil, 0, err } + sdb, ok := env.ReadOnlyState() + if !ok { + return nil, 0, fmt.Errorf("%T.ReadOnlyState() returned false", env) + } out := &statefulPrecompileOutput{ ChainID: env.ChainConfig().ChainID, Addresses: env.Addresses(), - StateValue: env.ReadOnlyState().GetState(precompile, slot), + StateValue: sdb.GetState(precompile, slot), CallValue: env.Value(), - ReadOnly: env.ReadOnly(), + Mutability: env.StateMutability(), BlockNumber: env.BlockNumber(), BlockTime: env.BlockTime(), Difficulty: hdr.Difficulty, @@ -233,8 +235,8 @@ func TestNewStatefulPrecompile(t *testing.T) { wantCallValue *uint256.Int // Note that this only covers evm.readOnly being true because of the // precompile's call. See TestInheritReadOnly for alternate case. - wantReadOnly bool - wantCallType vm.CallType + wantMutability vm.StateMutability + wantCallType vm.CallType }{ { name: "EVM.Call()", @@ -246,9 +248,9 @@ func TestNewStatefulPrecompile(t *testing.T) { EVMSemantic: rawAddresses, Raw: &rawAddresses, }, - wantReadOnly: false, - wantCallValue: callPrecompileValue, - wantCallType: vm.Call, + wantMutability: vm.MutableState, + wantCallValue: callPrecompileValue, + wantCallType: vm.Call, }, { name: "EVM.CallCode()", @@ -263,9 +265,9 @@ func TestNewStatefulPrecompile(t *testing.T) { }, Raw: &rawAddresses, }, - wantReadOnly: false, - wantCallValue: callPrecompileValue, - wantCallType: vm.CallCode, + wantMutability: vm.MutableState, + wantCallValue: callPrecompileValue, + wantCallType: vm.CallCode, }, { name: "EVM.DelegateCall()", @@ -280,9 +282,9 @@ func TestNewStatefulPrecompile(t *testing.T) { }, Raw: &rawAddresses, }, - wantReadOnly: false, - wantCallValue: callCallerValue, // Important difference from [vm.EVM.Call] - wantCallType: vm.DelegateCall, + wantMutability: vm.MutableState, + wantCallValue: callCallerValue, // Important difference from [vm.EVM.Call] + wantCallType: vm.DelegateCall, }, { name: "EVM.StaticCall()", @@ -294,9 +296,9 @@ func TestNewStatefulPrecompile(t *testing.T) { EVMSemantic: rawAddresses, Raw: &rawAddresses, }, - wantReadOnly: true, - wantCallValue: uint256.NewInt(0), - wantCallType: vm.StaticCall, + wantMutability: vm.ReadOnlyState, + wantCallValue: uint256.NewInt(0), + wantCallType: vm.StaticCall, }, } @@ -307,7 +309,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Addresses: tt.wantAddresses, StateValue: stateValue, CallValue: tt.wantCallValue, - ReadOnly: tt.wantReadOnly, + Mutability: tt.wantMutability, BlockNumber: header.Number, BlockTime: header.Time, Difficulty: header.Difficulty, @@ -430,7 +432,7 @@ func TestInheritReadOnly(t *testing.T) { PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{ precompile: vm.NewStatefulPrecompile( func(env vm.PrecompileEnvironment, input []byte) ([]byte, error) { - if env.ReadOnly() { + if env.StateMutability() != vm.MutableState { return []byte{ifReadOnly}, nil } return []byte{ifNotReadOnly}, nil @@ -679,9 +681,9 @@ func TestPrecompileMakeCall(t *testing.T) { }), dest: vm.NewStatefulPrecompile(func(env vm.PrecompileEnvironment, input []byte) (ret []byte, err error) { out := &statefulPrecompileOutput{ - Addresses: env.Addresses(), - ReadOnly: env.ReadOnly(), - Input: input, // expected to be callData + Addresses: env.Addresses(), + Mutability: env.StateMutability(), + Input: input, // expected to be callData } return out.Bytes(), nil }), @@ -712,7 +714,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -726,7 +729,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -739,7 +743,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -753,7 +758,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -766,7 +772,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -780,7 +787,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -798,7 +806,7 @@ func TestPrecompileMakeCall(t *testing.T) { // (non-static) CALL, the read-only state is inherited. Yes, // this is _another_ way to get a read-only state, different to // the other tests. - ReadOnly: true, + Mutability: vm.ReadOnlyState, }, }, { @@ -812,8 +820,8 @@ func TestPrecompileMakeCall(t *testing.T) { Self: dest, }, }, - Input: precompileCallData, - ReadOnly: true, + Input: precompileCallData, + Mutability: vm.ReadOnlyState, }, }, } @@ -930,3 +938,122 @@ func TestPrecompileCallWithCallTracer(t *testing.T) { t.Errorf("%q tracer diff (-want +got):\n%s", tracerName, diff) } } + +func TestStateMutability(t *testing.T) { + rng := ethtest.NewPseudoRand(0) + precompileAddr := rng.Address() + chainID := rng.BigUint64() + + const precompileReturn = "precompile executed" + precompile := vm.NewStatefulPrecompile(func(env vm.PrecompileEnvironment, input []byte) (ret []byte, err error) { + type test struct { + name string + env vm.PrecompileEnvironment + want vm.StateMutability + } + var tests []test + + incomingCallType := env.IncomingCallType() + + switch incomingCallType { + case vm.Call: + tests = []test{ + { + name: "incoming argument", + env: env, + want: vm.MutableState, + }, + { + name: "AsReadOnly()", + env: env.AsReadOnly(), + want: vm.ReadOnlyState, + }, + { + name: "AsPure()", + env: env.AsPure(), + want: vm.Pure, + }, + { + name: "AsPure().AsReadOnly() is still pure", + env: env.AsPure().AsReadOnly(), + want: vm.Pure, + }, + } + case vm.StaticCall: + tests = []test{ + { + name: "incoming argument", + env: env, + want: vm.ReadOnlyState, + }, + } + default: + return nil, fmt.Errorf("BAD TEST SETUP: no cases for precompile %T == %[1]v", incomingCallType) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + env := tt.env // deliberately shadow the incoming arg + t.Run("mutability_and_access", func(t *testing.T) { + require.Equal(t, tt.want, env.StateMutability(), "env.StateMutability()") + + _, err := env.StateDB() + var want testerr.Want + if tt.want != vm.MutableState { + want = testerr.Equals(vm.ErrWriteProtection) + } + if diff := testerr.Diff(err, want); diff != "" { + t.Errorf("env.StateDB() %s", diff) + } + + _, ok := env.ReadOnlyState() + assert.Equal(t, ok, tt.want != vm.Pure, "env.ReadOnlyState() ok i.f.f !Pure (%v)", tt.want) + }) + + t.Run("environment_unmodified", func(t *testing.T) { + // Each of these demonstrate that the underlying + // copy of the environment propagates everything but + // mutability. + assert.Equal(t, chainID, env.ChainConfig().ChainID, "Chain ID preserved") + assert.Equalf(t, precompileAddr, env.Addresses().EVMSemantic.Self, "%T preserved", env.Addresses()) + assert.Equalf(t, incomingCallType, env.IncomingCallType(), "%T preserved", env.IncomingCallType()) + }) + }) + } + + return []byte(precompileReturn), nil + }) + + hooks := &hookstest.Stub{ + PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{ + precompileAddr: precompile, + }, + } + hooks.Register(t) + + _, evm := ethtest.NewZeroEVM(t, ethtest.WithChainConfig(¶ms.ChainConfig{ + ChainID: chainID, + })) + tests := map[string]func() ([]byte, error){ + "Call": func() (ret []byte, err error) { + ret, _, err = evm.Call(vm.AccountRef{}, precompileAddr, nil, 0, uint256.NewInt(0)) + return + }, + "StaticCall": func() (ret []byte, err error) { + ret, _, err = evm.StaticCall(vm.AccountRef{}, precompileAddr, nil, 0) + return + }, + } + for typ, fn := range tests { + got, err := fn() + if got, want := string(got), precompileReturn; err != nil || got != want { + t.Errorf("%T.%s([precompile]) got {%q, %v}; want {%q, nil}", evm, typ, got, err, want) + } + } +} + +func TestRevertErrorIs(t *testing.T) { + err := vm.RevertError{} + target := vm.ErrExecutionReverted + require.Truef(t, errors.Is(err, target), "errors.Is(%T, %v)", err, target) +} diff --git a/core/vm/environment.libevm.go b/core/vm/environment.libevm.go index 67743f16cff7..86442488d834 100644 --- a/core/vm/environment.libevm.go +++ b/core/vm/environment.libevm.go @@ -17,6 +17,7 @@ package vm import ( + "errors" "fmt" "math/big" @@ -38,18 +39,30 @@ type environment struct { callType CallType rawSelf, rawCaller common.Address + view, pure bool +} + +func (e *environment) copy() *environment { + return &environment{ + evm: e.evm, + self: e.self, + callType: e.callType, + rawSelf: e.rawSelf, + rawCaller: e.rawCaller, + view: e.view, + pure: e.pure, + } } func (e *environment) Gas() uint64 { return e.self.Gas } func (e *environment) UseGas(gas uint64) bool { return e.self.UseGas(gas) } func (e *environment) Value() *uint256.Int { return new(uint256.Int).Set(e.self.Value()) } -func (e *environment) ChainConfig() *params.ChainConfig { return e.evm.chainConfig } -func (e *environment) Rules() params.Rules { return e.evm.chainRules } -func (e *environment) ReadOnlyState() libevm.StateReader { return e.evm.StateDB } -func (e *environment) IncomingCallType() CallType { return e.callType } -func (e *environment) BlockNumber() *big.Int { return new(big.Int).Set(e.evm.Context.BlockNumber) } -func (e *environment) BlockTime() uint64 { return e.evm.Context.Time } +func (e *environment) ChainConfig() *params.ChainConfig { return e.evm.chainConfig } +func (e *environment) Rules() params.Rules { return e.evm.chainRules } +func (e *environment) IncomingCallType() CallType { return e.callType } +func (e *environment) BlockNumber() *big.Int { return new(big.Int).Set(e.evm.Context.BlockNumber) } +func (e *environment) BlockTime() uint64 { return e.evm.Context.Time } func (e *environment) InvalidateExecution(err error) { e.evm.InvalidateExecution(err) } @@ -62,8 +75,42 @@ func (e *environment) refundGas(add uint64) error { return nil } -func (e *environment) ReadOnly() bool { - return e.evm.interpreter.readOnly +func (e *environment) AsReadOnly() PrecompileEnvironment { + cp := e.copy() + cp.view = true + return cp +} + +func (e *environment) AsPure() PrecompileEnvironment { + cp := e.copy() + cp.pure = true + return cp +} + +func (e *environment) StateMutability() StateMutability { + switch { + // cases MUST be ordered from most to least restrictive + case e.pure: + return Pure + case e.view || e.evm.interpreter.readOnly: + return ReadOnlyState + default: + return MutableState + } +} + +func (e *environment) ReadOnlyState() (libevm.StateReader, bool) { + if e.pure { + return nil, false + } + return e.evm.StateDB, true +} + +func (e *environment) StateDB() (StateDB, error) { + if e.StateMutability() != MutableState { + return nil, ErrWriteProtection + } + return e.evm.StateDB, nil } func (e *environment) Addresses() *libevm.AddressContext { @@ -80,13 +127,6 @@ func (e *environment) Addresses() *libevm.AddressContext { } } -func (e *environment) StateDB() StateDB { - if e.ReadOnly() { - return nil - } - return e.evm.StateDB -} - func (e *environment) BlockHeader() (types.Header, error) { hdr := e.evm.Context.Header if hdr == nil { @@ -103,7 +143,26 @@ func (e *environment) Call(addr common.Address, input []byte, gas uint64, value return e.callContract(Call, addr, input, gas, value, opts...) } +var ( + errPureFunctionMakeCall = errors.New("contract call from pure function") + errViewFunctionMakeNonStaticCall = errors.New("non-static contract call from view function") +) + func (e *environment) callContract(typ CallType, addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) ([]byte, error) { + switch { + case e.pure: + return nil, errPureFunctionMakeCall + case e.view && typ != StaticCall: + // We condition this on [environment.view] instead of + // [environment.StateMutability] being [ReadOnlyState] because the + // latter would be too restrictive. The `view` and `pure` flags are + // analogous to the equivalent Solidity concepts, while + // [StateMutability] also considers [EVMInterpreter.readOnly], which + // [opCall] allows when there is zero value transfer. The same rationale + // holds for the custom error instead of [ErrWriteProtection]. + return nil, errViewFunctionMakeNonStaticCall + } + var caller ContractRef = e.self if options.As[callConfig](opts...).unsafeCallerAddressProxying { // Note that, in addition to being unsafe, this breaks an EVM @@ -116,7 +175,7 @@ func (e *environment) callContract(typ CallType, addr common.Address, input []by } } - if e.ReadOnly() && value != nil && !value.IsZero() { + if e.StateMutability() != MutableState && value != nil && !value.IsZero() { return nil, ErrWriteProtection } if !e.UseGas(gas) { diff --git a/go.mod b/go.mod index ad207113fcb4..ae420cf25dae 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 github.com/Microsoft/go-winio v0.6.1 github.com/VictoriaMetrics/fastcache v1.12.1 + github.com/arr4n/shed v0.0.0-20260908170710-35e123a98127 github.com/aws/aws-sdk-go-v2 v1.21.2 github.com/aws/aws-sdk-go-v2/config v1.18.45 github.com/aws/aws-sdk-go-v2/credentials v1.13.43 diff --git a/go.sum b/go.sum index 3f876903d968..5451a18fcb42 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/arr4n/shed v0.0.0-20260908170710-35e123a98127 h1:TU5hfzfrqHuGepNJCL3Y3mZi/DmDDiM6RKITFtpTVTc= +github.com/arr4n/shed v0.0.0-20260908170710-35e123a98127/go.mod h1:eD5UkxiWTdbkqM7mg2Xf981SAeWQ/Q80js/1rFcKpfg= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/config v1.18.45 h1:Aka9bI7n8ysuwPeFdm77nfbyHCAKQ3z9ghB3S/38zes= diff --git a/libevm/ethtest/rand.go b/libevm/ethtest/rand.go index 832d783e7185..af39a29dc8c6 100644 --- a/libevm/ethtest/rand.go +++ b/libevm/ethtest/rand.go @@ -17,13 +17,16 @@ package ethtest import ( + "crypto/ecdsa" "math/big" + "testing" "github.com/holiman/uint256" "golang.org/x/exp/rand" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" ) // PseudoRand extends [rand.Rand] (*not* crypto/rand). @@ -90,6 +93,16 @@ func (r *PseudoRand) Uint256() *uint256.Int { return new(uint256.Int).SetBytes(r.Bytes(32)) } +// UnsafePrivateKey returns a private key on the secp256k1 curve. +func (r *PseudoRand) UnsafePrivateKey(tb testing.TB) *ecdsa.PrivateKey { + tb.Helper() + key, err := ecdsa.GenerateKey(crypto.S256(), r.Rand) + if err != nil { + tb.Fatalf("ecdsa.GenerateKey(crypto.S256(), %T) error %v", r.Rand, err) + } + return key +} + // Bloom returns a pseudorandom Bloom. func (r *PseudoRand) Bloom() (b types.Bloom) { r.Read(b[:]) diff --git a/libevm/precompilegen/main.go b/libevm/precompilegen/main.go new file mode 100644 index 000000000000..5210f39b84aa --- /dev/null +++ b/libevm/precompilegen/main.go @@ -0,0 +1,172 @@ +// Copyright 2024 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +// The precompilegen binary generates code for creating EVM precompiles +// conforming to arbitrary Solidity interfaces. +package main + +import ( + "bytes" + "flag" + "fmt" + "go/format" + "io" + "os" + "sort" + "strings" + "text/template" + + "github.com/ava-labs/libevm/accounts/abi" + + _ "embed" +) + +type config struct { + in, out string + pkg string +} + +func main() { + var c config + flag.StringVar(&c.in, "in", "", `Input ABI file or empty for stdin.`) + flag.StringVar(&c.out, "out", "", `Output Go file or empty for stdout.`) + flag.StringVar(&c.pkg, "package", "", "Generated package name.") + flag.Parse() + + in := os.Stdin + if c.in != "" { + in = mustOpen(c.in, os.O_RDONLY, 0) + } + out := os.Stdout + if c.out != "" { + out = mustOpen(c.out, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + } + + if err := c.generate(in, out); err != nil { + exit(err) + } + fmt.Fprintln(os.Stderr, "Generated precompile") +} + +func mustOpen(name string, flag int, perm os.FileMode) *os.File { + f, err := os.OpenFile(name, flag, perm) //nolint:gosec // User-provided file path is necessary behaviour, isolated to development environment. + if err != nil { + exit(err) + } + return f +} + +func exit(a ...any) { + fmt.Fprintln(os.Stderr, a...) + os.Exit(1) +} + +var ( + //go:embed precompile.go.tmpl + rawTemplate string + funcs = template.FuncMap{ + "methods": methods, + "signature": signature, + "hex": hex, + "type": goType, + "args": args, + "interfaceID": interfaceID, + } + tmpl = template.Must(template.New("contract").Funcs(funcs).Parse(rawTemplate)) +) + +func (c *config) generate(abiJSON io.Reader, out io.WriteCloser) error { + var jsonCopy bytes.Buffer + parsed, err := abi.JSON(io.TeeReader(abiJSON, &jsonCopy)) + if err != nil { + return fmt.Errorf("parse ABI: %v", err) + } + + var buf bytes.Buffer + data := struct { + ABI abi.ABI + JSON string + Package string + }{parsed, jsonCopy.String(), c.pkg} + if err := tmpl.Execute(&buf, data); err != nil { + return fmt.Errorf("execute template: %v", err) + } + + src, err := format.Source(buf.Bytes()) + if err != nil { + return fmt.Errorf("format source: %v", err) + } + if _, err := out.Write(src); err != nil { + return fmt.Errorf("write output: %v", err) + } + return out.Close() +} + +func methods(a abi.ABI) []abi.Method { + methods := make([]abi.Method, 0, len(a.Methods)) + for _, m := range a.Methods { + methods = append(methods, m) + } + sort.Slice(methods, func(i, j int) bool { + return methods[i].Name < methods[j].Name + }) + return methods +} + +func signature(m abi.Method) string { + in := append([]string{"vm.PrecompileEnvironment"}, asGoArgs(m.Inputs)...) + out := append(asGoArgs(m.Outputs), "error") + return fmt.Sprintf("%s(%s) (%s)", m.Name, strings.Join(in, ","), strings.Join(out, ",")) +} + +// interfaceID returns the EIP-165 interface ID of the methods. +func interfaceID(a abi.ABI) abi.Selector { + var id abi.Selector + for _, m := range a.Methods { + id ^= m.Selector() + } + return id +} + +func asGoArgs(args abi.Arguments) []string { + goArgs := make([]string, len(args)) + for i, a := range args { + goArgs[i] = a.Type.GetType().String() + } + return goArgs +} + +func hex(x any) string { + return fmt.Sprintf("%#x", x) +} + +func goType(a abi.Argument) string { + return a.Type.GetType().String() +} + +func args(prefix string, n int, withEnv, withErr bool) string { + a := make([]string, n) + for i := 0; i < n; i++ { + a[i] = fmt.Sprintf("%s%d", prefix, i) + } + if withEnv { + a = append([]string{"env"}, a...) + } + if withErr { + a = append(a, "err") + } + return strings.Join(a, ", ") +} diff --git a/libevm/precompilegen/precompile.go.tmpl b/libevm/precompilegen/precompile.go.tmpl new file mode 100644 index 000000000000..bf2b4e734c2b --- /dev/null +++ b/libevm/precompilegen/precompile.go.tmpl @@ -0,0 +1,129 @@ +// Package {{.Package}} is a generated package for creating EVM precompiles +// conforming to the EIP-165 interface ID {{interfaceID .ABI}}. +package {{.Package}} + +// Code generated by precompilegen. DO NOT EDIT. + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ava-labs/libevm/accounts/abi" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/vm" +) + +// A Contract is an implementation of a precompiled contract conforming to the +// EIP-165 interface ID {{interfaceID .ABI}}. +type Contract interface { + // Fallback implements a fallback function, called if the method selector + // fails to match any other method. + Fallback(vm.PrecompileEnvironment, []byte) ([]byte, error) +{{range methods .ABI}} + // {{.Name}} implements the function with selector {{hex .ID}}: + // {{.String}} + {{signature .}} +{{end}} +} + +type methodDispatcher = func(Contract, vm.PrecompileEnvironment, []byte) ([]byte, error) + +var ( + // Avoid unused-import errors. + _ *big.Int = nil + _ *common.Address = nil + + methods abi.MethodsBySelector + dispatchers map[abi.Selector]methodDispatcher +) + +const abiJSON = `{{.JSON}}` + +func init() { + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + panic(err.Error()) + } + methods = parsed.MethodsBySelector() + + var d dispatch + dispatchers = map[abi.Selector]methodDispatcher{ + {{range methods .ABI}}{{hex .ID}}: d.{{.Name}}, + {{end}} + } +} + +// New returns a precompiled contract backed by the provided implementation. +func New(impl Contract) vm.PrecompiledContract { + return vm.NewStatefulPrecompile(precompile{impl}.run) +} + +type precompile struct { + impl Contract +} + +const revertBufferWhenNonPayableReceivesValue = "non-payable" + +func (p precompile) run(env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + selector, ok := methods.FindSelector(input) + if !ok { + return p.impl.Fallback(env, input) + } + + payable := false + switch m := methods[selector]; m.StateMutability { + case "payable": + payable = true + case "nonpayable": + case "pure": + env = env.AsPure() + case "view": + env = env.AsReadOnly() + default: + // If this happens then `precompilegen` needs to be extended because the + // Solidity ABI spec changed. + data := fmt.Sprintf("unsupported state mutability %q on method %s", m.StateMutability, m.Sig) + return []byte(data), vm.ErrExecutionReverted + } + if !payable && !env.Value().IsZero() { + return []byte(revertBufferWhenNonPayableReceivesValue), vm.ErrExecutionReverted + } + + return dispatchers[selector](p.impl, env, input) +} + +// dispatch is the type on which all method dispatchers are defined, stopping +// them from being exported as top-level identifiers that clutter package +// documentation. +type dispatch struct{} + +{{range methods .ABI}} +func (dispatch) {{.Name}}(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + {{if or (gt (len .Inputs) 0) (gt (len .Outputs) 0) -}} + method := methods[{{hex .ID}}] + {{- end}} + + {{if gt (len .Inputs) 0 -}} + inputs, err := method.Inputs.Unpack(input[abi.SelectorByteLen:]) + if err != nil { + return nil, err + } + {{- range $i, $in := .Inputs}} + i{{$i}} := inputs[{{$i}}].({{type $in}}) + {{- end}} + {{- end}} + + { + {{args "o" (len .Outputs) false true}} := impl.{{.Name}}({{args "i" (len .Inputs) true false}}) + if err != nil { + return nil, err + } + {{if gt (len .Outputs) 0}} + return method.Outputs.Pack({{args "o" (len .Outputs) false false}}) + {{- else -}} + return nil, nil + {{- end}} + } +} +{{end}} \ No newline at end of file diff --git a/libevm/precompilegen/testprecompile/.gitignore b/libevm/precompilegen/testprecompile/.gitignore new file mode 100644 index 000000000000..ba75a4ee7f65 --- /dev/null +++ b/libevm/precompilegen/testprecompile/.gitignore @@ -0,0 +1,3 @@ +*.abi +*.bin + diff --git a/libevm/precompilegen/testprecompile/IPrecompile.sol b/libevm/precompilegen/testprecompile/IPrecompile.sol new file mode 100644 index 000000000000..65de9086f700 --- /dev/null +++ b/libevm/precompilegen/testprecompile/IPrecompile.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: LGPL-3.0 +pragma solidity 0.8.28; + +/// @dev Interface of precompiled contract for implementation via `precompilegen`. +interface IPrecompile { + function Echo(string memory) external view returns (string memory); + + function Echo(uint256) external view returns (uint256); + + function HashPacked(uint256, bytes2, address) external view returns (bytes32); + + struct Wrapper { + int256 val; + } + + function Extract(Wrapper memory) external view returns (int256); + + function Self() external view returns (address); + + function RevertWith(bytes memory) external; + + function View() external view returns (bool canReadState, bool canWriteState); + + function Pure() external pure returns (bool canReadState, bool canWriteState); + + function NeitherViewNorPure() external returns (bool canReadState, bool canWriteState); + + function Payable() external payable returns (uint256 value); + + function NonPayable() external; +} diff --git a/libevm/precompilegen/testprecompile/TestSuite.sol b/libevm/precompilegen/testprecompile/TestSuite.sol new file mode 100644 index 000000000000..53a05f9de954 --- /dev/null +++ b/libevm/precompilegen/testprecompile/TestSuite.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-3.0 +pragma solidity 0.8.28; + +import {IPrecompile} from "./IPrecompile.sol"; + +/// @dev Testing contract to exercise the Go implementaiton of `IPrecompile`. +contract TestSuite { + IPrecompile immutable precompile; + + /// @dev Expected revert buffer when paying a non-payable function. + string private _expectedNonPayableErrorMsg; + + constructor(IPrecompile _precompile, string memory nonPayableErrorMsg) payable { + precompile = _precompile; + _expectedNonPayableErrorMsg = nonPayableErrorMsg; + } + + /// @dev Emitted by each function to prove that it was successfully called. + event Called(string func); + + function Echo(string memory x) external { + assert(keccak256(abi.encodePacked(precompile.Echo(x))) == keccak256(abi.encodePacked(x))); + emit Called("Echo(string)"); + } + + function Echo(uint256 x) external { + assert(precompile.Echo(x) == x); + emit Called("Echo(uint256)"); + } + + function Extract(IPrecompile.Wrapper memory x) external { + assert(x.val == precompile.Extract(x)); + emit Called("Extract(...)"); + } + + function HashPacked(uint256 x, bytes2 y, address z) external { + assert(precompile.HashPacked(x, y, z) == keccak256(abi.encodePacked(x, y, z))); + emit Called("HashPacked(...)"); + } + + function Self() external { + assert(precompile.Self() == address(precompile)); + emit Called("Self()"); + } + + function RevertWith(bytes memory err) external { + (bool ok, bytes memory ret) = + address(precompile).call(abi.encodeWithSelector(IPrecompile.RevertWith.selector, err)); + assert(!ok); + assert(keccak256(ret) == keccak256(err)); + + emit Called("RevertWith(...)"); + } + + function EchoingFallback(bytes memory input) external { + bytes memory data = abi.encodeWithSelector( /*non-existent selector*/ 0, input); + (bool ok, bytes memory ret) = address(precompile).staticcall(data); + assert(ok); + // Note equality with `data`, not `input` as the fallback echoes its + // entire calldata, which includes the non-matching selector. + assert(keccak256(ret) == keccak256(data)); + + emit Called("EchoingFallback(...)"); + } + + function assertReadOnly(bytes4 selector, bool expectCanReadState, bool expectCanWriteState) internal { + // We use a low-level call() here to ensure that the Solidity compiler + // doesn't issue a staticcall() to view/pure functions as this would + // hide our forcing of a read-only state. + (bool ok, bytes memory ret) = address(precompile).call(abi.encodeWithSelector(selector)); + assert(ok); + (bool canReadState, bool canWriteState) = abi.decode(ret, (bool, bool)); + assert(canReadState == expectCanReadState); + assert(canWriteState == expectCanWriteState); + } + + function View() external { + assertReadOnly(IPrecompile.View.selector, true, false); + emit Called("View()"); + } + + function Pure() external { + assertReadOnly(IPrecompile.Pure.selector, false, false); + emit Called("Pure()"); + } + + function NeitherViewNorPure() external { + assertReadOnly(IPrecompile.NeitherViewNorPure.selector, true, true); + emit Called("NeitherViewNorPure()"); + } + + function Transfer() external { + uint256 value = precompile.Payable{value: 42}(); + assert(value == 42); + + bytes4 nonPayable = IPrecompile.NonPayable.selector; + callNonPayable(nonPayable, 0, ""); + + bytes memory err = abi.encodePacked(_expectedNonPayableErrorMsg); + callNonPayable(nonPayable, 1, err); + callNonPayable(IPrecompile.View.selector, 1, err); + callNonPayable(IPrecompile.Pure.selector, 1, err); + + emit Called("Transfer()"); + } + + function callNonPayable(bytes4 selector, uint256 value, bytes memory expectRevertWith) internal { + // We can't use just call `precompile.NonPayable()` directly because + // (a) it's a precompile and (b) it doesn't return values, which means + // that Solidity will perform an EXTCODESIZE check first and revert. + bytes memory data = abi.encodeWithSelector(selector); + (bool ok, bytes memory ret) = address(precompile).call{value: value}(data); + + if (expectRevertWith.length == 0) { + assert(ok); + } else { + assert(!ok); + assert(keccak256(ret) == keccak256(expectRevertWith)); + } + } +} diff --git a/libevm/precompilegen/testprecompile/generated.go b/libevm/precompilegen/testprecompile/generated.go new file mode 100644 index 000000000000..ed2edf3574a6 --- /dev/null +++ b/libevm/precompilegen/testprecompile/generated.go @@ -0,0 +1,323 @@ +// Package testprecompile is a generated package for creating EVM precompiles +// conforming to the EIP-165 interface ID 0xcacc24d1. +package testprecompile + +// Code generated by precompilegen. DO NOT EDIT. + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ava-labs/libevm/accounts/abi" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/vm" +) + +// A Contract is an implementation of a precompiled contract conforming to the +// EIP-165 interface ID 0xcacc24d1. +type Contract interface { + // Fallback implements a fallback function, called if the method selector + // fails to match any other method. + Fallback(vm.PrecompileEnvironment, []byte) ([]byte, error) + + // Echo implements the function with selector 0x34d6d9be: + // function Echo(uint256 ) view returns(uint256) + Echo(vm.PrecompileEnvironment, *big.Int) (*big.Int, error) + + // Echo0 implements the function with selector 0xdb84d7c0: + // function Echo(string ) view returns(string) + Echo0(vm.PrecompileEnvironment, string) (string, error) + + // Extract implements the function with selector 0xad8108a4: + // function Extract((int256) ) view returns(int256) + Extract(vm.PrecompileEnvironment, struct { + Val *big.Int "json:\"val\"" + }) (*big.Int, error) + + // HashPacked implements the function with selector 0xd7cc1f37: + // function HashPacked(uint256 , bytes2 , address ) view returns(bytes32) + HashPacked(vm.PrecompileEnvironment, *big.Int, [2]uint8, common.Address) ([32]uint8, error) + + // NeitherViewNorPure implements the function with selector 0xa7263000: + // function NeitherViewNorPure() returns(bool canReadState, bool canWriteState) + NeitherViewNorPure(vm.PrecompileEnvironment) (bool, bool, error) + + // NonPayable implements the function with selector 0x6fb1b0e9: + // function NonPayable() returns() + NonPayable(vm.PrecompileEnvironment) error + + // Payable implements the function with selector 0x1b7fb4b0: + // function Payable() payable returns(uint256 value) + Payable(vm.PrecompileEnvironment) (*big.Int, error) + + // Pure implements the function with selector 0xf5ad2fb8: + // function Pure() pure returns(bool canReadState, bool canWriteState) + Pure(vm.PrecompileEnvironment) (bool, bool, error) + + // RevertWith implements the function with selector 0xa93cbd97: + // function RevertWith(bytes ) returns() + RevertWith(vm.PrecompileEnvironment, []uint8) error + + // Self implements the function with selector 0xc62c692f: + // function Self() view returns(address) + Self(vm.PrecompileEnvironment) (common.Address, error) + + // View implements the function with selector 0x1686f265: + // function View() view returns(bool canReadState, bool canWriteState) + View(vm.PrecompileEnvironment) (bool, bool, error) +} + +type methodDispatcher = func(Contract, vm.PrecompileEnvironment, []byte) ([]byte, error) + +var ( + // Avoid unused-import errors. + _ *big.Int = nil + _ *common.Address = nil + + methods abi.MethodsBySelector + dispatchers map[abi.Selector]methodDispatcher +) + +const abiJSON = `[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"Echo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"Echo","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"int256","name":"val","type":"int256"}],"internalType":"struct IPrecompile.Wrapper","name":"","type":"tuple"}],"name":"Extract","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes2","name":"","type":"bytes2"},{"internalType":"address","name":"","type":"address"}],"name":"HashPacked","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NeitherViewNorPure","outputs":[{"internalType":"bool","name":"canReadState","type":"bool"},{"internalType":"bool","name":"canWriteState","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"NonPayable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"Payable","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"Pure","outputs":[{"internalType":"bool","name":"canReadState","type":"bool"},{"internalType":"bool","name":"canWriteState","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"RevertWith","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"Self","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"View","outputs":[{"internalType":"bool","name":"canReadState","type":"bool"},{"internalType":"bool","name":"canWriteState","type":"bool"}],"stateMutability":"view","type":"function"}]` + +func init() { + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + panic(err.Error()) + } + methods = parsed.MethodsBySelector() + + var d dispatch + dispatchers = map[abi.Selector]methodDispatcher{ + 0x34d6d9be: d.Echo, + 0xdb84d7c0: d.Echo0, + 0xad8108a4: d.Extract, + 0xd7cc1f37: d.HashPacked, + 0xa7263000: d.NeitherViewNorPure, + 0x6fb1b0e9: d.NonPayable, + 0x1b7fb4b0: d.Payable, + 0xf5ad2fb8: d.Pure, + 0xa93cbd97: d.RevertWith, + 0xc62c692f: d.Self, + 0x1686f265: d.View, + } +} + +// New returns a precompiled contract backed by the provided implementation. +func New(impl Contract) vm.PrecompiledContract { + return vm.NewStatefulPrecompile(precompile{impl}.run) +} + +type precompile struct { + impl Contract +} + +const revertBufferWhenNonPayableReceivesValue = "non-payable" + +func (p precompile) run(env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + selector, ok := methods.FindSelector(input) + if !ok { + return p.impl.Fallback(env, input) + } + + payable := false + switch m := methods[selector]; m.StateMutability { + case "payable": + payable = true + case "nonpayable": + case "pure": + env = env.AsPure() + case "view": + env = env.AsReadOnly() + default: + // If this happens then `precompilegen` needs to be extended because the + // Solidity ABI spec changed. + data := fmt.Sprintf("unsupported state mutability %q on method %s", m.StateMutability, m.Sig) + return []byte(data), vm.ErrExecutionReverted + } + if !payable && !env.Value().IsZero() { + return []byte(revertBufferWhenNonPayableReceivesValue), vm.ErrExecutionReverted + } + + return dispatchers[selector](p.impl, env, input) +} + +// dispatch is the type on which all method dispatchers are defined, stopping +// them from being exported as top-level identifiers that clutter package +// documentation. +type dispatch struct{} + +func (dispatch) Echo(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0x34d6d9be] + + inputs, err := method.Inputs.Unpack(input[abi.SelectorByteLen:]) + if err != nil { + return nil, err + } + i0 := inputs[0].(*big.Int) + + { + o0, err := impl.Echo(env, i0) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0) + } +} + +func (dispatch) Echo0(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xdb84d7c0] + + inputs, err := method.Inputs.Unpack(input[abi.SelectorByteLen:]) + if err != nil { + return nil, err + } + i0 := inputs[0].(string) + + { + o0, err := impl.Echo0(env, i0) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0) + } +} + +func (dispatch) Extract(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xad8108a4] + + inputs, err := method.Inputs.Unpack(input[abi.SelectorByteLen:]) + if err != nil { + return nil, err + } + i0 := inputs[0].(struct { + Val *big.Int "json:\"val\"" + }) + + { + o0, err := impl.Extract(env, i0) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0) + } +} + +func (dispatch) HashPacked(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xd7cc1f37] + + inputs, err := method.Inputs.Unpack(input[abi.SelectorByteLen:]) + if err != nil { + return nil, err + } + i0 := inputs[0].(*big.Int) + i1 := inputs[1].([2]uint8) + i2 := inputs[2].(common.Address) + + { + o0, err := impl.HashPacked(env, i0, i1, i2) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0) + } +} + +func (dispatch) NeitherViewNorPure(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xa7263000] + + { + o0, o1, err := impl.NeitherViewNorPure(env) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0, o1) + } +} + +func (dispatch) NonPayable(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + + { + err := impl.NonPayable(env) + if err != nil { + return nil, err + } + return nil, nil + } +} + +func (dispatch) Payable(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0x1b7fb4b0] + + { + o0, err := impl.Payable(env) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0) + } +} + +func (dispatch) Pure(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xf5ad2fb8] + + { + o0, o1, err := impl.Pure(env) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0, o1) + } +} + +func (dispatch) RevertWith(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xa93cbd97] + + inputs, err := method.Inputs.Unpack(input[abi.SelectorByteLen:]) + if err != nil { + return nil, err + } + i0 := inputs[0].([]uint8) + + { + err := impl.RevertWith(env, i0) + if err != nil { + return nil, err + } + return nil, nil + } +} + +func (dispatch) Self(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0xc62c692f] + + { + o0, err := impl.Self(env) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0) + } +} + +func (dispatch) View(impl Contract, env vm.PrecompileEnvironment, input []byte) ([]byte, error) { + method := methods[0x1686f265] + + { + o0, o1, err := impl.View(env) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(o0, o1) + } +} diff --git a/libevm/precompilegen/testprecompile/generated_test.go b/libevm/precompilegen/testprecompile/generated_test.go new file mode 100644 index 000000000000..bc20eb28ce32 --- /dev/null +++ b/libevm/precompilegen/testprecompile/generated_test.go @@ -0,0 +1,271 @@ +// Copyright 2024-2026 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +package testprecompile + +import ( + "context" + "fmt" + "math/big" + "testing" + + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/libevm/accounts/abi/bind" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/core/vm" + "github.com/ava-labs/libevm/crypto" + "github.com/ava-labs/libevm/eth/ethconfig" + "github.com/ava-labs/libevm/ethclient/simulated" + "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/libevm/ethtest" + "github.com/ava-labs/libevm/libevm/hookstest" + "github.com/ava-labs/libevm/node" + "github.com/ava-labs/libevm/params" +) + +// Note that the .abi and .bin files are .gitignored as only the generated Go +// files are necessary. +//go:generate solc -o ./ --overwrite --abi --bin IPrecompile.sol TestSuite.sol +//go:generate go run ../ -in IPrecompile.abi -out ./generated.go -package testprecompile +//go:generate go run ../../../cmd/abigen --abi TestSuite.abi --bin TestSuite.bin --pkg testprecompile --out ./suite.abigen_test.go --type TestSuite + +func successfulTxReceipt(ctx context.Context, tb testing.TB, client bind.DeployBackend, tx *types.Transaction) *types.Receipt { + tb.Helper() + r, err := bind.WaitMined(ctx, client, tx) + require.NoErrorf(tb, err, "bind.WaitMined(tx %#x)", tx.Hash()) + require.Equalf(tb, uint64(1), r.Status, "%T.Status", r) + return r +} + +func TestGeneratedPrecompile(t *testing.T) { + ctx := context.Background() + rng := ethtest.NewPseudoRand(424242) + precompile := rng.Address() + + hooks := &hookstest.Stub{ + PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{ + precompile: New(contract{}), + }, + } + extras := hookstest.Register(t, params.Extras[*hookstest.Stub, *hookstest.Stub]{ + NewRules: func(_ *params.ChainConfig, r *params.Rules, _ *hookstest.Stub, blockNum *big.Int, isMerge bool, timestamp uint64) *hookstest.Stub { + r.IsCancun = true // enable PUSH0 + return hooks + }, + }) + + key := rng.UnsafePrivateKey(t) + eoa := crypto.PubkeyToAddress(key.PublicKey) + + sim := simulated.NewBackend( + types.GenesisAlloc{ + eoa: types.Account{ + Balance: new(uint256.Int).Not(uint256.NewInt(0)).ToBig(), + }, + }, + func(nodeConf *node.Config, ethConf *ethconfig.Config) { + ethConf.Genesis.GasLimit = 30e6 + extras.ChainConfig.Set(ethConf.Genesis.Config, hooks) + }, + ) + defer sim.Close() + + txOpts, err := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + require.NoError(t, err, "bind.NewKeyedTransactorWithChainID(..., 1337)") + txOpts.GasLimit = 30e6 + txOpts.Value = big.NewInt(1e9) + + client := sim.Client() + _, tx, test, err := DeployTestSuite(txOpts, client, precompile, revertBufferWhenNonPayableReceivesValue) + require.NoError(t, err, "DeployTestSuite(...)") + sim.Commit() + successfulTxReceipt(ctx, t, client, tx) + + txOpts.Value = nil + suite := &TestSuiteSession{ + Contract: test, + TransactOpts: *txOpts, + } + + tests := []struct { + transact func() (*types.Transaction, error) + wantCalledEvent string + }{ + { + transact: func() (*types.Transaction, error) { + return suite.Echo(rng.BigUint64()) + }, + wantCalledEvent: "Echo(uint256)", + }, + { + transact: func() (*types.Transaction, error) { + return suite.Echo0("hello world") + }, + wantCalledEvent: "Echo(string)", + }, + { + transact: func() (*types.Transaction, error) { + return suite.Extract(IPrecompileWrapper{ + Val: rng.BigUint64(), + }) + }, + wantCalledEvent: "Extract(...)", + }, + { + transact: func() (*types.Transaction, error) { + return suite.HashPacked(rng.BigUint64(), [2]byte{42, 42}, rng.Address()) + }, + wantCalledEvent: "HashPacked(...)", + }, + { + transact: func() (*types.Transaction, error) { + return suite.Self() + }, + wantCalledEvent: "Self()", + }, + { + transact: func() (*types.Transaction, error) { + return suite.RevertWith(rng.Bytes(8)) + }, + wantCalledEvent: "RevertWith(...)", + }, + { + transact: func() (*types.Transaction, error) { + return suite.EchoingFallback(rng.Bytes(8)) + }, + wantCalledEvent: "EchoingFallback(...)", + }, + { + transact: func() (*types.Transaction, error) { + return suite.View() + }, + wantCalledEvent: "View()", + }, + { + transact: func() (*types.Transaction, error) { + return suite.Pure() + }, + wantCalledEvent: "Pure()", + }, + { + transact: func() (*types.Transaction, error) { + return suite.NeitherViewNorPure() + }, + wantCalledEvent: "NeitherViewNorPure()", + }, + { + transact: func() (*types.Transaction, error) { + return suite.Transfer() + }, + wantCalledEvent: "Transfer()", + }, + } + + for _, tt := range tests { + t.Run(tt.wantCalledEvent, func(t *testing.T) { + tx, err := tt.transact() + require.NoError(t, err, "send tx") + sim.Commit() + + rcpt := successfulTxReceipt(ctx, t, client, tx) + require.Equalf(t, uint64(1), rcpt.Status, "%T.Status (i.e. transaction executed without error)", rcpt) + + require.Lenf(t, rcpt.Logs, 1, "%T.Logs", rcpt) + called, err := test.ParseCalled(*rcpt.Logs[0]) + require.NoErrorf(t, err, "%T.ParseCalled(...)", test) + assert.Equal(t, tt.wantCalledEvent, called.Arg0, "function name emitted with `Called` event") + }) + } +} + +type contract struct{} + +var _ Contract = contract{} + +func (contract) Fallback(env vm.PrecompileEnvironment, callData []byte) ([]byte, error) { + // Note the test-suite assumption of the fallback's behaviour: + var _ = (*TestSuite).EchoingFallback + return callData, nil +} + +func (contract) Echo(env vm.PrecompileEnvironment, x *big.Int) (*big.Int, error) { + return x, nil +} + +func (contract) Echo0(env vm.PrecompileEnvironment, x string) (string, error) { + return x, nil +} + +func (contract) Extract(env vm.PrecompileEnvironment, x struct { + Val *big.Int "json:\"val\"" +}) (*big.Int, error) { + return x.Val, nil +} + +func (contract) HashPacked(env vm.PrecompileEnvironment, x *big.Int, y [2]byte, z common.Address) (hash [32]byte, _ error) { + copy( + hash[:], + crypto.Keccak256( + uint256.MustFromBig(x).PaddedBytes(32), + y[:], + z.Bytes(), + ), + ) + return hash, nil +} + +func (contract) RevertWith(env vm.PrecompileEnvironment, x []byte) error { + // Wrapping ensures that the implementation doesn't use type switching. + return fmt.Errorf("%w", vm.RevertError(x)) +} + +func (contract) Self(env vm.PrecompileEnvironment) (common.Address, error) { + return env.Addresses().EVMSemantic.Self, nil +} + +func canReadState(env vm.PrecompileEnvironment) bool { + _, ok := env.ReadOnlyState() + return ok +} + +func canWriteState(env vm.PrecompileEnvironment) bool { + _, err := env.StateDB() + return err == nil +} + +func (contract) View(env vm.PrecompileEnvironment) (bool, bool, error) { + return canReadState(env), canWriteState(env), nil +} + +func (contract) Pure(env vm.PrecompileEnvironment) (bool, bool, error) { + return canReadState(env), canWriteState(env), nil +} + +func (contract) NeitherViewNorPure(env vm.PrecompileEnvironment) (bool, bool, error) { + return canReadState(env), canWriteState(env), nil +} + +func (contract) Payable(env vm.PrecompileEnvironment) (*big.Int, error) { + return env.Value().ToBig(), nil +} + +func (contract) NonPayable(env vm.PrecompileEnvironment) error { + return nil +} diff --git a/libevm/precompilegen/testprecompile/suite.abigen_test.go b/libevm/precompilegen/testprecompile/suite.abigen_test.go new file mode 100644 index 000000000000..480cfc591f0e --- /dev/null +++ b/libevm/precompilegen/testprecompile/suite.abigen_test.go @@ -0,0 +1,573 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package testprecompile + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ava-labs/libevm" + "github.com/ava-labs/libevm/accounts/abi" + "github.com/ava-labs/libevm/accounts/abi/bind" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// IPrecompileWrapper is an auto generated low-level Go binding around an user-defined struct. +type IPrecompileWrapper struct { + Val *big.Int +} + +// TestSuiteMetaData contains all meta data concerning the TestSuite contract. +var TestSuiteMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"contractIPrecompile\",\"name\":\"_precompile\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"nonPayableErrorMsg\",\"type\":\"string\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"func\",\"type\":\"string\"}],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"Echo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"x\",\"type\":\"string\"}],\"name\":\"Echo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"input\",\"type\":\"bytes\"}],\"name\":\"EchoingFallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"int256\",\"name\":\"val\",\"type\":\"int256\"}],\"internalType\":\"structIPrecompile.Wrapper\",\"name\":\"x\",\"type\":\"tuple\"}],\"name\":\"Extract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"bytes2\",\"name\":\"y\",\"type\":\"bytes2\"},{\"internalType\":\"address\",\"name\":\"z\",\"type\":\"address\"}],\"name\":\"HashPacked\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"NeitherViewNorPure\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Pure\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"RevertWith\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Self\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Transfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"View\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a060405260405161236f38038061236f83398181016040528101906100259190610227565b8173ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050805f90816100679190610491565b505050610560565b5f604051905090565b5f5ffd5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100a982610080565b9050919050565b5f6100ba8261009f565b9050919050565b6100ca816100b0565b81146100d4575f5ffd5b50565b5f815190506100e5816100c1565b92915050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610139826100f3565b810181811067ffffffffffffffff8211171561015857610157610103565b5b80604052505050565b5f61016a61006f565b90506101768282610130565b919050565b5f67ffffffffffffffff82111561019557610194610103565b5b61019e826100f3565b9050602081019050919050565b8281835e5f83830152505050565b5f6101cb6101c68461017b565b610161565b9050828152602081018484840111156101e7576101e66100ef565b5b6101f28482856101ab565b509392505050565b5f82601f83011261020e5761020d6100eb565b5b815161021e8482602086016101b9565b91505092915050565b5f5f6040838503121561023d5761023c610078565b5b5f61024a858286016100d7565b925050602083015167ffffffffffffffff81111561026b5761026a61007c565b5b610277858286016101fa565b9150509250929050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806102cf57607f821691505b6020821081036102e2576102e161028b565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026103447fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610309565b61034e8683610309565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f61039261038d61038884610366565b61036f565b610366565b9050919050565b5f819050919050565b6103ab83610378565b6103bf6103b782610399565b848454610315565b825550505050565b5f5f905090565b6103d66103c7565b6103e18184846103a2565b505050565b5b81811015610404576103f95f826103ce565b6001810190506103e7565b5050565b601f8211156104495761041a816102e8565b610423846102fa565b81016020851015610432578190505b61044661043e856102fa565b8301826103e6565b50505b505050565b5f82821c905092915050565b5f6104695f198460080261044e565b1980831691505092915050565b5f610481838361045a565b9150826002028217905092915050565b61049a82610281565b67ffffffffffffffff8111156104b3576104b2610103565b5b6104bd82546102b8565b6104c8828285610408565b5f60209050601f8311600181146104f9575f84156104e7578287015190505b6104f18582610476565b865550610558565b601f198416610507866102e8565b5f5b8281101561052e57848901518255600182019150602085019450602081019050610509565b8683101561054b5784890151610547601f89168261045a565b8355505b6001600288020188555050505b505050505050565b608051611db16105be5f395f81816101d2015281816102b101528181610454015281816105b8015281816106f2015281816107e0015281816108170152818161092a01528181610a3501528181610b870152610d340152611db15ff3fe608060405234801561000f575f5ffd5b50600436106100a7575f3560e01c8063ad8108a41161006f578063ad8108a414610101578063b4a818081461011d578063c62c692f14610139578063d7cc1f3714610143578063db84d7c01461015f578063f5ad2fb81461017b576100a7565b80631686f265146100ab57806334d6d9be146100b5578063406dade3146100d1578063a7263000146100db578063a93cbd97146100e5575b5f5ffd5b6100b3610185565b005b6100cf60048036038101906100ca9190610e51565b6101cf565b005b6100d96102ae565b005b6100e3610406565b005b6100ff60048036038101906100fa9190610fb8565b610450565b005b61011b6004803603810190610116919061106f565b6105b6565b005b61013760048036038101906101329190610fb8565b610698565b005b6101416107de565b005b61015d60048036038101906101589190611149565b6108fd565b005b61017960048036038101906101749190611237565b610a0c565b005b610183610b3a565b005b610198631686f26560e01b60015f610b83565b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516101c5906112d8565b60405180910390a1565b807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166334d6d9be836040518263ffffffff1660e01b81526004016102299190611305565b602060405180830381865afa158015610244573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102689190611332565b146102765761027561135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516102a3906113d4565b60405180910390a150565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16631b7fb4b0602a6040518263ffffffff1660e01b815260040160206040518083038185885af115801561031b573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906103409190611332565b9050602a81146103535761035261135d565b5b5f636fb1b0e960e01b9050610377815f60405180602001604052805f815250610cc9565b5f5f60405160200161038991906114eb565b60405160208183030381529060405290506103a682600183610cc9565b6103b9631686f26560e01b600183610cc9565b6103cc63f5ad2fb860e01b600183610cc9565b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516103f99061154b565b60405180910390a1505050565b61041963a726300060e01b600180610b83565b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a604051610446906115b3565b60405180910390a1565b5f5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a93cbd9760e01b846040516024016104a29190611631565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161050c919061168b565b5f604051808303815f865af19150503d805f8114610545576040519150601f19603f3d011682016040523d82523d5f602084013e61054a565b606091505b5091509150811561055e5761055d61135d565b5b828051906020012081805190602001201461057c5761057b61135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516105a9906116eb565b60405180910390a1505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad8108a4826040518263ffffffff1660e01b815260040161060f9190611732565b602060405180830381865afa15801561062a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064e919061175f565b815f0151146106605761065f61135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a60405161068d906117d4565b60405180910390a150565b5f5f826040516024016106ab9190611631565b6040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1683604051610735919061168b565b5f60405180830381855afa9150503d805f811461076d576040519150601f19603f3d011682016040523d82523d5f602084013e610772565b606091505b5091509150816107855761078461135d565b5b82805190602001208180519060200120146107a3576107a261135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516107d09061183c565b60405180910390a150505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663c62c692f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561087e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108a2919061186e565b73ffffffffffffffffffffffffffffffffffffffff16146108c6576108c561135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516108f3906118e3565b60405180910390a1565b82828260405160200161091293929190611986565b604051602081830303815290604052805190602001207f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d7cc1f378585856040518463ffffffff1660e01b8152600401610985939291906119e0565b602060405180830381865afa1580156109a0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109c49190611a48565b146109d2576109d161135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a6040516109ff90611abd565b60405180910390a1505050565b80604051602001610a1d9190611b15565b604051602081830303815290604052805190602001207f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663db84d7c0836040518263ffffffff1660e01b8152600401610a8c9190611b63565b5f60405180830381865afa158015610aa6573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610ace9190611bf1565b604051602001610ade9190611b15565b6040516020818303038152906040528051906020012014610b0257610b0161135d565b5b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a604051610b2f90611c82565b60405180910390a150565b610b4c63f5ad2fb860e01b5f5f610b83565b7f3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a604051610b7990611cea565b60405180910390a1565b5f5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1685604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c2d919061168b565b5f604051808303815f865af19150503d805f8114610c66576040519150601f19603f3d011682016040523d82523d5f602084013e610c6b565b606091505b509150915081610c7e57610c7d61135d565b5b5f5f82806020019051810190610c949190611d3d565b9150915085151582151514610cac57610cab61135d565b5b84151581151514610cc057610cbf61135d565b5b50505050505050565b5f83604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168584604051610d78919061168b565b5f6040518083038185875af1925050503d805f8114610db2576040519150601f19603f3d011682016040523d82523d5f602084013e610db7565b606091505b50915091505f845103610dd75781610dd257610dd161135d565b5b610e05565b8115610de657610de561135d565b5b8380519060200120818051906020012014610e0457610e0361135d565b5b5b505050505050565b5f604051905090565b5f5ffd5b5f5ffd5b5f819050919050565b610e3081610e1e565b8114610e3a575f5ffd5b50565b5f81359050610e4b81610e27565b92915050565b5f60208284031215610e6657610e65610e16565b5b5f610e7384828501610e3d565b91505092915050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610eca82610e84565b810181811067ffffffffffffffff82111715610ee957610ee8610e94565b5b80604052505050565b5f610efb610e0d565b9050610f078282610ec1565b919050565b5f67ffffffffffffffff821115610f2657610f25610e94565b5b610f2f82610e84565b9050602081019050919050565b828183375f83830152505050565b5f610f5c610f5784610f0c565b610ef2565b905082815260208101848484011115610f7857610f77610e80565b5b610f83848285610f3c565b509392505050565b5f82601f830112610f9f57610f9e610e7c565b5b8135610faf848260208601610f4a565b91505092915050565b5f60208284031215610fcd57610fcc610e16565b5b5f82013567ffffffffffffffff811115610fea57610fe9610e1a565b5b610ff684828501610f8b565b91505092915050565b5f5ffd5b5f819050919050565b61101581611003565b811461101f575f5ffd5b50565b5f813590506110308161100c565b92915050565b5f6020828403121561104b5761104a610fff565b5b6110556020610ef2565b90505f61106484828501611022565b5f8301525092915050565b5f6020828403121561108457611083610e16565b5b5f61109184828501611036565b91505092915050565b5f7fffff00000000000000000000000000000000000000000000000000000000000082169050919050565b6110ce8161109a565b81146110d8575f5ffd5b50565b5f813590506110e9816110c5565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611118826110ef565b9050919050565b6111288161110e565b8114611132575f5ffd5b50565b5f813590506111438161111f565b92915050565b5f5f5f606084860312156111605761115f610e16565b5b5f61116d86828701610e3d565b935050602061117e868287016110db565b925050604061118f86828701611135565b9150509250925092565b5f67ffffffffffffffff8211156111b3576111b2610e94565b5b6111bc82610e84565b9050602081019050919050565b5f6111db6111d684611199565b610ef2565b9050828152602081018484840111156111f7576111f6610e80565b5b611202848285610f3c565b509392505050565b5f82601f83011261121e5761121d610e7c565b5b813561122e8482602086016111c9565b91505092915050565b5f6020828403121561124c5761124b610e16565b5b5f82013567ffffffffffffffff81111561126957611268610e1a565b5b6112758482850161120a565b91505092915050565b5f82825260208201905092915050565b7f56696577282900000000000000000000000000000000000000000000000000005f82015250565b5f6112c260068361127e565b91506112cd8261128e565b602082019050919050565b5f6020820190508181035f8301526112ef816112b6565b9050919050565b6112ff81610e1e565b82525050565b5f6020820190506113185f8301846112f6565b92915050565b5f8151905061132c81610e27565b92915050565b5f6020828403121561134757611346610e16565b5b5f6113548482850161131e565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b7f4563686f2875696e7432353629000000000000000000000000000000000000005f82015250565b5f6113be600d8361127e565b91506113c98261138a565b602082019050919050565b5f6020820190508181035f8301526113eb816113b2565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061143657607f821691505b602082108103611449576114486113f2565b5b50919050565b5f81905092915050565b5f819050815f5260205f209050919050565b5f81546114778161141f565b611481818661144f565b9450600182165f811461149b57600181146114b0576114e2565b60ff19831686528115158202860193506114e2565b6114b985611459565b5f5b838110156114da578154818901526001820191506020810190506114bb565b838801955050505b50505092915050565b5f6114f6828461146b565b915081905092915050565b7f5472616e736665722829000000000000000000000000000000000000000000005f82015250565b5f611535600a8361127e565b915061154082611501565b602082019050919050565b5f6020820190508181035f83015261156281611529565b9050919050565b7f4e656974686572566965774e6f725075726528290000000000000000000000005f82015250565b5f61159d60148361127e565b91506115a882611569565b602082019050919050565b5f6020820190508181035f8301526115ca81611591565b9050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f611603826115d1565b61160d81856115db565b935061161d8185602086016115eb565b61162681610e84565b840191505092915050565b5f6020820190508181035f83015261164981846115f9565b905092915050565b5f81905092915050565b5f611665826115d1565b61166f8185611651565b935061167f8185602086016115eb565b80840191505092915050565b5f611696828461165b565b915081905092915050565b7f52657665727457697468282e2e2e2900000000000000000000000000000000005f82015250565b5f6116d5600f8361127e565b91506116e0826116a1565b602082019050919050565b5f6020820190508181035f830152611702816116c9565b9050919050565b61171281611003565b82525050565b602082015f82015161172c5f850182611709565b50505050565b5f6020820190506117455f830184611718565b92915050565b5f815190506117598161100c565b92915050565b5f6020828403121561177457611773610e16565b5b5f6117818482850161174b565b91505092915050565b7f45787472616374282e2e2e2900000000000000000000000000000000000000005f82015250565b5f6117be600c8361127e565b91506117c98261178a565b602082019050919050565b5f6020820190508181035f8301526117eb816117b2565b9050919050565b7f4563686f696e6746616c6c6261636b282e2e2e290000000000000000000000005f82015250565b5f61182660148361127e565b9150611831826117f2565b602082019050919050565b5f6020820190508181035f8301526118538161181a565b9050919050565b5f815190506118688161111f565b92915050565b5f6020828403121561188357611882610e16565b5b5f6118908482850161185a565b91505092915050565b7f53656c66282900000000000000000000000000000000000000000000000000005f82015250565b5f6118cd60068361127e565b91506118d882611899565b602082019050919050565b5f6020820190508181035f8301526118fa816118c1565b9050919050565b5f819050919050565b61191b61191682610e1e565b611901565b82525050565b5f819050919050565b61193b6119368261109a565b611921565b82525050565b5f8160601b9050919050565b5f61195782611941565b9050919050565b5f6119688261194d565b9050919050565b61198061197b8261110e565b61195e565b82525050565b5f611991828661190a565b6020820191506119a1828561192a565b6002820191506119b1828461196f565b601482019150819050949350505050565b6119cb8161109a565b82525050565b6119da8161110e565b82525050565b5f6060820190506119f35f8301866112f6565b611a0060208301856119c2565b611a0d60408301846119d1565b949350505050565b5f819050919050565b611a2781611a15565b8114611a31575f5ffd5b50565b5f81519050611a4281611a1e565b92915050565b5f60208284031215611a5d57611a5c610e16565b5b5f611a6a84828501611a34565b91505092915050565b7f486173685061636b6564282e2e2e2900000000000000000000000000000000005f82015250565b5f611aa7600f8361127e565b9150611ab282611a73565b602082019050919050565b5f6020820190508181035f830152611ad481611a9b565b9050919050565b5f81519050919050565b5f611aef82611adb565b611af9818561144f565b9350611b098185602086016115eb565b80840191505092915050565b5f611b208284611ae5565b915081905092915050565b5f611b3582611adb565b611b3f818561127e565b9350611b4f8185602086016115eb565b611b5881610e84565b840191505092915050565b5f6020820190508181035f830152611b7b8184611b2b565b905092915050565b5f611b95611b9084611199565b610ef2565b905082815260208101848484011115611bb157611bb0610e80565b5b611bbc8482856115eb565b509392505050565b5f82601f830112611bd857611bd7610e7c565b5b8151611be8848260208601611b83565b91505092915050565b5f60208284031215611c0657611c05610e16565b5b5f82015167ffffffffffffffff811115611c2357611c22610e1a565b5b611c2f84828501611bc4565b91505092915050565b7f4563686f28737472696e672900000000000000000000000000000000000000005f82015250565b5f611c6c600c8361127e565b9150611c7782611c38565b602082019050919050565b5f6020820190508181035f830152611c9981611c60565b9050919050565b7f50757265282900000000000000000000000000000000000000000000000000005f82015250565b5f611cd460068361127e565b9150611cdf82611ca0565b602082019050919050565b5f6020820190508181035f830152611d0181611cc8565b9050919050565b5f8115159050919050565b611d1c81611d08565b8114611d26575f5ffd5b50565b5f81519050611d3781611d13565b92915050565b5f5f60408385031215611d5357611d52610e16565b5b5f611d6085828601611d29565b9250506020611d7185828601611d29565b915050925092905056fea2646970667358221220f0b141f0869405a047b6257eeea3f5957b650b4c10613fbb449d8778f8d6986764736f6c634300081c0033", +} + +// TestSuiteABI is the input ABI used to generate the binding from. +// Deprecated: Use TestSuiteMetaData.ABI instead. +var TestSuiteABI = TestSuiteMetaData.ABI + +// TestSuiteBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use TestSuiteMetaData.Bin instead. +var TestSuiteBin = TestSuiteMetaData.Bin + +// DeployTestSuite deploys a new Ethereum contract, binding an instance of TestSuite to it. +func DeployTestSuite(auth *bind.TransactOpts, backend bind.ContractBackend, _precompile common.Address, nonPayableErrorMsg string) (common.Address, *types.Transaction, *TestSuite, error) { + parsed, err := TestSuiteMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestSuiteBin), backend, _precompile, nonPayableErrorMsg) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &TestSuite{TestSuiteCaller: TestSuiteCaller{contract: contract}, TestSuiteTransactor: TestSuiteTransactor{contract: contract}, TestSuiteFilterer: TestSuiteFilterer{contract: contract}}, nil +} + +// TestSuite is an auto generated Go binding around an Ethereum contract. +type TestSuite struct { + TestSuiteCaller // Read-only binding to the contract + TestSuiteTransactor // Write-only binding to the contract + TestSuiteFilterer // Log filterer for contract events +} + +// TestSuiteCaller is an auto generated read-only Go binding around an Ethereum contract. +type TestSuiteCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestSuiteTransactor is an auto generated write-only Go binding around an Ethereum contract. +type TestSuiteTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestSuiteFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TestSuiteFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestSuiteSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TestSuiteSession struct { + Contract *TestSuite // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestSuiteCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TestSuiteCallerSession struct { + Contract *TestSuiteCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TestSuiteTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TestSuiteTransactorSession struct { + Contract *TestSuiteTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestSuiteRaw is an auto generated low-level Go binding around an Ethereum contract. +type TestSuiteRaw struct { + Contract *TestSuite // Generic contract binding to access the raw methods on +} + +// TestSuiteCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TestSuiteCallerRaw struct { + Contract *TestSuiteCaller // Generic read-only contract binding to access the raw methods on +} + +// TestSuiteTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TestSuiteTransactorRaw struct { + Contract *TestSuiteTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewTestSuite creates a new instance of TestSuite, bound to a specific deployed contract. +func NewTestSuite(address common.Address, backend bind.ContractBackend) (*TestSuite, error) { + contract, err := bindTestSuite(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &TestSuite{TestSuiteCaller: TestSuiteCaller{contract: contract}, TestSuiteTransactor: TestSuiteTransactor{contract: contract}, TestSuiteFilterer: TestSuiteFilterer{contract: contract}}, nil +} + +// NewTestSuiteCaller creates a new read-only instance of TestSuite, bound to a specific deployed contract. +func NewTestSuiteCaller(address common.Address, caller bind.ContractCaller) (*TestSuiteCaller, error) { + contract, err := bindTestSuite(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TestSuiteCaller{contract: contract}, nil +} + +// NewTestSuiteTransactor creates a new write-only instance of TestSuite, bound to a specific deployed contract. +func NewTestSuiteTransactor(address common.Address, transactor bind.ContractTransactor) (*TestSuiteTransactor, error) { + contract, err := bindTestSuite(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TestSuiteTransactor{contract: contract}, nil +} + +// NewTestSuiteFilterer creates a new log filterer instance of TestSuite, bound to a specific deployed contract. +func NewTestSuiteFilterer(address common.Address, filterer bind.ContractFilterer) (*TestSuiteFilterer, error) { + contract, err := bindTestSuite(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TestSuiteFilterer{contract: contract}, nil +} + +// bindTestSuite binds a generic wrapper to an already deployed contract. +func bindTestSuite(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TestSuiteMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestSuite *TestSuiteRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestSuite.Contract.TestSuiteCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestSuite *TestSuiteRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.Contract.TestSuiteTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestSuite *TestSuiteRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestSuite.Contract.TestSuiteTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestSuite *TestSuiteCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestSuite.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestSuite *TestSuiteTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestSuite *TestSuiteTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestSuite.Contract.contract.Transact(opts, method, params...) +} + +// Echo is a paid mutator transaction binding the contract method 0x34d6d9be. +// +// Solidity: function Echo(uint256 x) returns() +func (_TestSuite *TestSuiteTransactor) Echo(opts *bind.TransactOpts, x *big.Int) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "Echo", x) +} + +// Echo is a paid mutator transaction binding the contract method 0x34d6d9be. +// +// Solidity: function Echo(uint256 x) returns() +func (_TestSuite *TestSuiteSession) Echo(x *big.Int) (*types.Transaction, error) { + return _TestSuite.Contract.Echo(&_TestSuite.TransactOpts, x) +} + +// Echo is a paid mutator transaction binding the contract method 0x34d6d9be. +// +// Solidity: function Echo(uint256 x) returns() +func (_TestSuite *TestSuiteTransactorSession) Echo(x *big.Int) (*types.Transaction, error) { + return _TestSuite.Contract.Echo(&_TestSuite.TransactOpts, x) +} + +// Echo0 is a paid mutator transaction binding the contract method 0xdb84d7c0. +// +// Solidity: function Echo(string x) returns() +func (_TestSuite *TestSuiteTransactor) Echo0(opts *bind.TransactOpts, x string) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "Echo0", x) +} + +// Echo0 is a paid mutator transaction binding the contract method 0xdb84d7c0. +// +// Solidity: function Echo(string x) returns() +func (_TestSuite *TestSuiteSession) Echo0(x string) (*types.Transaction, error) { + return _TestSuite.Contract.Echo0(&_TestSuite.TransactOpts, x) +} + +// Echo0 is a paid mutator transaction binding the contract method 0xdb84d7c0. +// +// Solidity: function Echo(string x) returns() +func (_TestSuite *TestSuiteTransactorSession) Echo0(x string) (*types.Transaction, error) { + return _TestSuite.Contract.Echo0(&_TestSuite.TransactOpts, x) +} + +// EchoingFallback is a paid mutator transaction binding the contract method 0xb4a81808. +// +// Solidity: function EchoingFallback(bytes input) returns() +func (_TestSuite *TestSuiteTransactor) EchoingFallback(opts *bind.TransactOpts, input []byte) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "EchoingFallback", input) +} + +// EchoingFallback is a paid mutator transaction binding the contract method 0xb4a81808. +// +// Solidity: function EchoingFallback(bytes input) returns() +func (_TestSuite *TestSuiteSession) EchoingFallback(input []byte) (*types.Transaction, error) { + return _TestSuite.Contract.EchoingFallback(&_TestSuite.TransactOpts, input) +} + +// EchoingFallback is a paid mutator transaction binding the contract method 0xb4a81808. +// +// Solidity: function EchoingFallback(bytes input) returns() +func (_TestSuite *TestSuiteTransactorSession) EchoingFallback(input []byte) (*types.Transaction, error) { + return _TestSuite.Contract.EchoingFallback(&_TestSuite.TransactOpts, input) +} + +// Extract is a paid mutator transaction binding the contract method 0xad8108a4. +// +// Solidity: function Extract((int256) x) returns() +func (_TestSuite *TestSuiteTransactor) Extract(opts *bind.TransactOpts, x IPrecompileWrapper) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "Extract", x) +} + +// Extract is a paid mutator transaction binding the contract method 0xad8108a4. +// +// Solidity: function Extract((int256) x) returns() +func (_TestSuite *TestSuiteSession) Extract(x IPrecompileWrapper) (*types.Transaction, error) { + return _TestSuite.Contract.Extract(&_TestSuite.TransactOpts, x) +} + +// Extract is a paid mutator transaction binding the contract method 0xad8108a4. +// +// Solidity: function Extract((int256) x) returns() +func (_TestSuite *TestSuiteTransactorSession) Extract(x IPrecompileWrapper) (*types.Transaction, error) { + return _TestSuite.Contract.Extract(&_TestSuite.TransactOpts, x) +} + +// HashPacked is a paid mutator transaction binding the contract method 0xd7cc1f37. +// +// Solidity: function HashPacked(uint256 x, bytes2 y, address z) returns() +func (_TestSuite *TestSuiteTransactor) HashPacked(opts *bind.TransactOpts, x *big.Int, y [2]byte, z common.Address) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "HashPacked", x, y, z) +} + +// HashPacked is a paid mutator transaction binding the contract method 0xd7cc1f37. +// +// Solidity: function HashPacked(uint256 x, bytes2 y, address z) returns() +func (_TestSuite *TestSuiteSession) HashPacked(x *big.Int, y [2]byte, z common.Address) (*types.Transaction, error) { + return _TestSuite.Contract.HashPacked(&_TestSuite.TransactOpts, x, y, z) +} + +// HashPacked is a paid mutator transaction binding the contract method 0xd7cc1f37. +// +// Solidity: function HashPacked(uint256 x, bytes2 y, address z) returns() +func (_TestSuite *TestSuiteTransactorSession) HashPacked(x *big.Int, y [2]byte, z common.Address) (*types.Transaction, error) { + return _TestSuite.Contract.HashPacked(&_TestSuite.TransactOpts, x, y, z) +} + +// NeitherViewNorPure is a paid mutator transaction binding the contract method 0xa7263000. +// +// Solidity: function NeitherViewNorPure() returns() +func (_TestSuite *TestSuiteTransactor) NeitherViewNorPure(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "NeitherViewNorPure") +} + +// NeitherViewNorPure is a paid mutator transaction binding the contract method 0xa7263000. +// +// Solidity: function NeitherViewNorPure() returns() +func (_TestSuite *TestSuiteSession) NeitherViewNorPure() (*types.Transaction, error) { + return _TestSuite.Contract.NeitherViewNorPure(&_TestSuite.TransactOpts) +} + +// NeitherViewNorPure is a paid mutator transaction binding the contract method 0xa7263000. +// +// Solidity: function NeitherViewNorPure() returns() +func (_TestSuite *TestSuiteTransactorSession) NeitherViewNorPure() (*types.Transaction, error) { + return _TestSuite.Contract.NeitherViewNorPure(&_TestSuite.TransactOpts) +} + +// Pure is a paid mutator transaction binding the contract method 0xf5ad2fb8. +// +// Solidity: function Pure() returns() +func (_TestSuite *TestSuiteTransactor) Pure(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "Pure") +} + +// Pure is a paid mutator transaction binding the contract method 0xf5ad2fb8. +// +// Solidity: function Pure() returns() +func (_TestSuite *TestSuiteSession) Pure() (*types.Transaction, error) { + return _TestSuite.Contract.Pure(&_TestSuite.TransactOpts) +} + +// Pure is a paid mutator transaction binding the contract method 0xf5ad2fb8. +// +// Solidity: function Pure() returns() +func (_TestSuite *TestSuiteTransactorSession) Pure() (*types.Transaction, error) { + return _TestSuite.Contract.Pure(&_TestSuite.TransactOpts) +} + +// RevertWith is a paid mutator transaction binding the contract method 0xa93cbd97. +// +// Solidity: function RevertWith(bytes err) returns() +func (_TestSuite *TestSuiteTransactor) RevertWith(opts *bind.TransactOpts, err []byte) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "RevertWith", err) +} + +// RevertWith is a paid mutator transaction binding the contract method 0xa93cbd97. +// +// Solidity: function RevertWith(bytes err) returns() +func (_TestSuite *TestSuiteSession) RevertWith(err []byte) (*types.Transaction, error) { + return _TestSuite.Contract.RevertWith(&_TestSuite.TransactOpts, err) +} + +// RevertWith is a paid mutator transaction binding the contract method 0xa93cbd97. +// +// Solidity: function RevertWith(bytes err) returns() +func (_TestSuite *TestSuiteTransactorSession) RevertWith(err []byte) (*types.Transaction, error) { + return _TestSuite.Contract.RevertWith(&_TestSuite.TransactOpts, err) +} + +// Self is a paid mutator transaction binding the contract method 0xc62c692f. +// +// Solidity: function Self() returns() +func (_TestSuite *TestSuiteTransactor) Self(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "Self") +} + +// Self is a paid mutator transaction binding the contract method 0xc62c692f. +// +// Solidity: function Self() returns() +func (_TestSuite *TestSuiteSession) Self() (*types.Transaction, error) { + return _TestSuite.Contract.Self(&_TestSuite.TransactOpts) +} + +// Self is a paid mutator transaction binding the contract method 0xc62c692f. +// +// Solidity: function Self() returns() +func (_TestSuite *TestSuiteTransactorSession) Self() (*types.Transaction, error) { + return _TestSuite.Contract.Self(&_TestSuite.TransactOpts) +} + +// Transfer is a paid mutator transaction binding the contract method 0x406dade3. +// +// Solidity: function Transfer() returns() +func (_TestSuite *TestSuiteTransactor) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "Transfer") +} + +// Transfer is a paid mutator transaction binding the contract method 0x406dade3. +// +// Solidity: function Transfer() returns() +func (_TestSuite *TestSuiteSession) Transfer() (*types.Transaction, error) { + return _TestSuite.Contract.Transfer(&_TestSuite.TransactOpts) +} + +// Transfer is a paid mutator transaction binding the contract method 0x406dade3. +// +// Solidity: function Transfer() returns() +func (_TestSuite *TestSuiteTransactorSession) Transfer() (*types.Transaction, error) { + return _TestSuite.Contract.Transfer(&_TestSuite.TransactOpts) +} + +// View is a paid mutator transaction binding the contract method 0x1686f265. +// +// Solidity: function View() returns() +func (_TestSuite *TestSuiteTransactor) View(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestSuite.contract.Transact(opts, "View") +} + +// View is a paid mutator transaction binding the contract method 0x1686f265. +// +// Solidity: function View() returns() +func (_TestSuite *TestSuiteSession) View() (*types.Transaction, error) { + return _TestSuite.Contract.View(&_TestSuite.TransactOpts) +} + +// View is a paid mutator transaction binding the contract method 0x1686f265. +// +// Solidity: function View() returns() +func (_TestSuite *TestSuiteTransactorSession) View() (*types.Transaction, error) { + return _TestSuite.Contract.View(&_TestSuite.TransactOpts) +} + +// TestSuiteCalledIterator is returned from FilterCalled and is used to iterate over the raw logs and unpacked data for Called events raised by the TestSuite contract. +type TestSuiteCalledIterator struct { + Event *TestSuiteCalled // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TestSuiteCalledIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TestSuiteCalled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TestSuiteCalled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TestSuiteCalledIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TestSuiteCalledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TestSuiteCalled represents a Called event raised by the TestSuite contract. +type TestSuiteCalled struct { + Arg0 string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCalled is a free log retrieval operation binding the contract event 0x3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a. +// +// Solidity: event Called(string func) +func (_TestSuite *TestSuiteFilterer) FilterCalled(opts *bind.FilterOpts) (*TestSuiteCalledIterator, error) { + + logs, sub, err := _TestSuite.contract.FilterLogs(opts, "Called") + if err != nil { + return nil, err + } + return &TestSuiteCalledIterator{contract: _TestSuite.contract, event: "Called", logs: logs, sub: sub}, nil +} + +// WatchCalled is a free log subscription operation binding the contract event 0x3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a. +// +// Solidity: event Called(string func) +func (_TestSuite *TestSuiteFilterer) WatchCalled(opts *bind.WatchOpts, sink chan<- *TestSuiteCalled) (event.Subscription, error) { + + logs, sub, err := _TestSuite.contract.WatchLogs(opts, "Called") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TestSuiteCalled) + if err := _TestSuite.contract.UnpackLog(event, "Called", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCalled is a log parse operation binding the contract event 0x3fd5724095fcb65d3b7e6d79ac130db7e9dbf891d59bb0da292d471bc40d564a. +// +// Solidity: event Called(string func) +func (_TestSuite *TestSuiteFilterer) ParseCalled(log types.Log) (*TestSuiteCalled, error) { + event := new(TestSuiteCalled) + if err := _TestSuite.contract.UnpackLog(event, "Called", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/libevm/precompiles/parallel/parallel_test.go b/libevm/precompiles/parallel/parallel_test.go index a8eb1a860436..10d5357d41ce 100644 --- a/libevm/precompiles/parallel/parallel_test.go +++ b/libevm/precompiles/parallel/parallel_test.go @@ -119,7 +119,11 @@ var _ PrecompileResult = recorded{} func (r recorded) PrecompileOutput(env vm.PrecompileEnvironment, input []byte) ([]byte, error) { l := r.asLog() l.Address = env.Addresses().EVMSemantic.Self - env.StateDB().AddLog(l) + sdb, err := env.StateDB() + if err != nil { + return nil, err + } + sdb.AddLog(l) return r.precompileReturnData(), nil } diff --git a/libevm/precompiles/parallel/precompile.go b/libevm/precompiles/parallel/precompile.go index b4436b510d42..3fbe3891f71c 100644 --- a/libevm/precompiles/parallel/precompile.go +++ b/libevm/precompiles/parallel/precompile.go @@ -36,9 +36,13 @@ func AddAsPrecompile[CD, D any, R PrecompileResult, A any](p *Processor, h Handl results := AddHandler(p, h) return func(env vm.PrecompileEnvironment, input []byte) ([]byte, error) { - res, ok := results(env.ReadOnlyState().TxIndex()) + // TODO(arr4n) add revert data to match Solidity-style errors. + sdb, ok := env.ReadOnlyState() + if !ok { + return nil, vm.ErrExecutionReverted + } + res, ok := results(sdb.TxIndex()) if !ok { - // TODO(arr4n) add revert data to match a Solidity-style error return nil, vm.ErrExecutionReverted } return res.Result.PrecompileOutput(env, input) diff --git a/libevm/reentrancy/guard.go b/libevm/reentrancy/guard.go index ac5206359fcc..c0940f5323b3 100644 --- a/libevm/reentrancy/guard.go +++ b/libevm/reentrancy/guard.go @@ -39,14 +39,17 @@ var slotPreimagePrefix = []byte("libevm-reentrancy-guard-") // Contract equality is defined as the [libevm.AddressContext] "self" address // being the same under EVM semantics. func Guard(env vm.PrecompileEnvironment, key []byte) error { - if env.ReadOnly() { + if env.StateMutability() != vm.MutableState { return vm.ErrWriteProtection } self := env.Addresses().EVMSemantic.Self slot := crypto.Keccak256Hash(slotPreimagePrefix, key) - sdb := env.StateDB() + sdb, err := env.StateDB() + if err != nil { + return err + } if sdb.GetTransientState(self, slot) != (common.Hash{}) { return vm.ErrExecutionReverted } diff --git a/libevm/reentrancy/guard_test.go b/libevm/reentrancy/guard_test.go index ac8ec8cd8225..2845462c0add 100644 --- a/libevm/reentrancy/guard_test.go +++ b/libevm/reentrancy/guard_test.go @@ -97,12 +97,12 @@ func (s *envStub) Addresses() *libevm.AddressContext { } } -func (*envStub) ReadOnly() bool { - return false +func (*envStub) StateMutability() vm.StateMutability { + return vm.MutableState } -func (s *envStub) StateDB() vm.StateDB { - return s.db +func (s *envStub) StateDB() (vm.StateDB, error) { + return s.db, nil } func TestGuard(t *testing.T) {