diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index db48851ef50c..3287a7c9c328 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -30,6 +30,13 @@ jobs: EXCLUDE_REGEX: 'ava-labs/libevm/(accounts/usbwallet/trezor)$' runs-on: ubuntu-latest steps: + - name: Install solc + run: | + sudo add-apt-repository ppa:ethereum/ethereum + sudo apt update + sudo apt list -a solc + sudo apt install solc=1:0.8.28-0ubuntu1~noble + - 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/vm/contracts.libevm.go b/core/vm/contracts.libevm.go index 66e3c91783a0..183c86ca2ce1 100644 --- a/core/vm/contracts.libevm.go +++ b/core/vm/contracts.libevm.go @@ -57,10 +57,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 { @@ -88,6 +88,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) { @@ -96,7 +122,7 @@ func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, e return p.Run(input) case statefulPrecompile: env := args.env() - ret, err := p(env, input) + ret, err := p(env, common.CopyBytes(input)) args.gasRemaining = env.Gas() return ret, err } @@ -141,14 +167,28 @@ 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 will be non-nil i.f.f StateMutability() returns [MutableState]. StateDB() StateDB - // ReadOnlyState will always be non-nil. + // ReadOnlyState will be non-nil i.f.f. StateMutability() does not return + // [Pure]. ReadOnlyState() libevm.StateReader + // 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) @@ -210,3 +250,21 @@ var ( (*EVM)(nil).StaticCall, } ) + +// A RevertError is an error that couples [ErrExecutionReverted] with the EVM +// return buffer. Although not used in vanilla geth, it can be returned by a +// libevm `precompilegen` method implementation to circumvent regular argument +// packing. +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) } + +// Is returns true if `err` is directly == to `e` or if `err` is +// [ErrExecutionReverted]. +func (e RevertError) Is(err error) bool { + return error(e) == err || err == ErrExecutionReverted +} diff --git a/core/vm/contracts.libevm_test.go b/core/vm/contracts.libevm_test.go index 1bb98fac2fa0..a547c149f030 100644 --- a/core/vm/contracts.libevm_test.go +++ b/core/vm/contracts.libevm_test.go @@ -108,7 +108,7 @@ type statefulPrecompileOutput struct { Addresses *libevm.AddressContext StateValue common.Hash ValueReceived *uint256.Int - ReadOnly bool + Mutability vm.StateMutability BlockNumber, Difficulty *big.Int BlockTime uint64 Input []byte @@ -149,9 +149,6 @@ 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 @@ -162,7 +159,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Addresses: env.Addresses(), StateValue: env.ReadOnlyState().GetState(precompile, slot), ValueReceived: env.Value(), - ReadOnly: env.ReadOnly(), + Mutability: env.StateMutability(), BlockNumber: env.BlockNumber(), BlockTime: env.BlockTime(), Difficulty: hdr.Difficulty, @@ -216,8 +213,8 @@ func TestNewStatefulPrecompile(t *testing.T) { wantTransferValue *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()", @@ -229,7 +226,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Caller: caller, Self: precompile, }, - wantReadOnly: false, + wantMutability: vm.MutableState, wantTransferValue: transferValue, wantCallType: vm.Call, }, @@ -243,7 +240,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Caller: caller, Self: caller, }, - wantReadOnly: false, + wantMutability: vm.MutableState, wantTransferValue: transferValue, wantCallType: vm.CallCode, }, @@ -257,7 +254,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Caller: eoa, // inherited from caller Self: caller, }, - wantReadOnly: false, + wantMutability: vm.MutableState, wantTransferValue: uint256.NewInt(0), wantCallType: vm.DelegateCall, }, @@ -271,7 +268,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Caller: caller, Self: precompile, }, - wantReadOnly: true, + wantMutability: vm.ReadOnlyState, wantTransferValue: uint256.NewInt(0), wantCallType: vm.StaticCall, }, @@ -284,7 +281,7 @@ func TestNewStatefulPrecompile(t *testing.T) { Addresses: tt.wantAddresses, StateValue: stateValue, ValueReceived: tt.wantTransferValue, - ReadOnly: tt.wantReadOnly, + Mutability: tt.wantMutability, BlockNumber: header.Number, BlockTime: header.Time, Difficulty: header.Difficulty, @@ -334,7 +331,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 @@ -560,9 +557,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 }), @@ -591,7 +588,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: sut, Self: dest, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -603,7 +601,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: caller, // overridden by CallOption Self: dest, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -614,7 +613,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: caller, // SUT runs as its own caller because of CALLCODE Self: dest, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -626,7 +626,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: caller, // CallOption is a NOOP Self: dest, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -637,7 +638,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: caller, // as with CALLCODE Self: dest, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -649,7 +651,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: caller, // CallOption is a NOOP Self: dest, }, - Input: precompileCallData, + Input: precompileCallData, + Mutability: vm.MutableState, }, }, { @@ -665,7 +668,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, }, }, { @@ -677,8 +680,8 @@ func TestPrecompileMakeCall(t *testing.T) { Caller: caller, // overridden by CallOption Self: dest, }, - Input: precompileCallData, - ReadOnly: true, + Input: precompileCallData, + Mutability: vm.ReadOnlyState, }, }, } @@ -764,3 +767,76 @@ func ExamplePrecompileEnvironment() { // variable to include it in this example function. _ = actualCaller } + +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) { + tests := []struct { + name string + env vm.PrecompileEnvironment + want vm.StateMutability + }{ + { + 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, + }, + } + + 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) { + assert.Equal(t, tt.want, env.StateMutability(), "env.StateMutability()") + assert.Equal(t, env.StateDB() != nil, tt.want == vm.MutableState, "env.StateDB() != nil i.f.f. MutableState") + assert.Equal(t, env.ReadOnlyState() != nil, tt.want != vm.Pure, "env.ReadOnlyState() != nil i.f.f !Pure") + }) + + 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().Self, "%T preserved", env.Addresses()) + assert.Equalf(t, vm.Call, 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, + })) + got, _, err := evm.Call(vm.AccountRef{}, precompileAddr, nil, 0, uint256.NewInt(0)) + if got, want := string(got), precompileReturn; err != nil || got != want { + t.Errorf("%T.Call([precompile]) got {%q, %v}; want {%q, nil}", evm, got, err, want) + } +} diff --git a/core/vm/environment.libevm.go b/core/vm/environment.libevm.go index f2b61169d26e..79fe4d09f0cb 100644 --- a/core/vm/environment.libevm.go +++ b/core/vm/environment.libevm.go @@ -17,6 +17,7 @@ package vm import ( + "errors" "fmt" "math/big" @@ -33,21 +34,27 @@ import ( var _ PrecompileEnvironment = (*environment)(nil) type environment struct { - evm *EVM - self *Contract - callType CallType + evm *EVM + self *Contract + callType CallType + view, pure bool +} + +func (e *environment) copy() *environment { + // This deliberately does not use field names so that a change in the fields + // will break this code and force it to be reviewed. + return &environment{e.evm, e.self, e.callType, e.view, 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) refundGas(add uint64) error { gas, overflow := math.SafeAdd(e.self.Gas, add) @@ -58,37 +65,61 @@ func (e *environment) refundGas(add uint64) error { return nil } -func (e *environment) ReadOnly() bool { +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 { // A switch statement provides clearer code coverage for difficult-to-test // cases. switch { + // cases MUST be ordered from most to least restrictive + case e.pure: + return Pure case e.callType == StaticCall: // evm.interpreter.readOnly is only set to true via a call to // EVMInterpreter.Run() so, if a precompile is called directly with // StaticCall(), then readOnly might not be set yet. - return true + return ReadOnlyState case e.evm.interpreter.readOnly: - return true + return ReadOnlyState + case e.view: + return ReadOnlyState default: - return false + return MutableState } } -func (e *environment) Addresses() *libevm.AddressContext { - return &libevm.AddressContext{ - Origin: e.evm.Origin, - Caller: e.self.CallerAddress, - Self: e.self.Address(), +func (e *environment) ReadOnlyState() libevm.StateReader { + if e.pure { + return nil } + return e.evm.StateDB } func (e *environment) StateDB() StateDB { - if e.ReadOnly() { + if e.StateMutability() != MutableState { return nil } return e.evm.StateDB } +func (e *environment) Addresses() *libevm.AddressContext { + return &libevm.AddressContext{ + Origin: e.evm.Origin, + Caller: e.self.CallerAddress, + Self: e.self.Address(), + } +} + func (e *environment) BlockHeader() (types.Header, error) { hdr := e.evm.Context.Header if hdr == nil { @@ -105,7 +136,13 @@ 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") + func (e *environment) callContract(typ CallType, addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) (retData []byte, retErr error) { + if e.StateMutability() == Pure { + return nil, errPureFunctionMakeCall + } + // Depth and read-only setting are handled by [EVMInterpreter.Run], which // isn't used for precompiles, so we need to do it ourselves to maintain the // expected invariants. @@ -114,7 +151,7 @@ func (e *environment) callContract(typ CallType, addr common.Address, input []by in.evm.depth++ defer func() { in.evm.depth-- }() - if e.ReadOnly() && !in.readOnly { // i.e. the precompile was StaticCall()ed + if e.StateMutability() != MutableState && !in.readOnly { // i.e. the precompile was StaticCall()ed in.readOnly = true defer func() { in.readOnly = false }() } 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/hookstest/stub.go b/libevm/hookstest/stub.go index 1e37aa3f940d..40cd5c185740 100644 --- a/libevm/hookstest/stub.go +++ b/libevm/hookstest/stub.go @@ -19,6 +19,7 @@ package hookstest import ( + "encoding/json" "math/big" "testing" @@ -126,3 +127,11 @@ var _ interface { params.ChainConfigHooks params.RulesHooks } = Stub{} + +// MarshalJSON implements [json.Marshaler], always returning the empty JSON +// object. +func (*Stub) MarshalJSON() ([]byte, error) { + return []byte(`{}`), nil +} + +var _ json.Marshaler = (*Stub)(nil) 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..8633a673a6e0 --- /dev/null +++ b/libevm/precompilegen/precompile.go.tmpl @@ -0,0 +1,137 @@ +// 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 + } + + ret, err := dispatchers[selector](p.impl, env, input) + switch err := err.(type) { + case nil: + return ret, nil + case vm.RevertError: + return err.Bytes(), vm.ErrExecutionReverted + default: + return nil, err + } +} + +// 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..e00f83efa138 --- /dev/null +++ b/libevm/precompilegen/testprecompile/generated.go @@ -0,0 +1,331 @@ +// 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 + } + + ret, err := dispatchers[selector](p.impl, env, input) + switch err := err.(type) { + case nil: + return ret, nil + case vm.RevertError: + return err.Bytes(), vm.ErrExecutionReverted + default: + return nil, err + } +} + +// 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..42d400a8bc83 --- /dev/null +++ b/libevm/precompilegen/testprecompile/generated_test.go @@ -0,0 +1,267 @@ +// 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 testprecompile + +import ( + "context" + "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 { + return vm.RevertError(x) +} + +func (contract) Self(env vm.PrecompileEnvironment) (common.Address, error) { + return env.Addresses().Self, nil +} + +func canReadState(env vm.PrecompileEnvironment) bool { + return env.ReadOnlyState() != nil +} + +func canWriteState(env vm.PrecompileEnvironment) bool { + return env.StateDB() != 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 +}