diff --git a/Makefile b/Makefile index 4497efe1..5e3acb19 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,10 @@ install-subkey: cargo install --force --git https://github.com/paritytech/substrate subkey genmocks: - mockgen -destination=./chains/evm/evmgaspricer/mock/gas-pricer.go -source=./chains/evm/evmgaspricer/gas-pricer.go + mockgen -source=./chains/evm/evmgaspricer/gas-pricer.go -destination=./chains/evm/evmgaspricer/mock/gas-pricer.go + mockgen -source=chains/evm/calls/utils.go -destination=chains/evm/calls/mock/utils.go + e2e-test: ./scripts/int_tests.sh + diff --git a/chains/evm/calls/deploy.go b/chains/evm/calls/deploy.go index 58e94602..9dc2caf2 100644 --- a/chains/evm/calls/deploy.go +++ b/chains/evm/calls/deploy.go @@ -52,6 +52,8 @@ func DeployErc20Handler(c ClientDeployer, txFabric TxFabric, gasPriceClient GasP } func deployContract(client ClientDeployer, abi abi.ABI, bytecode []byte, txFabric TxFabric, gasPriceClient GasPricer, params ...interface{}) (common.Address, error) { + defer client.UnlockNonce() + client.LockNonce() n, err := client.UnsafeNonce() if err != nil { @@ -85,7 +87,6 @@ func deployContract(client ClientDeployer, abi abi.ABI, bytecode []byte, txFabri if err != nil { return common.Address{}, err } - client.UnlockNonce() // checks bytecode at address // nil is latest block if code, err := client.CodeAt(context.Background(), address, nil); err != nil { diff --git a/chains/evm/calls/deploy_test.go b/chains/evm/calls/deploy_test.go new file mode 100644 index 00000000..6d6863c1 --- /dev/null +++ b/chains/evm/calls/deploy_test.go @@ -0,0 +1,149 @@ +package calls_test + +import ( + "errors" + + calls "github.com/ChainSafe/chainbridge-core/chains/evm/calls" + mock_calls "github.com/ChainSafe/chainbridge-core/chains/evm/calls/mock" + "github.com/ChainSafe/chainbridge-core/chains/evm/evmtransaction" + "github.com/ethereum/go-ethereum/common" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/suite" + + "math/big" + + "testing" +) + +type DeployTestSuite struct { + suite.Suite + mockClientDeployer *mock_calls.MockClientDeployer + mockgasPricer *mock_calls.MockGasPricer +} + +func TestRunDeployTestSuite(t *testing.T) { + suite.Run(t, new(DeployTestSuite)) +} + +func (s *DeployTestSuite) SetupSuite() { + gomockController := gomock.NewController(s.T()) + s.mockClientDeployer = mock_calls.NewMockClientDeployer(gomockController) + s.mockgasPricer = mock_calls.NewMockGasPricer(gomockController) +} +func (s *DeployTestSuite) TearDownSuite() {} +func (s *DeployTestSuite) SetupTest() {} +func (s *DeployTestSuite) TearDownTest() {} + +func (s *DeployTestSuite) TestDeployErc20NonceUnlockCallWithErrorThrown() { + s.mockClientDeployer.EXPECT().LockNonce().Times(1) + s.mockClientDeployer.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDeployer.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, nil) + s.mockClientDeployer.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(1).Return(nil, nil) + s.mockClientDeployer.EXPECT().From().Times(1).Return(common.Address{}) + s.mockClientDeployer.EXPECT().UnsafeIncreaseNonce().Times(1) + s.mockClientDeployer.EXPECT().CodeAt(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + s.mockClientDeployer.EXPECT().UnlockNonce().Times(1) + + _, _ = calls.DeployErc20( + s.mockClientDeployer, + evmtransaction.NewTransaction, + s.mockgasPricer, + "TEST", + "TST") +} + +func (s *DeployTestSuite) TestDeployErc20NonceUnlockCallWithoutErrorsThrown() { + s.mockClientDeployer.EXPECT().LockNonce().Times(1) + s.mockClientDeployer.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDeployer.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, errors.New("error")) + s.mockClientDeployer.EXPECT().UnlockNonce().Times(1) + s.mockClientDeployer.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(0) + s.mockClientDeployer.EXPECT().UnsafeIncreaseNonce().Times(0) + + _, _ = calls.DeployErc20( + s.mockClientDeployer, + evmtransaction.NewTransaction, + s.mockgasPricer, + "TEST", + "TST") +} + +func (s *DeployTestSuite) TestDeployBridgeNonceUnlockCall() { + s.mockClientDeployer.EXPECT().LockNonce().Times(1) + s.mockClientDeployer.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDeployer.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, errors.New("error")) + s.mockClientDeployer.EXPECT().UnlockNonce().Times(1) + s.mockClientDeployer.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(0) + s.mockClientDeployer.EXPECT().UnsafeIncreaseNonce().Times(0) + + toAddress := common.HexToAddress("0xtest1") + + _, _ = calls.DeployBridge( + s.mockClientDeployer, + evmtransaction.NewTransaction, + s.mockgasPricer, + 0x1, + []common.Address{toAddress}, + big.NewInt(2), + big.NewInt(10)) +} + +func (s *DeployTestSuite) TestDeployBridgeNonceUnlockCallWithoutErrorsThrown() { + s.mockClientDeployer.EXPECT().LockNonce().Times(1) + s.mockClientDeployer.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDeployer.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, errors.New("error")) + s.mockClientDeployer.EXPECT().UnlockNonce().Times(1) + s.mockClientDeployer.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(0) + s.mockClientDeployer.EXPECT().UnsafeIncreaseNonce().Times(0) + + toAddress := common.HexToAddress("0xtest1") + + _, _ = calls.DeployBridge( + s.mockClientDeployer, + evmtransaction.NewTransaction, + s.mockgasPricer, + 0x1, + []common.Address{toAddress}, + big.NewInt(2), + big.NewInt(10)) +} + +func (s *DeployTestSuite) TestDeployErc20HandlerNonceUnlockCallWithErrorThrown() { + s.mockClientDeployer.EXPECT().LockNonce().Times(1) + s.mockClientDeployer.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDeployer.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, errors.New("error")) + s.mockClientDeployer.EXPECT().UnlockNonce().Times(1) + s.mockClientDeployer.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(0) + s.mockClientDeployer.EXPECT().UnsafeIncreaseNonce().Times(0) + + toAddress := common.HexToAddress("0xtest1") + + _, _ = calls.DeployErc20Handler( + s.mockClientDeployer, + evmtransaction.NewTransaction, + s.mockgasPricer, + toAddress) +} + +func (s *DeployTestSuite) TestDeployErc20HandlerNonceUnlockCallWithoutErrorsThrown() { + s.mockClientDeployer.EXPECT().LockNonce().Times(1) + s.mockClientDeployer.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDeployer.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, errors.New("error")) + s.mockClientDeployer.EXPECT().UnlockNonce().Times(1) + s.mockClientDeployer.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(0) + s.mockClientDeployer.EXPECT().UnsafeIncreaseNonce().Times(0) + + toAddress := common.HexToAddress("0xtest1") + + _, _ = calls.DeployErc20Handler( + s.mockClientDeployer, + evmtransaction.NewTransaction, + s.mockgasPricer, + toAddress) +} diff --git a/chains/evm/calls/mock/bridge.go b/chains/evm/calls/mock/bridge.go deleted file mode 100644 index 2b91b36b..00000000 --- a/chains/evm/calls/mock/bridge.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: chains/evm/calls/bridge.go - -// Package mock_calls is a generated GoMock package. -package mock_calls - -import ( - context "context" - big "math/big" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockContractCallerClient is a mock of ContractCallerClient interface. -type MockContractCallerClient struct { - ctrl *gomock.Controller - recorder *MockContractCallerClientMockRecorder -} - -// MockContractCallerClientMockRecorder is the mock recorder for MockContractCallerClient. -type MockContractCallerClientMockRecorder struct { - mock *MockContractCallerClient -} - -// NewMockContractCallerClient creates a new mock instance. -func NewMockContractCallerClient(ctrl *gomock.Controller) *MockContractCallerClient { - mock := &MockContractCallerClient{ctrl: ctrl} - mock.recorder = &MockContractCallerClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockContractCallerClient) EXPECT() *MockContractCallerClientMockRecorder { - return m.recorder -} - -// CallContract mocks base method. -func (m *MockContractCallerClient) CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CallContract", ctx, callArgs, blockNumber) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CallContract indicates an expected call of CallContract. -func (mr *MockContractCallerClientMockRecorder) CallContract(ctx, callArgs, blockNumber interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockContractCallerClient)(nil).CallContract), ctx, callArgs, blockNumber) -} diff --git a/chains/evm/calls/mock/utils.go b/chains/evm/calls/mock/utils.go new file mode 100644 index 00000000..7b5a59a5 --- /dev/null +++ b/chains/evm/calls/mock/utils.go @@ -0,0 +1,492 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: chains/evm/calls/utils.go + +// Package mock_calls is a generated GoMock package. +package mock_calls + +import ( + context "context" + big "math/big" + reflect "reflect" + + evmclient "github.com/ChainSafe/chainbridge-core/chains/evm/evmclient" + common "github.com/ethereum/go-ethereum/common" + types "github.com/ethereum/go-ethereum/core/types" + gomock "github.com/golang/mock/gomock" +) + +// MockClientContractChecker is a mock of ClientContractChecker interface. +type MockClientContractChecker struct { + ctrl *gomock.Controller + recorder *MockClientContractCheckerMockRecorder +} + +// MockClientContractCheckerMockRecorder is the mock recorder for MockClientContractChecker. +type MockClientContractCheckerMockRecorder struct { + mock *MockClientContractChecker +} + +// NewMockClientContractChecker creates a new mock instance. +func NewMockClientContractChecker(ctrl *gomock.Controller) *MockClientContractChecker { + mock := &MockClientContractChecker{ctrl: ctrl} + mock.recorder = &MockClientContractCheckerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClientContractChecker) EXPECT() *MockClientContractCheckerMockRecorder { + return m.recorder +} + +// CodeAt mocks base method. +func (m *MockClientContractChecker) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CodeAt", ctx, contract, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CodeAt indicates an expected call of CodeAt. +func (mr *MockClientContractCheckerMockRecorder) CodeAt(ctx, contract, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CodeAt", reflect.TypeOf((*MockClientContractChecker)(nil).CodeAt), ctx, contract, blockNumber) +} + +// MockContractCallerClient is a mock of ContractCallerClient interface. +type MockContractCallerClient struct { + ctrl *gomock.Controller + recorder *MockContractCallerClientMockRecorder +} + +// MockContractCallerClientMockRecorder is the mock recorder for MockContractCallerClient. +type MockContractCallerClientMockRecorder struct { + mock *MockContractCallerClient +} + +// NewMockContractCallerClient creates a new mock instance. +func NewMockContractCallerClient(ctrl *gomock.Controller) *MockContractCallerClient { + mock := &MockContractCallerClient{ctrl: ctrl} + mock.recorder = &MockContractCallerClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockContractCallerClient) EXPECT() *MockContractCallerClientMockRecorder { + return m.recorder +} + +// CallContract mocks base method. +func (m *MockContractCallerClient) CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CallContract", ctx, callArgs, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockContractCallerClientMockRecorder) CallContract(ctx, callArgs, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockContractCallerClient)(nil).CallContract), ctx, callArgs, blockNumber) +} + +// MockContractCheckerCallerClient is a mock of ContractCheckerCallerClient interface. +type MockContractCheckerCallerClient struct { + ctrl *gomock.Controller + recorder *MockContractCheckerCallerClientMockRecorder +} + +// MockContractCheckerCallerClientMockRecorder is the mock recorder for MockContractCheckerCallerClient. +type MockContractCheckerCallerClientMockRecorder struct { + mock *MockContractCheckerCallerClient +} + +// NewMockContractCheckerCallerClient creates a new mock instance. +func NewMockContractCheckerCallerClient(ctrl *gomock.Controller) *MockContractCheckerCallerClient { + mock := &MockContractCheckerCallerClient{ctrl: ctrl} + mock.recorder = &MockContractCheckerCallerClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockContractCheckerCallerClient) EXPECT() *MockContractCheckerCallerClientMockRecorder { + return m.recorder +} + +// CallContract mocks base method. +func (m *MockContractCheckerCallerClient) CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CallContract", ctx, callArgs, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockContractCheckerCallerClientMockRecorder) CallContract(ctx, callArgs, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockContractCheckerCallerClient)(nil).CallContract), ctx, callArgs, blockNumber) +} + +// CodeAt mocks base method. +func (m *MockContractCheckerCallerClient) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CodeAt", ctx, contract, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CodeAt indicates an expected call of CodeAt. +func (mr *MockContractCheckerCallerClientMockRecorder) CodeAt(ctx, contract, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CodeAt", reflect.TypeOf((*MockContractCheckerCallerClient)(nil).CodeAt), ctx, contract, blockNumber) +} + +// MockClientDeployer is a mock of ClientDeployer interface. +type MockClientDeployer struct { + ctrl *gomock.Controller + recorder *MockClientDeployerMockRecorder +} + +// MockClientDeployerMockRecorder is the mock recorder for MockClientDeployer. +type MockClientDeployerMockRecorder struct { + mock *MockClientDeployer +} + +// NewMockClientDeployer creates a new mock instance. +func NewMockClientDeployer(ctrl *gomock.Controller) *MockClientDeployer { + mock := &MockClientDeployer{ctrl: ctrl} + mock.recorder = &MockClientDeployerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClientDeployer) EXPECT() *MockClientDeployerMockRecorder { + return m.recorder +} + +// CodeAt mocks base method. +func (m *MockClientDeployer) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CodeAt", ctx, contract, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CodeAt indicates an expected call of CodeAt. +func (mr *MockClientDeployerMockRecorder) CodeAt(ctx, contract, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CodeAt", reflect.TypeOf((*MockClientDeployer)(nil).CodeAt), ctx, contract, blockNumber) +} + +// From mocks base method. +func (m *MockClientDeployer) From() common.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "From") + ret0, _ := ret[0].(common.Address) + return ret0 +} + +// From indicates an expected call of From. +func (mr *MockClientDeployerMockRecorder) From() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "From", reflect.TypeOf((*MockClientDeployer)(nil).From)) +} + +// LockNonce mocks base method. +func (m *MockClientDeployer) LockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "LockNonce") +} + +// LockNonce indicates an expected call of LockNonce. +func (mr *MockClientDeployerMockRecorder) LockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockNonce", reflect.TypeOf((*MockClientDeployer)(nil).LockNonce)) +} + +// SignAndSendTransaction mocks base method. +func (m *MockClientDeployer) SignAndSendTransaction(ctx context.Context, tx evmclient.CommonTransaction) (common.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignAndSendTransaction", ctx, tx) + ret0, _ := ret[0].(common.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignAndSendTransaction indicates an expected call of SignAndSendTransaction. +func (mr *MockClientDeployerMockRecorder) SignAndSendTransaction(ctx, tx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignAndSendTransaction", reflect.TypeOf((*MockClientDeployer)(nil).SignAndSendTransaction), ctx, tx) +} + +// UnlockNonce mocks base method. +func (m *MockClientDeployer) UnlockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnlockNonce") +} + +// UnlockNonce indicates an expected call of UnlockNonce. +func (mr *MockClientDeployerMockRecorder) UnlockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnlockNonce", reflect.TypeOf((*MockClientDeployer)(nil).UnlockNonce)) +} + +// UnsafeIncreaseNonce mocks base method. +func (m *MockClientDeployer) UnsafeIncreaseNonce() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeIncreaseNonce") + ret0, _ := ret[0].(error) + return ret0 +} + +// UnsafeIncreaseNonce indicates an expected call of UnsafeIncreaseNonce. +func (mr *MockClientDeployerMockRecorder) UnsafeIncreaseNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeIncreaseNonce", reflect.TypeOf((*MockClientDeployer)(nil).UnsafeIncreaseNonce)) +} + +// UnsafeNonce mocks base method. +func (m *MockClientDeployer) UnsafeNonce() (*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeNonce") + ret0, _ := ret[0].(*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UnsafeNonce indicates an expected call of UnsafeNonce. +func (mr *MockClientDeployerMockRecorder) UnsafeNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeNonce", reflect.TypeOf((*MockClientDeployer)(nil).UnsafeNonce)) +} + +// WaitAndReturnTxReceipt mocks base method. +func (m *MockClientDeployer) WaitAndReturnTxReceipt(h common.Hash) (*types.Receipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitAndReturnTxReceipt", h) + ret0, _ := ret[0].(*types.Receipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitAndReturnTxReceipt indicates an expected call of WaitAndReturnTxReceipt. +func (mr *MockClientDeployerMockRecorder) WaitAndReturnTxReceipt(h interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitAndReturnTxReceipt", reflect.TypeOf((*MockClientDeployer)(nil).WaitAndReturnTxReceipt), h) +} + +// MockGasPricer is a mock of GasPricer interface. +type MockGasPricer struct { + ctrl *gomock.Controller + recorder *MockGasPricerMockRecorder +} + +// MockGasPricerMockRecorder is the mock recorder for MockGasPricer. +type MockGasPricerMockRecorder struct { + mock *MockGasPricer +} + +// NewMockGasPricer creates a new mock instance. +func NewMockGasPricer(ctrl *gomock.Controller) *MockGasPricer { + mock := &MockGasPricer{ctrl: ctrl} + mock.recorder = &MockGasPricerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGasPricer) EXPECT() *MockGasPricerMockRecorder { + return m.recorder +} + +// GasPrice mocks base method. +func (m *MockGasPricer) GasPrice() ([]*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GasPrice") + ret0, _ := ret[0].([]*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GasPrice indicates an expected call of GasPrice. +func (mr *MockGasPricerMockRecorder) GasPrice() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasPrice", reflect.TypeOf((*MockGasPricer)(nil).GasPrice)) +} + +// MockClientDispatcher is a mock of ClientDispatcher interface. +type MockClientDispatcher struct { + ctrl *gomock.Controller + recorder *MockClientDispatcherMockRecorder +} + +// MockClientDispatcherMockRecorder is the mock recorder for MockClientDispatcher. +type MockClientDispatcherMockRecorder struct { + mock *MockClientDispatcher +} + +// NewMockClientDispatcher creates a new mock instance. +func NewMockClientDispatcher(ctrl *gomock.Controller) *MockClientDispatcher { + mock := &MockClientDispatcher{ctrl: ctrl} + mock.recorder = &MockClientDispatcherMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClientDispatcher) EXPECT() *MockClientDispatcherMockRecorder { + return m.recorder +} + +// From mocks base method. +func (m *MockClientDispatcher) From() common.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "From") + ret0, _ := ret[0].(common.Address) + return ret0 +} + +// From indicates an expected call of From. +func (mr *MockClientDispatcherMockRecorder) From() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "From", reflect.TypeOf((*MockClientDispatcher)(nil).From)) +} + +// LockNonce mocks base method. +func (m *MockClientDispatcher) LockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "LockNonce") +} + +// LockNonce indicates an expected call of LockNonce. +func (mr *MockClientDispatcherMockRecorder) LockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockNonce", reflect.TypeOf((*MockClientDispatcher)(nil).LockNonce)) +} + +// SignAndSendTransaction mocks base method. +func (m *MockClientDispatcher) SignAndSendTransaction(ctx context.Context, tx evmclient.CommonTransaction) (common.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignAndSendTransaction", ctx, tx) + ret0, _ := ret[0].(common.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignAndSendTransaction indicates an expected call of SignAndSendTransaction. +func (mr *MockClientDispatcherMockRecorder) SignAndSendTransaction(ctx, tx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignAndSendTransaction", reflect.TypeOf((*MockClientDispatcher)(nil).SignAndSendTransaction), ctx, tx) +} + +// UnlockNonce mocks base method. +func (m *MockClientDispatcher) UnlockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnlockNonce") +} + +// UnlockNonce indicates an expected call of UnlockNonce. +func (mr *MockClientDispatcherMockRecorder) UnlockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnlockNonce", reflect.TypeOf((*MockClientDispatcher)(nil).UnlockNonce)) +} + +// UnsafeIncreaseNonce mocks base method. +func (m *MockClientDispatcher) UnsafeIncreaseNonce() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeIncreaseNonce") + ret0, _ := ret[0].(error) + return ret0 +} + +// UnsafeIncreaseNonce indicates an expected call of UnsafeIncreaseNonce. +func (mr *MockClientDispatcherMockRecorder) UnsafeIncreaseNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeIncreaseNonce", reflect.TypeOf((*MockClientDispatcher)(nil).UnsafeIncreaseNonce)) +} + +// UnsafeNonce mocks base method. +func (m *MockClientDispatcher) UnsafeNonce() (*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeNonce") + ret0, _ := ret[0].(*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UnsafeNonce indicates an expected call of UnsafeNonce. +func (mr *MockClientDispatcherMockRecorder) UnsafeNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeNonce", reflect.TypeOf((*MockClientDispatcher)(nil).UnsafeNonce)) +} + +// WaitAndReturnTxReceipt mocks base method. +func (m *MockClientDispatcher) WaitAndReturnTxReceipt(h common.Hash) (*types.Receipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitAndReturnTxReceipt", h) + ret0, _ := ret[0].(*types.Receipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitAndReturnTxReceipt indicates an expected call of WaitAndReturnTxReceipt. +func (mr *MockClientDispatcherMockRecorder) WaitAndReturnTxReceipt(h interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitAndReturnTxReceipt", reflect.TypeOf((*MockClientDispatcher)(nil).WaitAndReturnTxReceipt), h) +} + +// MockSimulateCallerClient is a mock of SimulateCallerClient interface. +type MockSimulateCallerClient struct { + ctrl *gomock.Controller + recorder *MockSimulateCallerClientMockRecorder +} + +// MockSimulateCallerClientMockRecorder is the mock recorder for MockSimulateCallerClient. +type MockSimulateCallerClientMockRecorder struct { + mock *MockSimulateCallerClient +} + +// NewMockSimulateCallerClient creates a new mock instance. +func NewMockSimulateCallerClient(ctrl *gomock.Controller) *MockSimulateCallerClient { + mock := &MockSimulateCallerClient{ctrl: ctrl} + mock.recorder = &MockSimulateCallerClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSimulateCallerClient) EXPECT() *MockSimulateCallerClientMockRecorder { + return m.recorder +} + +// CallContract mocks base method. +func (m *MockSimulateCallerClient) CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CallContract", ctx, callArgs, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockSimulateCallerClientMockRecorder) CallContract(ctx, callArgs, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockSimulateCallerClient)(nil).CallContract), ctx, callArgs, blockNumber) +} + +// TransactionByHash mocks base method. +func (m *MockSimulateCallerClient) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionByHash", ctx, hash) + ret0, _ := ret[0].(*types.Transaction) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// TransactionByHash indicates an expected call of TransactionByHash. +func (mr *MockSimulateCallerClientMockRecorder) TransactionByHash(ctx, hash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionByHash", reflect.TypeOf((*MockSimulateCallerClient)(nil).TransactionByHash), ctx, hash) +} diff --git a/chains/evm/calls/utils.go b/chains/evm/calls/utils.go index 000db622..fc9e3b92 100644 --- a/chains/evm/calls/utils.go +++ b/chains/evm/calls/utils.go @@ -103,6 +103,8 @@ func UserAmountToWei(amount string, decimal *big.Int) (*big.Int, error) { } func Transact(client ClientDispatcher, txFabric TxFabric, gasPriceClient GasPricer, to *common.Address, data []byte, gasLimit uint64, value *big.Int) (common.Hash, error) { + defer client.UnlockNonce() + client.LockNonce() n, err := client.UnsafeNonce() if err != nil { @@ -129,7 +131,6 @@ func Transact(client ClientDispatcher, txFabric TxFabric, gasPriceClient GasPric if err != nil { return common.Hash{}, err } - client.UnlockNonce() return tx.Hash(), nil } diff --git a/chains/evm/calls/utils_test.go b/chains/evm/calls/utils_test.go index ac391f4b..8049bbf9 100644 --- a/chains/evm/calls/utils_test.go +++ b/chains/evm/calls/utils_test.go @@ -1,10 +1,16 @@ -package calls +package calls_test import ( + "errors" + + calls "github.com/ChainSafe/chainbridge-core/chains/evm/calls" + mock_calls "github.com/ChainSafe/chainbridge-core/chains/evm/calls/mock" + "github.com/ChainSafe/chainbridge-core/chains/evm/evmtransaction" "github.com/ChainSafe/chainbridge-core/crypto/secp256k1" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/suite" "math/big" @@ -14,13 +20,19 @@ import ( type UtilsTestSuite struct { suite.Suite + mockClientDispatcher *mock_calls.MockClientDispatcher + mockgasPricer *mock_calls.MockGasPricer } func TestRunUtilsTestSuite(t *testing.T) { suite.Run(t, new(UtilsTestSuite)) } -func (s *UtilsTestSuite) SetupSuite() {} +func (s *UtilsTestSuite) SetupSuite() { + gomockController := gomock.NewController(s.T()) + s.mockClientDispatcher = mock_calls.NewMockClientDispatcher(gomockController) + s.mockgasPricer = mock_calls.NewMockGasPricer(gomockController) +} func (s *UtilsTestSuite) TearDownSuite() {} func (s *UtilsTestSuite) SetupTest() {} func (s *UtilsTestSuite) TearDownTest() {} @@ -39,7 +51,7 @@ func (s *UtilsTestSuite) TestToCallArg() { GasPrice: big.NewInt(3000), Data: []byte("test"), } - got := ToCallArg(msg) + got := calls.ToCallArg(msg) want := map[string]interface{}{ "from": msg.From, "to": msg.To, @@ -53,10 +65,57 @@ func (s *UtilsTestSuite) TestToCallArg() { func (s *UtilsTestSuite) TestToCallArgWithEmptyMessage() { msg := ethereum.CallMsg{} - got := ToCallArg(msg) + got := calls.ToCallArg(msg) want := map[string]interface{}{ "from": common.HexToAddress(""), "to": (*common.Address)(nil), } s.Equal(want, got) } + +func (s *UtilsTestSuite) TestTransactNonceUnlockCallWithErrorThrown() { + s.mockClientDispatcher.EXPECT().LockNonce().Times(1) + s.mockClientDispatcher.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDispatcher.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, errors.New("error")) + s.mockClientDispatcher.EXPECT().UnlockNonce().Times(1) + s.mockClientDispatcher.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(0) + s.mockClientDispatcher.EXPECT().UnsafeIncreaseNonce().Times(0) + + toAddress := common.HexToAddress("0xtest1") + gasLimit := uint64(250000) + amount := big.NewInt(10) + + _, _ = calls.Transact( + s.mockClientDispatcher, + evmtransaction.NewTransaction, + s.mockgasPricer, + &toAddress, + []byte("test"), + gasLimit, + amount) +} + +func (s *UtilsTestSuite) TestTransactNonceUnlockCallWithoutErrorsThrown() { + s.mockClientDispatcher.EXPECT().LockNonce().Times(1) + s.mockClientDispatcher.EXPECT().UnsafeNonce().Return(big.NewInt(1), nil) + s.mockgasPricer.EXPECT().GasPrice().Return([]*big.Int{big.NewInt(10)}, nil) + s.mockClientDispatcher.EXPECT().SignAndSendTransaction(gomock.Any(), gomock.Any()).Times(1).Return(common.Hash{}, nil) + s.mockClientDispatcher.EXPECT().From().Times(1).Return(common.Address{}) + s.mockClientDispatcher.EXPECT().WaitAndReturnTxReceipt(gomock.Any()).Times(1).Return(nil, nil) + s.mockClientDispatcher.EXPECT().UnsafeIncreaseNonce().Times(1) + s.mockClientDispatcher.EXPECT().UnlockNonce().Times(1) + + toAddress := common.HexToAddress("0xtest1") + gasLimit := uint64(250000) + amount := big.NewInt(10) + + _, _ = calls.Transact( + s.mockClientDispatcher, + evmtransaction.NewTransaction, + s.mockgasPricer, + &toAddress, + []byte("test"), + gasLimit, + amount) +}