From c82d5635b699ba1556f04364a45264e625c6f49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Cidre?= Date: Wed, 6 Jun 2018 09:35:32 +0200 Subject: [PATCH] Allow gas and gas input as argument on signing TTXX (#1000) * Allow gas and gas input as argument on signing TTXX * Two different bindings ApproveSignRequest and ApproveSignRequestWithArgs --- cmd/statusd/debug/commands.go | 11 ++++ geth/api/api.go | 9 ++- geth/api/backend.go | 14 ++++- geth/api/backend_test.go | 54 ++++++++++++++++ geth/api/txargs.go | 21 +++++++ geth/transactions/transactor.go | 16 ++++- geth/transactions/transactor_test.go | 93 +++++++++++++++++++++++----- lib/library.go | 19 +++++- services/personal/api.go | 2 +- sign/pending_requests.go | 4 +- sign/pending_requests_test.go | 63 ++++++++++++++----- sign/request.go | 9 ++- 12 files changed, 274 insertions(+), 41 deletions(-) create mode 100644 geth/api/txargs.go diff --git a/cmd/statusd/debug/commands.go b/cmd/statusd/debug/commands.go index 98dbd099b..d95cdb47a 100644 --- a/cmd/statusd/debug/commands.go +++ b/cmd/statusd/debug/commands.go @@ -175,3 +175,14 @@ func (cs *commandSet) ApproveSignRequest(id, password string) (string, error) { } return result.Response.Hex(), nil } + +// ApproveSignRequest instructs API to complete sending of a given transaction. +// gas and gasPrice will be overrided with the given values before signing the +// transaction. +func (cs *commandSet) ApproveSignRequestWithArgs(id, password string, gas, gasPrice int64) (string, error) { + result := cs.statusAPI.ApproveSignRequestWithArgs(id, password, gas, gasPrice) + if result.Error != nil { + return "", result.Error + } + return result.Response.Hex(), nil +} diff --git a/geth/api/api.go b/geth/api/api.go index 966a05f0a..695b06e81 100644 --- a/geth/api/api.go +++ b/geth/api/api.go @@ -158,10 +158,17 @@ func (api *StatusAPI) SendTransaction(ctx context.Context, args transactions.Sen } // ApproveSignRequest instructs backend to complete sending of a given transaction -func (api *StatusAPI) ApproveSignRequest(id string, password string) sign.Result { +func (api *StatusAPI) ApproveSignRequest(id, password string) sign.Result { return api.b.ApproveSignRequest(id, password) } +// ApproveSignRequestWithArgs instructs backend to complete sending of a given transaction +// gas and gasPrice will be overrided with the given values before signing the +// transaction. +func (api *StatusAPI) ApproveSignRequestWithArgs(id, password string, gas, gasPrice int64) sign.Result { + return api.b.ApproveSignRequestWithArgs(id, password, gas, gasPrice) +} + // ApproveSignRequests instructs backend to complete sending of multiple transactions func (api *StatusAPI) ApproveSignRequests(ids []string, password string) map[string]sign.Result { return api.b.ApproveSignRequests(ids, password) diff --git a/geth/api/backend.go b/geth/api/backend.go index 83bd52fa0..b6a04d8fb 100644 --- a/geth/api/backend.go +++ b/geth/api/backend.go @@ -251,9 +251,17 @@ func (b *StatusBackend) getVerifiedAccount(password string) (*account.SelectedEx return selectedAccount, nil } -// ApproveSignRequest instructs backend to complete sending of a given transaction -func (b *StatusBackend) ApproveSignRequest(id string, password string) sign.Result { - return b.pendingSignRequests.Approve(id, password, b.getVerifiedAccount) +// ApproveSignRequest instructs backend to complete sending of a given transaction. +func (b *StatusBackend) ApproveSignRequest(id, password string) sign.Result { + return b.pendingSignRequests.Approve(id, password, nil, b.getVerifiedAccount) +} + +// ApproveSignRequestWithArgs instructs backend to complete sending of a given transaction. +// gas and gasPrice will be overrided with the given values before signing the +// transaction. +func (b *StatusBackend) ApproveSignRequestWithArgs(id, password string, gas, gasPrice int64) sign.Result { + args := prepareTxArgs(gas, gasPrice) + return b.pendingSignRequests.Approve(id, password, &args, b.getVerifiedAccount) } // ApproveSignRequests instructs backend to complete sending of multiple transactions diff --git a/geth/api/backend_test.go b/geth/api/backend_test.go index e58f9f6e9..ad81bf7f8 100644 --- a/geth/api/backend_test.go +++ b/geth/api/backend_test.go @@ -2,10 +2,12 @@ package api import ( "fmt" + "math/big" "math/rand" "sync" "testing" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/params" "github.com/stretchr/testify/assert" @@ -243,4 +245,56 @@ func TestBackendCallRPCConcurrently(t *testing.T) { wg.Wait() } +func TestPrepareTxArgs(t *testing.T) { + var flagtests = []struct { + description string + gas int64 + gasPrice int64 + expectedGas *hexutil.Uint64 + expectedGasPrice *hexutil.Big + }{ + { + description: "Empty gas and gas price", + gas: 0, + gasPrice: 0, + expectedGas: nil, + expectedGasPrice: nil, + }, + { + description: "Non empty gas and gas price", + gas: 1, + gasPrice: 2, + expectedGas: func() *hexutil.Uint64 { + x := hexutil.Uint64(1) + return &x + }(), + expectedGasPrice: (*hexutil.Big)(big.NewInt(2)), + }, + { + description: "Empty gas price", + gas: 1, + gasPrice: 0, + expectedGas: func() *hexutil.Uint64 { + x := hexutil.Uint64(1) + return &x + }(), + expectedGasPrice: nil, + }, + { + description: "Empty gas", + gas: 0, + gasPrice: 2, + expectedGas: nil, + expectedGasPrice: (*hexutil.Big)(big.NewInt(2)), + }, + } + for _, tt := range flagtests { + t.Run(tt.description, func(t *testing.T) { + args := prepareTxArgs(tt.gas, tt.gasPrice) + assert.Equal(t, tt.expectedGas, args.Gas) + assert.Equal(t, tt.expectedGasPrice, args.GasPrice) + }) + } +} + // TODO(adam): add concurrent tests for: SendTransaction, ApproveSignRequest, DiscardSignRequest diff --git a/geth/api/txargs.go b/geth/api/txargs.go new file mode 100644 index 000000000..f0ec1e812 --- /dev/null +++ b/geth/api/txargs.go @@ -0,0 +1,21 @@ +package api + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/status-im/status-go/sign" +) + +// prepareTxArgs given gas and gasPrice will prepare a valid sign.TxArgs. +func prepareTxArgs(gas, gasPrice int64) (args sign.TxArgs) { + if gas > 0 { + g := hexutil.Uint64(gas) + args.Gas = &g + } + if gasPrice > 0 { + gp := (*hexutil.Big)(big.NewInt(gasPrice)) + args.GasPrice = gp + } + return +} diff --git a/geth/transactions/transactor.go b/geth/transactions/transactor.go index 1962a888e..f6f0e9970 100644 --- a/geth/transactions/transactor.go +++ b/geth/transactions/transactor.go @@ -72,7 +72,8 @@ func (t *Transactor) SendTransaction(ctx context.Context, args SendTxArgs) (geth ctx = context.Background() } - completeFunc := func(acc *account.SelectedExtKey, password string) (sign.Response, error) { + completeFunc := func(acc *account.SelectedExtKey, password string, signArgs *sign.TxArgs) (sign.Response, error) { + t.mergeSignTxArgsOntoSendTxArgs(signArgs, &args) hash, err := t.validateAndPropagate(acc, args) return sign.Response(hash.Bytes()), err } @@ -197,8 +198,21 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe } ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout) defer cancel() + if err := t.sender.SendTransaction(ctx, signedTx); err != nil { return hash, err } return signedTx.Hash(), nil } + +func (t *Transactor) mergeSignTxArgsOntoSendTxArgs(signArgs *sign.TxArgs, args *SendTxArgs) { + if signArgs == nil { + return + } + if signArgs.Gas != nil { + args.Gas = signArgs.Gas + } + if signArgs.GasPrice != nil { + args.GasPrice = signArgs.GasPrice + } +} diff --git a/geth/transactions/transactor_test.go b/geth/transactions/transactor_test.go index e75958de7..29d219201 100644 --- a/geth/transactions/transactor_test.go +++ b/geth/transactions/transactor_test.go @@ -76,24 +76,30 @@ func (s *TxQueueTestSuite) TearDownTest() { } var ( - testGas = hexutil.Uint64(defaultGas + 1) - testGasPrice = (*hexutil.Big)(big.NewInt(10)) - testNonce = hexutil.Uint64(10) + testGas = hexutil.Uint64(defaultGas + 1) + testGasPrice = (*hexutil.Big)(big.NewInt(10)) + testOverridenGas = hexutil.Uint64(defaultGas + 2) + testOverridenGasPrice = (*hexutil.Big)(big.NewInt(20)) + testNonce = hexutil.Uint64(10) ) -func (s *TxQueueTestSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce, resultNonce hexutil.Uint64, account *account.SelectedExtKey, txErr error) { +func (s *TxQueueTestSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce, resultNonce hexutil.Uint64, account *account.SelectedExtKey, txErr error, signArgs *sign.TxArgs) { // Expect calls to gas functions only if there are no user defined values. // And also set the expected gas and gas price for RLP encoding the expected tx. var usedGas hexutil.Uint64 var usedGasPrice *big.Int s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), account.Address, gethrpc.PendingBlockNumber).Return(&returnNonce, nil) - if args.GasPrice == nil { + if signArgs != nil && signArgs.GasPrice != nil { + usedGasPrice = (*big.Int)(signArgs.GasPrice) + } else if args.GasPrice == nil { usedGasPrice = (*big.Int)(testGasPrice) s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(usedGasPrice, nil) } else { usedGasPrice = (*big.Int)(args.GasPrice) } - if args.Gas == nil { + if signArgs != nil && signArgs.Gas != nil { + usedGas = *signArgs.Gas + } else if args.Gas == nil { s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil) usedGas = testGas } else { @@ -129,29 +135,80 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() { AccountKey: &keystore.Key{PrivateKey: key}, } testCases := []struct { - name string - gas *hexutil.Uint64 - gasPrice *hexutil.Big + name string + gas *hexutil.Uint64 + gasPrice *hexutil.Big + signTxArgs *sign.TxArgs }{ { "noGasDef", nil, nil, + s.defaultSignTxArgs(), }, { "gasDefined", &testGas, nil, + s.defaultSignTxArgs(), }, { "gasPriceDefined", nil, testGasPrice, + s.defaultSignTxArgs(), }, { "inputPassedInLegacyDataField", nil, testGasPrice, + s.defaultSignTxArgs(), + }, + { + "overrideGas", + nil, + nil, + &sign.TxArgs{ + Gas: &testGas, + }, + }, + { + "overridePreExistingGas", + &testGas, + nil, + &sign.TxArgs{ + Gas: &testOverridenGas, + }, + }, + { + "overridePreExistingGasPrice", + nil, + testGasPrice, + &sign.TxArgs{ + GasPrice: testOverridenGasPrice, + }, + }, + { + "nilSignTransactionSpecificArgs", + nil, + nil, + nil, + }, + { + "overridePreExistingGasWithNil", + &testGas, + nil, + &sign.TxArgs{ + Gas: nil, + }, + }, + { + "overridePreExistingGasPriceWithNil", + nil, + testGasPrice, + &sign.TxArgs{ + GasPrice: nil, + }, }, } @@ -164,7 +221,7 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() { Gas: testCase.gas, GasPrice: testCase.gasPrice, } - s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil) + s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil, testCase.signTxArgs) w := make(chan struct{}) var sendHash gethcommon.Hash @@ -184,7 +241,7 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() { req := s.manager.pendingSignRequests.First() s.NotNil(req) - approveResult := s.manager.pendingSignRequests.Approve(req.ID, "", simpleVerifyFunc(selectedAccount)) + approveResult := s.manager.pendingSignRequests.Approve(req.ID, "", testCase.signTxArgs, simpleVerifyFunc(selectedAccount)) s.NoError(approveResult.Error) s.NoError(WaitClosed(w, time.Second)) @@ -195,6 +252,10 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() { } } +func (s *TxQueueTestSuite) defaultSignTxArgs() *sign.TxArgs { + return &sign.TxArgs{} +} + func (s *TxQueueTestSuite) TestAccountMismatch() { selectedAccount := &account.SelectedExtKey{ Address: account.FromAddress(TestConfig.Account2.Address), @@ -218,7 +279,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() { req := s.manager.pendingSignRequests.First() s.NotNil(req) - result := s.manager.pendingSignRequests.Approve(req.ID, "", simpleVerifyFunc(selectedAccount)) + result := s.manager.pendingSignRequests.Approve(req.ID, "", s.defaultSignTxArgs(), simpleVerifyFunc(selectedAccount)) s.EqualError(result.Error, ErrInvalidCompleteTxSender.Error()) // Transaction should stay in the queue as mismatched accounts @@ -279,7 +340,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() { if req == nil { time.Sleep(time.Millisecond) } else { - s.manager.pendingSignRequests.Approve(req.ID, "", simpleVerifyFunc(selectedAccount)) // nolint: errcheck + s.manager.pendingSignRequests.Approve(req.ID, "", s.defaultSignTxArgs(), simpleVerifyFunc(selectedAccount)) // nolint: errcheck } } }() @@ -289,7 +350,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() { From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), } - s.setupTransactionPoolAPI(args, nonce, hexutil.Uint64(i), selectedAccount, nil) + s.setupTransactionPoolAPI(args, nonce, hexutil.Uint64(i), selectedAccount, nil, nil) _, err := s.manager.SendTransaction(context.Background(), args) s.NoError(err) @@ -303,7 +364,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() { To: account.ToAddress(TestConfig.Account2.Address), } - s.setupTransactionPoolAPI(args, nonce, nonce, selectedAccount, nil) + s.setupTransactionPoolAPI(args, nonce, nonce, selectedAccount, nil, nil) _, err := s.manager.SendTransaction(context.Background(), args) s.NoError(err) @@ -349,7 +410,7 @@ func (s *TxQueueTestSuite) TestContractCreation() { if req == nil { time.Sleep(time.Millisecond) } else { - s.manager.pendingSignRequests.Approve(req.ID, "", simpleVerifyFunc(selectedAccount)) // nolint: errcheck + s.manager.pendingSignRequests.Approve(req.ID, "", s.defaultSignTxArgs(), simpleVerifyFunc(selectedAccount)) // nolint: errcheck break } } diff --git a/lib/library.go b/lib/library.go index d1705e166..1d500f03c 100644 --- a/lib/library.go +++ b/lib/library.go @@ -11,6 +11,7 @@ import ( "github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/logutils" "github.com/status-im/status-go/profiling" + "github.com/status-im/status-go/sign" "gopkg.in/go-playground/validator.v9" ) @@ -206,11 +207,27 @@ func Logout() *C.char { return makeJSONResponse(err) } -//ApproveSignRequest instructs backend to complete sending of a given transaction +//ApproveSignRequestWithArgs instructs backend to complete sending of a given transaction. +// gas and gasPrice will be overrided with the given values before signing the +// transaction. +//export ApproveSignRequestWithArgs +func ApproveSignRequestWithArgs(id, password *C.char, gas, gasPrice C.int) *C.char { + result := statusAPI.ApproveSignRequestWithArgs(C.GoString(id), C.GoString(password), int64(gas), int64(gasPrice)) + + return prepareApproveSignRequestResponse(result, id) +} + +//ApproveSignRequest instructs backend to complete sending of a given transaction. //export ApproveSignRequest func ApproveSignRequest(id, password *C.char) *C.char { result := statusAPI.ApproveSignRequest(C.GoString(id), C.GoString(password)) + return prepareApproveSignRequestResponse(result, id) +} + +// prepareApproveSignRequestResponse based on a sign.Result prepares the binding +// response. +func prepareApproveSignRequestResponse(result sign.Result, id *C.char) *C.char { errString := "" if result.Error != nil { fmt.Fprintln(os.Stderr, result.Error) diff --git a/services/personal/api.go b/services/personal/api.go index fc8442a44..df897409b 100644 --- a/services/personal/api.go +++ b/services/personal/api.go @@ -88,7 +88,7 @@ func (api *PublicAPI) Sign(context context.Context, rpcParams ...interface{}) (i } func (api *PublicAPI) completeFunc(context context.Context, metadata metadata) sign.CompleteFunc { - return func(acc *account.SelectedExtKey, password string) (response sign.Response, err error) { + return func(acc *account.SelectedExtKey, password string, signArgs *sign.TxArgs) (response sign.Response, err error) { response = sign.EmptyResponse err = api.validateAccount(metadata, acc) diff --git a/sign/pending_requests.go b/sign/pending_requests.go index 0919e8fc4..8fd20c0c1 100644 --- a/sign/pending_requests.go +++ b/sign/pending_requests.go @@ -67,7 +67,7 @@ func (rs *PendingRequests) First() *Request { } // Approve a signing request by it's ID. Requires a valid password and a verification function. -func (rs *PendingRequests) Approve(id string, password string, verify verifyFunc) Result { +func (rs *PendingRequests) Approve(id string, password string, args *TxArgs, verify verifyFunc) Result { rs.log.Info("complete sign request", "id", id) request, err := rs.tryLock(id) if err != nil { @@ -81,7 +81,7 @@ func (rs *PendingRequests) Approve(id string, password string, verify verifyFunc return newErrResult(err) } - response, err := request.completeFunc(selectedAccount, password) + response, err := request.completeFunc(selectedAccount, password, args) rs.log.Info("completed sign request ", "id", request.ID, "response", response, "err", err) rs.complete(request, response, err) diff --git a/sign/pending_requests_test.go b/sign/pending_requests_test.go index 15d2fb40e..bdeef7252 100644 --- a/sign/pending_requests_test.go +++ b/sign/pending_requests_test.go @@ -3,12 +3,14 @@ package sign import ( "context" "errors" + "math/big" "sync/atomic" "testing" "time" "github.com/ethereum/go-ethereum/accounts/keystore" gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/geth/account" "github.com/stretchr/testify/suite" @@ -19,6 +21,11 @@ const ( wrongPassword = "password-wrong" ) +var ( + overridenGas = hexutil.Uint64(90002) + overridenGasPrice = (*hexutil.Big)(big.NewInt(20)) +) + func testVerifyFunc(password string) (*account.SelectedExtKey, error) { if password == correctPassword { return nil, nil @@ -40,9 +47,13 @@ func (s *PendingRequestsSuite) SetupTest() { s.pendingRequests = NewPendingRequests() } +func (s *PendingRequestsSuite) defaultSignTxArgs() *TxArgs { + return &TxArgs{} +} + func (s *PendingRequestsSuite) defaultCompleteFunc() CompleteFunc { hash := gethcommon.Hash{1} - return func(acc *account.SelectedExtKey, password string) (Response, error) { + return func(acc *account.SelectedExtKey, password string, args *TxArgs) (Response, error) { s.Nil(acc, "account should be `nil`") s.Equal(correctPassword, password) return hash.Bytes(), nil @@ -51,7 +62,7 @@ func (s *PendingRequestsSuite) defaultCompleteFunc() CompleteFunc { func (s *PendingRequestsSuite) delayedCompleteFunc() CompleteFunc { hash := gethcommon.Hash{1} - return func(acc *account.SelectedExtKey, password string) (Response, error) { + return func(acc *account.SelectedExtKey, password string, args *TxArgs) (Response, error) { time.Sleep(10 * time.Millisecond) s.Nil(acc, "account should be `nil`") s.Equal(correctPassword, password) @@ -59,9 +70,20 @@ func (s *PendingRequestsSuite) delayedCompleteFunc() CompleteFunc { } } +func (s *PendingRequestsSuite) overridenCompleteFunc() CompleteFunc { + hash := gethcommon.Hash{1} + return func(acc *account.SelectedExtKey, password string, args *TxArgs) (Response, error) { + s.Nil(acc, "account should be `nil`") + s.Equal(correctPassword, password) + s.Equal(&overridenGas, args.Gas) + s.Equal(overridenGasPrice, args.GasPrice) + return hash.Bytes(), nil + } +} + func (s *PendingRequestsSuite) errorCompleteFunc(err error) CompleteFunc { hash := gethcommon.Hash{1} - return func(acc *account.SelectedExtKey, password string) (Response, error) { + return func(acc *account.SelectedExtKey, password string, args *TxArgs) (Response, error) { s.Nil(acc, "account should be `nil`") return hash.Bytes(), err } @@ -77,13 +99,13 @@ func (s *PendingRequestsSuite) TestGet() { } } -func (s *PendingRequestsSuite) testComplete(password string, hash gethcommon.Hash, completeFunc CompleteFunc) (string, error) { +func (s *PendingRequestsSuite) testComplete(password string, hash gethcommon.Hash, completeFunc CompleteFunc, signArgs *TxArgs) (string, error) { req, err := s.pendingRequests.Add(context.Background(), "", nil, completeFunc) s.NoError(err) s.True(s.pendingRequests.Has(req.ID), "sign request should exist") - result := s.pendingRequests.Approve(req.ID, password, testVerifyFunc) + result := s.pendingRequests.Approve(req.ID, password, signArgs, testVerifyFunc) if s.pendingRequests.Has(req.ID) { // transient error @@ -96,7 +118,7 @@ func (s *PendingRequestsSuite) testComplete(password string, hash gethcommon.Has } func (s *PendingRequestsSuite) TestCompleteSuccess() { - id, err := s.testComplete(correctPassword, gethcommon.Hash{1}, s.defaultCompleteFunc()) + id, err := s.testComplete(correctPassword, gethcommon.Hash{1}, s.defaultCompleteFunc(), s.defaultSignTxArgs()) s.NoError(err, "no errors should be there") s.False(s.pendingRequests.Has(id), "sign request should not exist") @@ -104,7 +126,7 @@ func (s *PendingRequestsSuite) TestCompleteSuccess() { func (s *PendingRequestsSuite) TestCompleteTransientError() { hash := gethcommon.Hash{} - id, err := s.testComplete(wrongPassword, hash, s.errorCompleteFunc(keystore.ErrDecrypt)) + id, err := s.testComplete(wrongPassword, hash, s.errorCompleteFunc(keystore.ErrDecrypt), s.defaultSignTxArgs()) s.Equal(keystore.ErrDecrypt, err, "error value should be preserved") s.True(s.pendingRequests.Has(id)) @@ -117,7 +139,7 @@ func (s *PendingRequestsSuite) TestCompleteError() { hash := gethcommon.Hash{1} expectedError := errors.New("test") - id, err := s.testComplete(correctPassword, hash, s.errorCompleteFunc(expectedError)) + id, err := s.testComplete(correctPassword, hash, s.errorCompleteFunc(expectedError), s.defaultSignTxArgs()) s.Equal(expectedError, err, "error value should be preserved") @@ -125,10 +147,10 @@ func (s *PendingRequestsSuite) TestCompleteError() { } func (s PendingRequestsSuite) TestMultipleComplete() { - id, err := s.testComplete(correctPassword, gethcommon.Hash{1}, s.defaultCompleteFunc()) + id, err := s.testComplete(correctPassword, gethcommon.Hash{1}, s.defaultCompleteFunc(), s.defaultSignTxArgs()) s.NoError(err, "no errors should be there") - result := s.pendingRequests.Approve(id, correctPassword, testVerifyFunc) + result := s.pendingRequests.Approve(id, correctPassword, s.defaultSignTxArgs(), testVerifyFunc) s.Equal(ErrSignReqNotFound, result.Error) } @@ -144,7 +166,7 @@ func (s PendingRequestsSuite) TestConcurrentComplete() { for i := 10; i > 0; i-- { go func() { - result := s.pendingRequests.Approve(req.ID, correctPassword, testVerifyFunc) + result := s.pendingRequests.Approve(req.ID, correctPassword, s.defaultSignTxArgs(), testVerifyFunc) if result.Error == nil { atomic.AddInt32(&approved, 1) } @@ -167,7 +189,7 @@ func (s PendingRequestsSuite) TestWaitSuccess() { s.True(s.pendingRequests.Has(req.ID), "sign request should exist") go func() { - result := s.pendingRequests.Approve(req.ID, correctPassword, testVerifyFunc) + result := s.pendingRequests.Approve(req.ID, correctPassword, s.defaultSignTxArgs(), testVerifyFunc) s.NoError(result.Error) }() @@ -201,7 +223,7 @@ func (s PendingRequestsSuite) TestWaitFail() { s.True(s.pendingRequests.Has(req.ID), "sign request should exist") go func() { - result := s.pendingRequests.Approve(req.ID, correctPassword, testVerifyFunc) + result := s.pendingRequests.Approve(req.ID, correctPassword, s.defaultSignTxArgs(), testVerifyFunc) s.Equal(expectedError, result.Error) }() @@ -218,7 +240,18 @@ func (s PendingRequestsSuite) TestWaitTimeout() { result := s.pendingRequests.Wait(req.ID, 0*time.Second) s.Equal(ErrSignReqTimedOut, result.Error) - // Try approving the timeouted request, it will fail - result = s.pendingRequests.Approve(req.ID, correctPassword, testVerifyFunc) + // Try approving the timed out request, it will fail + result = s.pendingRequests.Approve(req.ID, correctPassword, s.defaultSignTxArgs(), testVerifyFunc) s.NotNil(result.Error) } + +func (s *PendingRequestsSuite) TestCompleteSuccessWithOverridenGas() { + txArgs := TxArgs{ + Gas: &overridenGas, + GasPrice: overridenGasPrice, + } + id, err := s.testComplete(correctPassword, gethcommon.Hash{1}, s.overridenCompleteFunc(), &txArgs) + s.NoError(err, "no errors should be there") + + s.False(s.pendingRequests.Has(id), "sign request should not exist") +} diff --git a/sign/request.go b/sign/request.go index d8c74445d..68afd1d92 100644 --- a/sign/request.go +++ b/sign/request.go @@ -3,12 +3,13 @@ package sign import ( "context" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pborman/uuid" "github.com/status-im/status-go/geth/account" ) // CompleteFunc is a function that is called after the sign request is approved. -type CompleteFunc func(account *account.SelectedExtKey, password string) (Response, error) +type CompleteFunc func(account *account.SelectedExtKey, password string, completeArgs *TxArgs) (Response, error) // Meta represents any metadata that could be attached to a signing request. // It will be JSON-serialized and used in notifications to the API consumer. @@ -25,6 +26,12 @@ type Request struct { result chan Result } +// TxArgs represents the arguments to submit when signing a transaction +type TxArgs struct { + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` +} + func newRequest(ctx context.Context, method string, meta Meta, completeFunc CompleteFunc) *Request { return &Request{ ID: uuid.New(),