From 735d44ba32b14262cd397140df33903f47551bd4 Mon Sep 17 00:00:00 2001 From: Robert Pirtle Date: Mon, 3 Apr 2023 09:58:45 -0700 Subject: [PATCH] support initialization of evm state in e2e tests (#1524) * check receipt status for failed txs from evm * make EvmSigner's Auth public * setup evm state initialization for e2e * add a dummy Greeter contract, deployed on start * move WaitForEvmTxReceipt to from account to util * add tests for interacting with the contract * add ContractAddrs map to Chain --- tests/e2e/contracts/greeter/Greeter.abi | 1 + tests/e2e/contracts/greeter/Greeter.bin | 1 + tests/e2e/contracts/greeter/Greeter.sol | 18 ++ tests/e2e/contracts/greeter/main.go | 255 ++++++++++++++++++++++++ tests/e2e/contracts/readme.md | 24 +++ tests/e2e/e2e_evm_contracts_test.go | 37 ++++ tests/e2e/kvtool | 2 +- tests/e2e/testutil/account.go | 26 +-- tests/e2e/testutil/chain.go | 11 +- tests/e2e/testutil/init_evm.go | 18 ++ tests/e2e/testutil/suite.go | 2 + tests/util/evmsigner.go | 44 +++- 12 files changed, 411 insertions(+), 28 deletions(-) create mode 100644 tests/e2e/contracts/greeter/Greeter.abi create mode 100644 tests/e2e/contracts/greeter/Greeter.bin create mode 100644 tests/e2e/contracts/greeter/Greeter.sol create mode 100644 tests/e2e/contracts/greeter/main.go create mode 100644 tests/e2e/contracts/readme.md create mode 100644 tests/e2e/e2e_evm_contracts_test.go create mode 100644 tests/e2e/testutil/init_evm.go diff --git a/tests/e2e/contracts/greeter/Greeter.abi b/tests/e2e/contracts/greeter/Greeter.abi new file mode 100644 index 00000000..fe4d5103 --- /dev/null +++ b/tests/e2e/contracts/greeter/Greeter.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/tests/e2e/contracts/greeter/Greeter.bin b/tests/e2e/contracts/greeter/Greeter.bin new file mode 100644 index 00000000..10bb71bb --- /dev/null +++ b/tests/e2e/contracts/greeter/Greeter.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b5060405162000bf238038062000bf28339818101604052810190620000379190620001e3565b80600090816200004891906200047f565b505062000566565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000b9826200006e565b810181811067ffffffffffffffff82111715620000db57620000da6200007f565b5b80604052505050565b6000620000f062000050565b9050620000fe8282620000ae565b919050565b600067ffffffffffffffff8211156200012157620001206200007f565b5b6200012c826200006e565b9050602081019050919050565b60005b83811015620001595780820151818401526020810190506200013c565b60008484015250505050565b60006200017c620001768462000103565b620000e4565b9050828152602081018484840111156200019b576200019a62000069565b5b620001a884828562000139565b509392505050565b600082601f830112620001c857620001c762000064565b5b8151620001da84826020860162000165565b91505092915050565b600060208284031215620001fc57620001fb6200005a565b5b600082015167ffffffffffffffff8111156200021d576200021c6200005f565b5b6200022b84828501620001b0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200028757607f821691505b6020821081036200029d576200029c6200023f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003077fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002c8565b620003138683620002c8565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003606200035a62000354846200032b565b62000335565b6200032b565b9050919050565b6000819050919050565b6200037c836200033f565b620003946200038b8262000367565b848454620002d5565b825550505050565b600090565b620003ab6200039c565b620003b881848462000371565b505050565b5b81811015620003e057620003d4600082620003a1565b600181019050620003be565b5050565b601f8211156200042f57620003f981620002a3565b6200040484620002b8565b8101602085101562000414578190505b6200042c6200042385620002b8565b830182620003bd565b50505b505050565b600082821c905092915050565b6000620004546000198460080262000434565b1980831691505092915050565b60006200046f838362000441565b9150826002028217905092915050565b6200048a8262000234565b67ffffffffffffffff811115620004a657620004a56200007f565b5b620004b282546200026e565b620004bf828285620003e4565b600060209050601f831160018114620004f75760008415620004e2578287015190505b620004ee858262000461565b8655506200055e565b601f1984166200050786620002a3565b60005b8281101562000531578489015182556001820191506020850194506020810190506200050a565b868310156200055157848901516200054d601f89168262000441565b8355505b6001600288020188555050505b505050505050565b61067c80620005766000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b61005560048036038101906100509190610274565b610075565b005b61005f610088565b60405161006c919061033c565b60405180910390f35b80600090816100849190610574565b5050565b6060600080546100979061038d565b80601f01602080910402602001604051908101604052809291908181526020018280546100c39061038d565b80156101105780601f106100e557610100808354040283529160200191610110565b820191906000526020600020905b8154815290600101906020018083116100f357829003601f168201915b5050505050905090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61018182610138565b810181811067ffffffffffffffff821117156101a05761019f610149565b5b80604052505050565b60006101b361011a565b90506101bf8282610178565b919050565b600067ffffffffffffffff8211156101df576101de610149565b5b6101e882610138565b9050602081019050919050565b82818337600083830152505050565b6000610217610212846101c4565b6101a9565b90508281526020810184848401111561023357610232610133565b5b61023e8482856101f5565b509392505050565b600082601f83011261025b5761025a61012e565b5b813561026b848260208601610204565b91505092915050565b60006020828403121561028a57610289610124565b5b600082013567ffffffffffffffff8111156102a8576102a7610129565b5b6102b484828501610246565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156102f75780820151818401526020810190506102dc565b60008484015250505050565b600061030e826102bd565b61031881856102c8565b93506103288185602086016102d9565b61033181610138565b840191505092915050565b600060208201905081810360008301526103568184610303565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103a557607f821691505b6020821081036103b8576103b761035e565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826103e3565b61042a86836103e3565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b600061047161046c61046784610442565b61044c565b610442565b9050919050565b6000819050919050565b61048b83610456565b61049f61049782610478565b8484546103f0565b825550505050565b600090565b6104b46104a7565b6104bf818484610482565b505050565b5b818110156104e3576104d86000826104ac565b6001810190506104c5565b5050565b601f821115610528576104f9816103be565b610502846103d3565b81016020851015610511578190505b61052561051d856103d3565b8301826104c4565b50505b505050565b600082821c905092915050565b600061054b6000198460080261052d565b1980831691505092915050565b6000610564838361053a565b9150826002028217905092915050565b61057d826102bd565b67ffffffffffffffff81111561059657610595610149565b5b6105a0825461038d565b6105ab8282856104e7565b600060209050601f8311600181146105de57600084156105cc578287015190505b6105d68582610558565b86555061063e565b601f1984166105ec866103be565b60005b82811015610614578489015182556001820191506020850194506020810190506105ef565b86831015610631578489015161062d601f89168261053a565b8355505b6001600288020188555050505b50505050505056fea2646970667358221220c103277e349bc27f877da8c13495470deae31d171e2ad22b71f97202ea17ae9564736f6c63430008130033 \ No newline at end of file diff --git a/tests/e2e/contracts/greeter/Greeter.sol b/tests/e2e/contracts/greeter/Greeter.sol new file mode 100644 index 00000000..26cb9552 --- /dev/null +++ b/tests/e2e/contracts/greeter/Greeter.sol @@ -0,0 +1,18 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.3; + +contract Greeter { + string greeting; + + constructor(string memory _greeting) { + greeting = _greeting; + } + + function greet() public view returns (string memory) { + return greeting; + } + + function setGreeting(string memory _greeting) public { + greeting = _greeting; + } +} diff --git a/tests/e2e/contracts/greeter/main.go b/tests/e2e/contracts/greeter/main.go new file mode 100644 index 00000000..9bf3c34c --- /dev/null +++ b/tests/e2e/contracts/greeter/main.go @@ -0,0 +1,255 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package greeter + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/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 +) + +// GreeterMetaData contains all meta data concerning the Greeter contract. +var GreeterMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_greeting\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"greet\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_greeting\",\"type\":\"string\"}],\"name\":\"setGreeting\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040523480156200001157600080fd5b5060405162000bf238038062000bf28339818101604052810190620000379190620001e3565b80600090816200004891906200047f565b505062000566565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000b9826200006e565b810181811067ffffffffffffffff82111715620000db57620000da6200007f565b5b80604052505050565b6000620000f062000050565b9050620000fe8282620000ae565b919050565b600067ffffffffffffffff8211156200012157620001206200007f565b5b6200012c826200006e565b9050602081019050919050565b60005b83811015620001595780820151818401526020810190506200013c565b60008484015250505050565b60006200017c620001768462000103565b620000e4565b9050828152602081018484840111156200019b576200019a62000069565b5b620001a884828562000139565b509392505050565b600082601f830112620001c857620001c762000064565b5b8151620001da84826020860162000165565b91505092915050565b600060208284031215620001fc57620001fb6200005a565b5b600082015167ffffffffffffffff8111156200021d576200021c6200005f565b5b6200022b84828501620001b0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200028757607f821691505b6020821081036200029d576200029c6200023f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003077fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002c8565b620003138683620002c8565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003606200035a62000354846200032b565b62000335565b6200032b565b9050919050565b6000819050919050565b6200037c836200033f565b620003946200038b8262000367565b848454620002d5565b825550505050565b600090565b620003ab6200039c565b620003b881848462000371565b505050565b5b81811015620003e057620003d4600082620003a1565b600181019050620003be565b5050565b601f8211156200042f57620003f981620002a3565b6200040484620002b8565b8101602085101562000414578190505b6200042c6200042385620002b8565b830182620003bd565b50505b505050565b600082821c905092915050565b6000620004546000198460080262000434565b1980831691505092915050565b60006200046f838362000441565b9150826002028217905092915050565b6200048a8262000234565b67ffffffffffffffff811115620004a657620004a56200007f565b5b620004b282546200026e565b620004bf828285620003e4565b600060209050601f831160018114620004f75760008415620004e2578287015190505b620004ee858262000461565b8655506200055e565b601f1984166200050786620002a3565b60005b8281101562000531578489015182556001820191506020850194506020810190506200050a565b868310156200055157848901516200054d601f89168262000441565b8355505b6001600288020188555050505b505050505050565b61067c80620005766000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b61005560048036038101906100509190610274565b610075565b005b61005f610088565b60405161006c919061033c565b60405180910390f35b80600090816100849190610574565b5050565b6060600080546100979061038d565b80601f01602080910402602001604051908101604052809291908181526020018280546100c39061038d565b80156101105780601f106100e557610100808354040283529160200191610110565b820191906000526020600020905b8154815290600101906020018083116100f357829003601f168201915b5050505050905090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61018182610138565b810181811067ffffffffffffffff821117156101a05761019f610149565b5b80604052505050565b60006101b361011a565b90506101bf8282610178565b919050565b600067ffffffffffffffff8211156101df576101de610149565b5b6101e882610138565b9050602081019050919050565b82818337600083830152505050565b6000610217610212846101c4565b6101a9565b90508281526020810184848401111561023357610232610133565b5b61023e8482856101f5565b509392505050565b600082601f83011261025b5761025a61012e565b5b813561026b848260208601610204565b91505092915050565b60006020828403121561028a57610289610124565b5b600082013567ffffffffffffffff8111156102a8576102a7610129565b5b6102b484828501610246565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156102f75780820151818401526020810190506102dc565b60008484015250505050565b600061030e826102bd565b61031881856102c8565b93506103288185602086016102d9565b61033181610138565b840191505092915050565b600060208201905081810360008301526103568184610303565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103a557607f821691505b6020821081036103b8576103b761035e565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826103e3565b61042a86836103e3565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b600061047161046c61046784610442565b61044c565b610442565b9050919050565b6000819050919050565b61048b83610456565b61049f61049782610478565b8484546103f0565b825550505050565b600090565b6104b46104a7565b6104bf818484610482565b505050565b5b818110156104e3576104d86000826104ac565b6001810190506104c5565b5050565b601f821115610528576104f9816103be565b610502846103d3565b81016020851015610511578190505b61052561051d856103d3565b8301826104c4565b50505b505050565b600082821c905092915050565b600061054b6000198460080261052d565b1980831691505092915050565b6000610564838361053a565b9150826002028217905092915050565b61057d826102bd565b67ffffffffffffffff81111561059657610595610149565b5b6105a0825461038d565b6105ab8282856104e7565b600060209050601f8311600181146105de57600084156105cc578287015190505b6105d68582610558565b86555061063e565b601f1984166105ec866103be565b60005b82811015610614578489015182556001820191506020850194506020810190506105ef565b86831015610631578489015161062d601f89168261053a565b8355505b6001600288020188555050505b50505050505056fea2646970667358221220c103277e349bc27f877da8c13495470deae31d171e2ad22b71f97202ea17ae9564736f6c63430008130033", +} + +// GreeterABI is the input ABI used to generate the binding from. +// Deprecated: Use GreeterMetaData.ABI instead. +var GreeterABI = GreeterMetaData.ABI + +// GreeterBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use GreeterMetaData.Bin instead. +var GreeterBin = GreeterMetaData.Bin + +// DeployGreeter deploys a new Ethereum contract, binding an instance of Greeter to it. +func DeployGreeter(auth *bind.TransactOpts, backend bind.ContractBackend, _greeting string) (common.Address, *types.Transaction, *Greeter, error) { + parsed, err := GreeterMetaData.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(GreeterBin), backend, _greeting) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Greeter{GreeterCaller: GreeterCaller{contract: contract}, GreeterTransactor: GreeterTransactor{contract: contract}, GreeterFilterer: GreeterFilterer{contract: contract}}, nil +} + +// Greeter is an auto generated Go binding around an Ethereum contract. +type Greeter struct { + GreeterCaller // Read-only binding to the contract + GreeterTransactor // Write-only binding to the contract + GreeterFilterer // Log filterer for contract events +} + +// GreeterCaller is an auto generated read-only Go binding around an Ethereum contract. +type GreeterCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GreeterTransactor is an auto generated write-only Go binding around an Ethereum contract. +type GreeterTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GreeterFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type GreeterFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GreeterSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type GreeterSession struct { + Contract *Greeter // 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 +} + +// GreeterCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type GreeterCallerSession struct { + Contract *GreeterCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// GreeterTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type GreeterTransactorSession struct { + Contract *GreeterTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GreeterRaw is an auto generated low-level Go binding around an Ethereum contract. +type GreeterRaw struct { + Contract *Greeter // Generic contract binding to access the raw methods on +} + +// GreeterCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type GreeterCallerRaw struct { + Contract *GreeterCaller // Generic read-only contract binding to access the raw methods on +} + +// GreeterTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type GreeterTransactorRaw struct { + Contract *GreeterTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewGreeter creates a new instance of Greeter, bound to a specific deployed contract. +func NewGreeter(address common.Address, backend bind.ContractBackend) (*Greeter, error) { + contract, err := bindGreeter(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Greeter{GreeterCaller: GreeterCaller{contract: contract}, GreeterTransactor: GreeterTransactor{contract: contract}, GreeterFilterer: GreeterFilterer{contract: contract}}, nil +} + +// NewGreeterCaller creates a new read-only instance of Greeter, bound to a specific deployed contract. +func NewGreeterCaller(address common.Address, caller bind.ContractCaller) (*GreeterCaller, error) { + contract, err := bindGreeter(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &GreeterCaller{contract: contract}, nil +} + +// NewGreeterTransactor creates a new write-only instance of Greeter, bound to a specific deployed contract. +func NewGreeterTransactor(address common.Address, transactor bind.ContractTransactor) (*GreeterTransactor, error) { + contract, err := bindGreeter(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &GreeterTransactor{contract: contract}, nil +} + +// NewGreeterFilterer creates a new log filterer instance of Greeter, bound to a specific deployed contract. +func NewGreeterFilterer(address common.Address, filterer bind.ContractFilterer) (*GreeterFilterer, error) { + contract, err := bindGreeter(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &GreeterFilterer{contract: contract}, nil +} + +// bindGreeter binds a generic wrapper to an already deployed contract. +func bindGreeter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := GreeterMetaData.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 (_Greeter *GreeterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Greeter.Contract.GreeterCaller.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 (_Greeter *GreeterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Greeter.Contract.GreeterTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Greeter *GreeterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Greeter.Contract.GreeterTransactor.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 (_Greeter *GreeterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Greeter.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 (_Greeter *GreeterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Greeter.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Greeter *GreeterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Greeter.Contract.contract.Transact(opts, method, params...) +} + +// Greet is a free data retrieval call binding the contract method 0xcfae3217. +// +// Solidity: function greet() view returns(string) +func (_Greeter *GreeterCaller) Greet(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _Greeter.contract.Call(opts, &out, "greet") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Greet is a free data retrieval call binding the contract method 0xcfae3217. +// +// Solidity: function greet() view returns(string) +func (_Greeter *GreeterSession) Greet() (string, error) { + return _Greeter.Contract.Greet(&_Greeter.CallOpts) +} + +// Greet is a free data retrieval call binding the contract method 0xcfae3217. +// +// Solidity: function greet() view returns(string) +func (_Greeter *GreeterCallerSession) Greet() (string, error) { + return _Greeter.Contract.Greet(&_Greeter.CallOpts) +} + +// SetGreeting is a paid mutator transaction binding the contract method 0xa4136862. +// +// Solidity: function setGreeting(string _greeting) returns() +func (_Greeter *GreeterTransactor) SetGreeting(opts *bind.TransactOpts, _greeting string) (*types.Transaction, error) { + return _Greeter.contract.Transact(opts, "setGreeting", _greeting) +} + +// SetGreeting is a paid mutator transaction binding the contract method 0xa4136862. +// +// Solidity: function setGreeting(string _greeting) returns() +func (_Greeter *GreeterSession) SetGreeting(_greeting string) (*types.Transaction, error) { + return _Greeter.Contract.SetGreeting(&_Greeter.TransactOpts, _greeting) +} + +// SetGreeting is a paid mutator transaction binding the contract method 0xa4136862. +// +// Solidity: function setGreeting(string _greeting) returns() +func (_Greeter *GreeterTransactorSession) SetGreeting(_greeting string) (*types.Transaction, error) { + return _Greeter.Contract.SetGreeting(&_Greeter.TransactOpts, _greeting) +} diff --git a/tests/e2e/contracts/readme.md b/tests/e2e/contracts/readme.md new file mode 100644 index 00000000..b44f3d7e --- /dev/null +++ b/tests/e2e/contracts/readme.md @@ -0,0 +1,24 @@ +This directory contains contract interfaces used by the e2e test suite. + +# Prereq +* Abigen: https://geth.ethereum.org/docs/tools/abigen +* Solidity: https://docs.soliditylang.org/en/latest/installing-solidity.html + +# Create Contract Interfaces for Go + +If you have the compiled ABI, you can skip directly to step 4. + +To create new go interfaces to contracts: +1. add the solidity file: `.sol` +2. decide on a package name. this will be the name of the package you'll import into go (``) +3. compile the abi & bin for the contract: `solc -o --abi --bin .sol` + * run from this directory + * note that `-o` is the output directory. this will generate `/.abi` +4. generate the golang interface: + `abigen --abi=/.abi --bin=/.bin --pkg= --out=/main.go` +5. import and use the contract in Go. + +By including the bin, the generated interface will have a `Deploy*` method. If you only need to interact with an existing contract, you can exclude the `--bin` and only an interaction method interface will be generated. + +# Resources +* https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings diff --git a/tests/e2e/e2e_evm_contracts_test.go b/tests/e2e/e2e_evm_contracts_test.go new file mode 100644 index 00000000..a6a652eb --- /dev/null +++ b/tests/e2e/e2e_evm_contracts_test.go @@ -0,0 +1,37 @@ +package e2e_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/tests/e2e/contracts/greeter" + "github.com/kava-labs/kava/tests/util" +) + +func (suite *IntegrationTestSuite) TestEthCallToGreeterContract() { + // this test manipulates state of the Greeter contract which means other tests shouldn't use it. + + // setup funded account to interact with contract + user := suite.Kava.NewFundedAccount("greeter-contract-user", sdk.NewCoins(ukava(10e6))) + + greeterAddr := suite.Kava.ContractAddrs["greeter"] + contract, err := greeter.NewGreeter(greeterAddr, suite.Kava.EvmClient) + suite.NoError(err) + + beforeGreeting, err := contract.Greet(nil) + suite.NoError(err) + + updatedGreeting := "look at me, using the evm" + tx, err := contract.SetGreeting(user.EvmAuth, updatedGreeting) + suite.NoError(err) + + _, err = util.WaitForEvmTxReceipt(suite.Kava.EvmClient, tx.Hash(), 10*time.Second) + suite.NoError(err) + + afterGreeting, err := contract.Greet(nil) + suite.NoError(err) + + suite.Equal("what's up!", beforeGreeting) + suite.Equal(updatedGreeting, afterGreeting) +} diff --git a/tests/e2e/kvtool b/tests/e2e/kvtool index 8153036b..af9629a2 160000 --- a/tests/e2e/kvtool +++ b/tests/e2e/kvtool @@ -1 +1 @@ -Subproject commit 8153036b95a930c7830c6969d1653165afa65f2c +Subproject commit af9629a2b97475d4d324e4578b58676efa7f07ed diff --git a/tests/e2e/testutil/account.go b/tests/e2e/testutil/account.go index 8493fc96..9978df0f 100644 --- a/tests/e2e/testutil/account.go +++ b/tests/e2e/testutil/account.go @@ -1,9 +1,7 @@ package testutil import ( - "context" "encoding/hex" - "errors" "fmt" "log" "os" @@ -13,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/go-bip39" - "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -25,8 +23,6 @@ import ( "github.com/kava-labs/kava/tests/util" ) -var ErrBroadcastTimeout = errors.New("timed out waiting for tx to be committed to block") - type SigningAccount struct { name string mnemonic string @@ -39,6 +35,8 @@ type SigningAccount struct { sdkReqChan chan<- util.KavaMsgRequest sdkResChan <-chan util.KavaMsgResponse + EvmAuth *bind.TransactOpts + EvmAddress common.Address SdkAddress sdk.AccAddress @@ -109,6 +107,8 @@ func (chain *Chain) AddNewSigningAccount(name string, hdPath *hd.BIP44Params, ch sdkReqChan: sdkReqChan, sdkResChan: sdkResChan, + EvmAuth: evmSigner.Auth, + EvmAddress: evmSigner.Address(), SdkAddress: kavaSigner.Address(), } @@ -162,21 +162,7 @@ func (a *SigningAccount) SignAndBroadcastEvmTx(req util.EvmTxRequest) EvmTxRespo } // if we don't have a tx receipt within a given timeout, fail the request - timeout := time.After(10 * time.Second) - for { - select { - case <-timeout: - response.Err = ErrBroadcastTimeout - default: - response.Receipt, response.Err = a.evmSigner.EvmClient.TransactionReceipt(context.Background(), res.TxHash) - if errors.Is(response.Err, ethereum.NotFound) { - // tx still not committed to a block. retry! - time.Sleep(100 * time.Millisecond) - continue - } - } - break - } + response.Receipt, response.Err = util.WaitForEvmTxReceipt(a.evmSigner.EvmClient, res.TxHash, 10*time.Second) return response } diff --git a/tests/e2e/testutil/chain.go b/tests/e2e/testutil/chain.go index 9487af3a..1dcf0fcf 100644 --- a/tests/e2e/testutil/chain.go +++ b/tests/e2e/testutil/chain.go @@ -11,6 +11,7 @@ import ( txtypes "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/require" @@ -31,7 +32,8 @@ type Chain struct { StakingDenom string ChainId string - EvmClient *ethclient.Client + EvmClient *ethclient.Client + ContractAddrs map[string]common.Address Auth authtypes.QueryClient Bank banktypes.QueryClient @@ -46,9 +48,10 @@ type Chain struct { // code as "whale" and it is used to supply funds to all new accounts. func NewChain(t *testing.T, details *runner.ChainDetails, fundedAccountMnemonic string) (*Chain, error) { chain := &Chain{ - t: t, - StakingDenom: details.StakingDenom, - ChainId: details.ChainId, + t: t, + StakingDenom: details.StakingDenom, + ChainId: details.ChainId, + ContractAddrs: make(map[string]common.Address), } chain.encodingConfig = app.MakeEncodingConfig() diff --git a/tests/e2e/testutil/init_evm.go b/tests/e2e/testutil/init_evm.go new file mode 100644 index 00000000..2763d853 --- /dev/null +++ b/tests/e2e/testutil/init_evm.go @@ -0,0 +1,18 @@ +package testutil + +import "github.com/kava-labs/kava/tests/e2e/contracts/greeter" + +// InitKavaEvmData is run after the chain is running, but before the tests are run. +// It is used to initialize some EVM state, such as deploying contracts. +func (suite *E2eTestSuite) InitKavaEvmData() { + whale := suite.Kava.GetAccount(FundedAccountName) + + // deploy an example contract + greeterAddr, _, _, err := greeter.DeployGreeter( + whale.evmSigner.Auth, + whale.evmSigner.EvmClient, + "what's up!", + ) + suite.NoError(err) + suite.Kava.ContractAddrs["greeter"] = greeterAddr +} diff --git a/tests/e2e/testutil/suite.go b/tests/e2e/testutil/suite.go index 47a5c8cd..5ace3463 100644 --- a/tests/e2e/testutil/suite.go +++ b/tests/e2e/testutil/suite.go @@ -73,6 +73,8 @@ func (suite *E2eTestSuite) SetupSuite() { suite.T().Fatalf("failed to create ibc chain querier: %s", err) } } + + suite.InitKavaEvmData() } func (suite *E2eTestSuite) TearDownSuite() { diff --git a/tests/util/evmsigner.go b/tests/util/evmsigner.go index aeb421be..5b860ef9 100644 --- a/tests/util/evmsigner.go +++ b/tests/util/evmsigner.go @@ -3,9 +3,12 @@ package util import ( "context" "crypto/ecdsa" + "errors" "fmt" "math/big" + "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -13,6 +16,14 @@ import ( "github.com/ethereum/go-ethereum/ethclient" ) +var ( + ErrEvmBroadcastTimeout = errors.New("timed out waiting for tx to be committed to block") + // ErrEvmTxFailed is returned when a tx is committed to a block, but the receipt status is 0. + // this means the tx failed. we don't have debug_traceTransaction RPC command so the best way + // to determine the problem is to attempt to make the tx manually. + ErrEvmTxFailed = errors.New("transaction was committed but failed. likely an execution revert by contract code") +) + type EvmTxRequest struct { Tx *ethtypes.Transaction Data interface{} @@ -38,8 +49,8 @@ func (e ErrEvmFailedToBroadcast) Error() string { // EvmSigner manages signing and broadcasting requests to transfer Erc20 tokens // Will work for calling all contracts that have func signature `transfer(address,uint256)` type EvmSigner struct { - auth *bind.TransactOpts signerAddress common.Address + Auth *bind.TransactOpts EvmClient *ethclient.Client } @@ -60,7 +71,7 @@ func NewEvmSigner( } return &EvmSigner{ - auth: auth, + Auth: auth, signerAddress: crypto.PubkeyToAddress(*publicKeyECDSA), EvmClient: evmClient, }, nil @@ -77,7 +88,7 @@ func (s *EvmSigner) Run(requests <-chan EvmTxRequest) <-chan EvmTxResponse { // wait for incoming request req := <-requests - signedTx, err := s.auth.Signer(s.signerAddress, req.Tx) + signedTx, err := s.Auth.Signer(s.signerAddress, req.Tx) if err != nil { err = ErrEvmFailedToSign{Err: err} } else { @@ -101,3 +112,30 @@ func (s *EvmSigner) Run(requests <-chan EvmTxRequest) <-chan EvmTxResponse { func (s *EvmSigner) Address() common.Address { return s.signerAddress } + +// WaitForEvmTxReceipt polls for a tx receipt and errors on timeout. +// If the receipt comes back, but with status 0 (failed), an error is returned. +func WaitForEvmTxReceipt(client *ethclient.Client, txHash common.Hash, timeout time.Duration) (*ethtypes.Receipt, error) { + var receipt *ethtypes.Receipt + var err error + outOfTime := time.After(timeout) + for { + select { + case <-outOfTime: + err = ErrEvmBroadcastTimeout + default: + receipt, err = client.TransactionReceipt(context.Background(), txHash) + if errors.Is(err, ethereum.NotFound) { + // tx still not committed to a block. retry! + time.Sleep(100 * time.Millisecond) + continue + } + // a response status of 0 means the tx was successfully committed but failed to execute + if receipt.Status == 0 { + err = ErrEvmTxFailed + } + } + break + } + return receipt, err +}