Merge pull request #773 from status-im/transactions-and-accountmanager
[#772] txQueueManager to not depend on AccountManager and nodeManager
This commit is contained in:
commit
e646001578
|
@ -152,12 +152,12 @@ func (api *StatusAPI) SendTransaction(ctx context.Context, args common.SendTxArg
|
|||
|
||||
// CompleteTransaction instructs backend to complete sending of a given transaction
|
||||
func (api *StatusAPI) CompleteTransaction(id common.QueuedTxID, password string) (gethcommon.Hash, error) {
|
||||
return api.b.txQueueManager.CompleteTransaction(id, password)
|
||||
return api.b.CompleteTransaction(id, password)
|
||||
}
|
||||
|
||||
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
||||
func (api *StatusAPI) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult {
|
||||
return api.b.txQueueManager.CompleteTransactions(ids, password)
|
||||
return api.b.CompleteTransactions(ids, password)
|
||||
}
|
||||
|
||||
// DiscardTransaction discards a given transaction from transaction queue
|
||||
|
|
|
@ -49,7 +49,7 @@ func NewStatusBackend() *StatusBackend {
|
|||
|
||||
nodeManager := node.NewNodeManager()
|
||||
accountManager := account.NewManager(nodeManager)
|
||||
txQueueManager := transactions.NewManager(nodeManager, accountManager)
|
||||
txQueueManager := transactions.NewManager(nodeManager)
|
||||
jailManager := jail.New(nodeManager)
|
||||
notificationManager := fcm.NewNotification(fcmServerKey)
|
||||
|
||||
|
@ -120,7 +120,7 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
|
|||
signal.Send(signal.Envelope{Type: signal.EventNodeStarted})
|
||||
// tx queue manager should be started after node is started, it depends
|
||||
// on rpc client being created
|
||||
b.txQueueManager.Start()
|
||||
b.txQueueManager.Start(config.NetworkID)
|
||||
if err := b.registerHandlers(); err != nil {
|
||||
b.log.Error("Handler registration failed", "err", err)
|
||||
}
|
||||
|
@ -208,14 +208,46 @@ func (b *StatusBackend) SendTransaction(ctx context.Context, args common.SendTxA
|
|||
return rst.Hash, nil
|
||||
}
|
||||
|
||||
func (b *StatusBackend) getVerifiedAccount(password string) (*account.SelectedExtKey, error) {
|
||||
selectedAccount, err := b.accountManager.SelectedAccount()
|
||||
if err != nil {
|
||||
b.log.Error("failed to get a selected account", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
config, err := b.NodeManager().NodeConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = b.accountManager.VerifyAccountPassword(config.KeyStoreDir, selectedAccount.Address.String(), password)
|
||||
if err != nil {
|
||||
b.log.Error("failed to verify account", "account", selectedAccount.Address.String(), "error", err)
|
||||
return nil, err
|
||||
}
|
||||
return selectedAccount, nil
|
||||
}
|
||||
|
||||
// CompleteTransaction instructs backend to complete sending of a given transaction
|
||||
func (b *StatusBackend) CompleteTransaction(id common.QueuedTxID, password string) (gethcommon.Hash, error) {
|
||||
return b.txQueueManager.CompleteTransaction(id, password)
|
||||
func (b *StatusBackend) CompleteTransaction(id common.QueuedTxID, password string) (hash gethcommon.Hash, err error) {
|
||||
selectedAccount, err := b.getVerifiedAccount(password)
|
||||
if err != nil {
|
||||
_ = b.txQueueManager.NotifyErrored(id, err)
|
||||
return hash, err
|
||||
}
|
||||
|
||||
return b.txQueueManager.CompleteTransaction(id, selectedAccount)
|
||||
}
|
||||
|
||||
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
||||
func (b *StatusBackend) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult {
|
||||
return b.txQueueManager.CompleteTransactions(ids, password)
|
||||
results := make(map[common.QueuedTxID]common.TransactionResult)
|
||||
for _, txID := range ids {
|
||||
txHash, txErr := b.CompleteTransaction(txID, password)
|
||||
results[txID] = common.TransactionResult{
|
||||
Hash: txHash,
|
||||
Error: txErr,
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// DiscardTransaction discards a given transaction from transaction queue
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
package transactions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
)
|
||||
|
||||
// MockAccountManager is a mock of AccountManager interface
|
||||
type MockAccountManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockAccountManagerMockRecorder
|
||||
}
|
||||
|
||||
// MockAccountManagerMockRecorder is the mock recorder for MockAccountManager
|
||||
type MockAccountManagerMockRecorder struct {
|
||||
mock *MockAccountManager
|
||||
}
|
||||
|
||||
// NewMockAccountManager creates a new mock instance
|
||||
func NewMockAccountManager(ctrl *gomock.Controller) *MockAccountManager {
|
||||
mock := &MockAccountManager{ctrl: ctrl}
|
||||
mock.recorder = &MockAccountManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockAccountManager) EXPECT() *MockAccountManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// VerifyAccountPassword mocks base method
|
||||
func (m *MockAccountManager) VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
||||
ret := m.ctrl.Call(m, "VerifyAccountPassword", keyStoreDir, address, password)
|
||||
ret0, _ := ret[0].(*keystore.Key)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// VerifyAccountPassword indicates an expected call of VerifyAccountPassword
|
||||
func (mr *MockAccountManagerMockRecorder) VerifyAccountPassword(keyStoreDir, address, password interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyAccountPassword", reflect.TypeOf((*MockAccountManager)(nil).VerifyAccountPassword), keyStoreDir, address, password)
|
||||
}
|
||||
|
||||
// SelectedAccount mocks base method
|
||||
func (m *MockAccountManager) SelectedAccount() (*account.SelectedExtKey, error) {
|
||||
ret := m.ctrl.Call(m, "SelectedAccount")
|
||||
ret0, _ := ret[0].(*account.SelectedExtKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SelectedAccount indicates an expected call of SelectedAccount
|
||||
func (mr *MockAccountManagerMockRecorder) SelectedAccount() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectedAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectedAccount))
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package transactions
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
)
|
||||
|
||||
// Accounter defines expected methods for managing Status accounts.
|
||||
type Accounter interface {
|
||||
// SelectedAccount returns currently selected account
|
||||
SelectedAccount() (*account.SelectedExtKey, error)
|
||||
|
||||
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
||||
// If no error is returned, then account is considered verified.
|
||||
VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error)
|
||||
}
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
"github.com/status-im/status-go/geth/transactions/queue"
|
||||
)
|
||||
|
||||
|
@ -27,15 +27,21 @@ const (
|
|||
defaultTimeout = time.Minute
|
||||
)
|
||||
|
||||
// RPCClientProvider is an interface that provides a way
|
||||
// to obtain an rpc.Client.
|
||||
type RPCClientProvider interface {
|
||||
RPCClient() *rpc.Client
|
||||
}
|
||||
|
||||
// Manager provides means to manage internal Status Backend (injected into LES)
|
||||
type Manager struct {
|
||||
nodeManager common.NodeManager
|
||||
accountManager Accounter
|
||||
rpcClientProvider RPCClientProvider
|
||||
txQueue *queue.TxQueue
|
||||
ethTxClient EthTransactor
|
||||
notify bool
|
||||
completionTimeout time.Duration
|
||||
rpcCallTimeout time.Duration
|
||||
networkID uint64
|
||||
|
||||
addrLock *AddrLocker
|
||||
localNonce sync.Map
|
||||
|
@ -43,10 +49,9 @@ type Manager struct {
|
|||
}
|
||||
|
||||
// NewManager returns a new Manager.
|
||||
func NewManager(nodeManager common.NodeManager, accountManager Accounter) *Manager {
|
||||
func NewManager(rpcClientProvider RPCClientProvider) *Manager {
|
||||
return &Manager{
|
||||
nodeManager: nodeManager,
|
||||
accountManager: accountManager,
|
||||
rpcClientProvider: rpcClientProvider,
|
||||
txQueue: queue.New(),
|
||||
addrLock: &AddrLocker{},
|
||||
notify: true,
|
||||
|
@ -64,9 +69,10 @@ func (m *Manager) DisableNotificactions() {
|
|||
}
|
||||
|
||||
// Start starts accepting new transactions into the queue.
|
||||
func (m *Manager) Start() {
|
||||
func (m *Manager) Start(networkID uint64) {
|
||||
m.log.Info("start Manager")
|
||||
m.ethTxClient = NewEthTxClient(m.nodeManager.RPCClient())
|
||||
m.networkID = networkID
|
||||
m.ethTxClient = NewEthTxClient(m.rpcClientProvider.RPCClient())
|
||||
m.txQueue.Start()
|
||||
}
|
||||
|
||||
|
@ -125,8 +131,23 @@ func (m *Manager) WaitForTransaction(tx *common.QueuedTx) common.TransactionResu
|
|||
}
|
||||
}
|
||||
|
||||
// NotifyErrored sends a notification for the given transaction
|
||||
func (m *Manager) NotifyErrored(id common.QueuedTxID, inputError error) error {
|
||||
tx, err := m.txQueue.Get(id)
|
||||
if err != nil {
|
||||
m.log.Warn("error getting a queued transaction", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if m.notify {
|
||||
NotifyOnReturn(tx, inputError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompleteTransaction instructs backend to complete sending of a given transaction.
|
||||
func (m *Manager) CompleteTransaction(id common.QueuedTxID, password string) (hash gethcommon.Hash, err error) {
|
||||
func (m *Manager) CompleteTransaction(id common.QueuedTxID, account *account.SelectedExtKey) (hash gethcommon.Hash, err error) {
|
||||
m.log.Info("complete transaction", "id", id)
|
||||
tx, err := m.txQueue.Get(id)
|
||||
if err != nil {
|
||||
|
@ -137,41 +158,33 @@ func (m *Manager) CompleteTransaction(id common.QueuedTxID, password string) (ha
|
|||
m.log.Warn("can't process transaction", "err", err)
|
||||
return hash, err
|
||||
}
|
||||
config, err := m.nodeManager.NodeConfig()
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
account, err := m.validateAccount(config, tx, password)
|
||||
if err != nil {
|
||||
|
||||
if err := m.validateAccount(tx, account); err != nil {
|
||||
m.txDone(tx, hash, err)
|
||||
return hash, err
|
||||
}
|
||||
hash, err = m.completeTransaction(config, account, tx)
|
||||
hash, err = m.completeTransaction(account, tx)
|
||||
m.log.Info("finally completed transaction", "id", tx.ID, "hash", hash, "err", err)
|
||||
m.txDone(tx, hash, err)
|
||||
return hash, err
|
||||
}
|
||||
|
||||
func (m *Manager) validateAccount(config *params.NodeConfig, tx *common.QueuedTx, password string) (*account.SelectedExtKey, error) {
|
||||
selectedAccount, err := m.accountManager.SelectedAccount()
|
||||
if err != nil {
|
||||
m.log.Warn("failed to get a selected account", "err", err)
|
||||
return nil, err
|
||||
// make sure that only account which created the tx can complete it
|
||||
func (m *Manager) validateAccount(tx *common.QueuedTx, selectedAccount *account.SelectedExtKey) error {
|
||||
if selectedAccount == nil {
|
||||
return account.ErrNoAccountSelected
|
||||
}
|
||||
|
||||
// make sure that only account which created the tx can complete it
|
||||
if tx.Args.From.Hex() != selectedAccount.Address.Hex() {
|
||||
m.log.Warn("queued transaction does not belong to the selected account", "err", queue.ErrInvalidCompleteTxSender)
|
||||
return nil, queue.ErrInvalidCompleteTxSender
|
||||
return queue.ErrInvalidCompleteTxSender
|
||||
}
|
||||
_, err = m.accountManager.VerifyAccountPassword(config.KeyStoreDir, selectedAccount.Address.String(), password)
|
||||
if err != nil {
|
||||
m.log.Warn("failed to verify account", "account", selectedAccount.Address.String(), "error", err)
|
||||
return nil, err
|
||||
}
|
||||
return selectedAccount, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) completeTransaction(config *params.NodeConfig, selectedAccount *account.SelectedExtKey, queuedTx *common.QueuedTx) (hash gethcommon.Hash, err error) {
|
||||
func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, queuedTx *common.QueuedTx) (hash gethcommon.Hash, err error) {
|
||||
m.log.Info("complete transaction", "id", queuedTx.ID)
|
||||
m.addrLock.LockAddr(queuedTx.Args.From)
|
||||
var localNonce uint64
|
||||
|
@ -210,7 +223,7 @@ func (m *Manager) completeTransaction(config *params.NodeConfig, selectedAccount
|
|||
}
|
||||
}
|
||||
|
||||
chainID := big.NewInt(int64(config.NetworkID))
|
||||
chainID := big.NewInt(int64(m.networkID))
|
||||
value := (*big.Int)(args.Value)
|
||||
toAddr := gethcommon.Address{}
|
||||
if args.To != nil {
|
||||
|
@ -260,19 +273,6 @@ func (m *Manager) completeTransaction(config *params.NodeConfig, selectedAccount
|
|||
return signedTx.Hash(), nil
|
||||
}
|
||||
|
||||
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
||||
func (m *Manager) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult {
|
||||
results := make(map[common.QueuedTxID]common.TransactionResult)
|
||||
for _, txID := range ids {
|
||||
txHash, txErr := m.CompleteTransaction(txID, password)
|
||||
results[txID] = common.TransactionResult{
|
||||
Hash: txHash,
|
||||
Error: txErr,
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// DiscardTransaction discards a given transaction from transaction queue
|
||||
func (m *Manager) DiscardTransaction(id common.QueuedTxID) error {
|
||||
tx, err := m.txQueue.Get(id)
|
||||
|
|
|
@ -35,8 +35,6 @@ type TxQueueTestSuite struct {
|
|||
suite.Suite
|
||||
nodeManagerMockCtrl *gomock.Controller
|
||||
nodeManagerMock *common.MockNodeManager
|
||||
accountManagerMockCtrl *gomock.Controller
|
||||
accountManagerMock *MockAccountManager
|
||||
server *gethrpc.Server
|
||||
client *gethrpc.Client
|
||||
txServiceMockCtrl *gomock.Controller
|
||||
|
@ -48,11 +46,9 @@ type TxQueueTestSuite struct {
|
|||
|
||||
func (s *TxQueueTestSuite) SetupTest() {
|
||||
s.nodeManagerMockCtrl = gomock.NewController(s.T())
|
||||
s.accountManagerMockCtrl = gomock.NewController(s.T())
|
||||
s.txServiceMockCtrl = gomock.NewController(s.T())
|
||||
|
||||
s.nodeManagerMock = common.NewMockNodeManager(s.nodeManagerMockCtrl)
|
||||
s.accountManagerMock = NewMockAccountManager(s.accountManagerMockCtrl)
|
||||
|
||||
s.server, s.txServiceMock = fake.NewTestServer(s.txServiceMockCtrl)
|
||||
s.client = gethrpc.DialInProc(s.server)
|
||||
|
@ -62,17 +58,16 @@ func (s *TxQueueTestSuite) SetupTest() {
|
|||
s.Require().NoError(err)
|
||||
s.nodeConfig = nodeConfig
|
||||
|
||||
s.manager = NewManager(s.nodeManagerMock, s.accountManagerMock)
|
||||
s.manager = NewManager(s.nodeManagerMock)
|
||||
s.manager.DisableNotificactions()
|
||||
s.manager.completionTimeout = time.Second
|
||||
s.manager.rpcCallTimeout = time.Second
|
||||
s.manager.Start()
|
||||
s.manager.Start(params.RopstenNetworkID)
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TearDownTest() {
|
||||
s.manager.Stop()
|
||||
s.nodeManagerMockCtrl.Finish()
|
||||
s.accountManagerMockCtrl.Finish()
|
||||
s.txServiceMockCtrl.Finish()
|
||||
s.server.Stop()
|
||||
s.client.Close()
|
||||
|
@ -125,15 +120,7 @@ func (s *TxQueueTestSuite) rlpEncodeTx(tx *common.QueuedTx, config *params.NodeC
|
|||
return hexutil.Bytes(data)
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) setupStatusBackend(account *account.SelectedExtKey, password string, passwordErr error) {
|
||||
s.nodeManagerMock.EXPECT().NodeConfig().Return(s.nodeConfig, nil)
|
||||
s.accountManagerMock.EXPECT().SelectedAccount().Return(account, nil)
|
||||
s.accountManagerMock.EXPECT().VerifyAccountPassword(s.nodeConfig.KeyStoreDir, account.Address.String(), password).Return(
|
||||
nil, passwordErr)
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
||||
password := TestConfig.Account1.Password
|
||||
key, _ := crypto.GenerateKey()
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||
|
@ -169,7 +156,6 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
|||
for _, testCase := range testCases {
|
||||
s.T().Run(testCase.name, func(t *testing.T) {
|
||||
s.SetupTest()
|
||||
s.setupStatusBackend(selectedAccount, password, nil)
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
To: account.ToAddress(TestConfig.Account2.Address),
|
||||
|
@ -185,7 +171,7 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
|||
err error
|
||||
)
|
||||
go func() {
|
||||
hash, err = s.manager.CompleteTransaction(tx.ID, password)
|
||||
hash, err = s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||
s.NoError(err)
|
||||
close(w)
|
||||
}()
|
||||
|
@ -202,13 +188,11 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
|||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
||||
password := TestConfig.Account1.Password
|
||||
key, _ := crypto.GenerateKey()
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||
AccountKey: &keystore.Key{PrivateKey: key},
|
||||
}
|
||||
s.setupStatusBackend(selectedAccount, password, nil)
|
||||
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
|
@ -231,7 +215,7 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, err := s.manager.CompleteTransaction(tx.ID, password)
|
||||
_, err := s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if err == nil {
|
||||
|
@ -257,42 +241,19 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
|||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestAccountMismatch() {
|
||||
s.nodeManagerMock.EXPECT().NodeConfig().Return(s.nodeConfig, nil)
|
||||
s.accountManagerMock.EXPECT().SelectedAccount().Return(&account.SelectedExtKey{
|
||||
Address: account.FromAddress(TestConfig.Account2.Address),
|
||||
}, nil)
|
||||
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
To: account.ToAddress(TestConfig.Account2.Address),
|
||||
})
|
||||
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
|
||||
_, err := s.manager.CompleteTransaction(tx.ID, TestConfig.Account1.Password)
|
||||
s.Equal(err, queue.ErrInvalidCompleteTxSender)
|
||||
|
||||
// Transaction should stay in the queue as mismatched accounts
|
||||
// is a recoverable error.
|
||||
s.True(s.manager.TransactionQueue().Has(tx.ID))
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestInvalidPassword() {
|
||||
password := "invalid-password"
|
||||
key, _ := crypto.GenerateKey()
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||
AccountKey: &keystore.Key{PrivateKey: key},
|
||||
Address: account.FromAddress(TestConfig.Account2.Address),
|
||||
}
|
||||
s.setupStatusBackend(selectedAccount, password, keystore.ErrDecrypt)
|
||||
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
To: account.ToAddress(TestConfig.Account2.Address),
|
||||
})
|
||||
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
_, err := s.manager.CompleteTransaction(tx.ID, password)
|
||||
s.Equal(err.Error(), keystore.ErrDecrypt.Error())
|
||||
|
||||
_, err := s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||
s.Equal(err, queue.ErrInvalidCompleteTxSender)
|
||||
|
||||
// Transaction should stay in the queue as mismatched accounts
|
||||
// is a recoverable error.
|
||||
|
@ -319,6 +280,54 @@ func (s *TxQueueTestSuite) TestDiscardTransaction() {
|
|||
s.NoError(WaitClosed(w, time.Second))
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestDiscardTransactions() {
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
To: account.ToAddress(TestConfig.Account2.Address),
|
||||
})
|
||||
var ids []common.QueuedTxID
|
||||
ids = append(ids, tx.ID)
|
||||
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
w := make(chan struct{})
|
||||
go func() {
|
||||
result := s.manager.DiscardTransactions(ids)
|
||||
s.Equal(0, len(result))
|
||||
close(w)
|
||||
}()
|
||||
|
||||
rst := s.manager.WaitForTransaction(tx)
|
||||
s.Equal(ErrQueuedTxDiscarded, rst.Error)
|
||||
// Transaction should be already removed from the queue.
|
||||
s.False(s.manager.TransactionQueue().Has(tx.ID))
|
||||
s.NoError(WaitClosed(w, time.Second))
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestDiscardTransactionsOnError() {
|
||||
fakeTxID := common.QueuedTxID("7ab94f26-a866-4aba-1234-b4bbe98737a9")
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
To: account.ToAddress(TestConfig.Account2.Address),
|
||||
})
|
||||
var ids []common.QueuedTxID
|
||||
ids = append(ids, fakeTxID)
|
||||
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
w := make(chan struct{})
|
||||
go func() {
|
||||
result := s.manager.DiscardTransactions(ids)
|
||||
s.Equal(1, len(result))
|
||||
s.Equal(queue.ErrQueuedTxIDNotFound, result[fakeTxID].Error)
|
||||
close(w)
|
||||
}()
|
||||
|
||||
rst := s.manager.WaitForTransaction(tx)
|
||||
s.Equal(ErrQueuedTxTimedOut, rst.Error)
|
||||
// Transaction should be already removed from the queue.
|
||||
s.False(s.manager.TransactionQueue().Has(tx.ID))
|
||||
s.NoError(WaitClosed(w, time.Second))
|
||||
}
|
||||
|
||||
func (s *TxQueueTestSuite) TestCompletionTimedOut() {
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
From: account.FromAddress(TestConfig.Account1.Address),
|
||||
|
@ -339,16 +348,11 @@ func (s *TxQueueTestSuite) TestCompletionTimedOut() {
|
|||
// as the last step, we verify that if tx failed nonce is not updated
|
||||
func (s *TxQueueTestSuite) TestLocalNonce() {
|
||||
txCount := 3
|
||||
password := TestConfig.Account1.Password
|
||||
key, _ := crypto.GenerateKey()
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||
AccountKey: &keystore.Key{PrivateKey: key},
|
||||
}
|
||||
// setup call expectations for 5 transactions in total
|
||||
for i := 0; i < txCount+2; i++ {
|
||||
s.setupStatusBackend(selectedAccount, password, nil)
|
||||
}
|
||||
nonce := hexutil.Uint64(0)
|
||||
for i := 0; i < txCount; i++ {
|
||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||
|
@ -357,7 +361,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
|
|||
})
|
||||
s.setupTransactionPoolAPI(tx, nonce, hexutil.Uint64(i), selectedAccount, nil)
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
hash, err := s.manager.CompleteTransaction(tx.ID, password)
|
||||
hash, err := s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||
rst := s.manager.WaitForTransaction(tx)
|
||||
// simple sanity checks
|
||||
s.NoError(err)
|
||||
|
@ -373,7 +377,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
|
|||
})
|
||||
s.setupTransactionPoolAPI(tx, nonce, nonce, selectedAccount, nil)
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
hash, err := s.manager.CompleteTransaction(tx.ID, password)
|
||||
hash, err := s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||
rst := s.manager.WaitForTransaction(tx)
|
||||
s.NoError(err)
|
||||
s.NoError(rst.Error)
|
||||
|
@ -388,7 +392,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
|
|||
To: account.ToAddress(TestConfig.Account2.Address),
|
||||
})
|
||||
s.NoError(s.manager.QueueTransaction(tx))
|
||||
_, err = s.manager.CompleteTransaction(tx.ID, password)
|
||||
_, err = s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||
rst = s.manager.WaitForTransaction(tx)
|
||||
s.EqualError(testErr, err.Error())
|
||||
s.EqualError(testErr, rst.Error.Error())
|
||||
|
|
|
@ -599,12 +599,12 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() {
|
|||
txIDs = append(txIDs, "invalid-tx-id")
|
||||
|
||||
// discard
|
||||
discardResults := txQueueManager.DiscardTransactions(txIDs)
|
||||
discardResults := s.Backend.DiscardTransactions(txIDs)
|
||||
require.Len(discardResults, 1, "cannot discard txs: %v", discardResults)
|
||||
require.Error(discardResults["invalid-tx-id"].Error, "transaction hash not found", "cannot discard txs: %v", discardResults)
|
||||
|
||||
// try completing discarded transaction
|
||||
completeResults := txQueueManager.CompleteTransactions(txIDs, TestConfig.Account1.Password)
|
||||
completeResults := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
|
||||
require.Len(completeResults, testTxCount+1, "unexpected number of errors (call to CompleteTransaction should not succeed)")
|
||||
|
||||
for _, txResult := range completeResults {
|
||||
|
|
Loading…
Reference in New Issue