[#772] txQueueManager to not depend on AccountManager
This commit is contained in:
parent
9bb732d981
commit
13cb2daaa2
|
@ -152,12 +152,12 @@ func (api *StatusAPI) SendTransaction(ctx context.Context, args common.SendTxArg
|
||||||
|
|
||||||
// CompleteTransaction instructs backend to complete sending of a given transaction
|
// CompleteTransaction instructs backend to complete sending of a given transaction
|
||||||
func (api *StatusAPI) CompleteTransaction(id common.QueuedTxID, password string) (gethcommon.Hash, error) {
|
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
|
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
||||||
func (api *StatusAPI) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult {
|
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
|
// DiscardTransaction discards a given transaction from transaction queue
|
||||||
|
|
|
@ -49,7 +49,7 @@ func NewStatusBackend() *StatusBackend {
|
||||||
|
|
||||||
nodeManager := node.NewNodeManager()
|
nodeManager := node.NewNodeManager()
|
||||||
accountManager := account.NewManager(nodeManager)
|
accountManager := account.NewManager(nodeManager)
|
||||||
txQueueManager := transactions.NewManager(nodeManager, accountManager)
|
txQueueManager := transactions.NewManager(nodeManager)
|
||||||
jailManager := jail.New(nodeManager)
|
jailManager := jail.New(nodeManager)
|
||||||
notificationManager := fcm.NewNotification(fcmServerKey)
|
notificationManager := fcm.NewNotification(fcmServerKey)
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
|
||||||
signal.Send(signal.Envelope{Type: signal.EventNodeStarted})
|
signal.Send(signal.Envelope{Type: signal.EventNodeStarted})
|
||||||
// tx queue manager should be started after node is started, it depends
|
// tx queue manager should be started after node is started, it depends
|
||||||
// on rpc client being created
|
// on rpc client being created
|
||||||
b.txQueueManager.Start()
|
b.txQueueManager.Start(config.NetworkID)
|
||||||
if err := b.registerHandlers(); err != nil {
|
if err := b.registerHandlers(); err != nil {
|
||||||
b.log.Error("Handler registration failed", "err", err)
|
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
|
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
|
// CompleteTransaction instructs backend to complete sending of a given transaction
|
||||||
func (b *StatusBackend) CompleteTransaction(id common.QueuedTxID, password string) (gethcommon.Hash, error) {
|
func (b *StatusBackend) CompleteTransaction(id common.QueuedTxID, password string) (hash gethcommon.Hash, err error) {
|
||||||
return b.txQueueManager.CompleteTransaction(id, password)
|
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
|
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
||||||
func (b *StatusBackend) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult {
|
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
|
// 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/ethereum/go-ethereum/core/types"
|
||||||
"github.com/status-im/status-go/geth/account"
|
"github.com/status-im/status-go/geth/account"
|
||||||
"github.com/status-im/status-go/geth/common"
|
"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"
|
"github.com/status-im/status-go/geth/transactions/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,15 +27,21 @@ const (
|
||||||
defaultTimeout = time.Minute
|
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)
|
// Manager provides means to manage internal Status Backend (injected into LES)
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
nodeManager common.NodeManager
|
rpcClientProvider RPCClientProvider
|
||||||
accountManager Accounter
|
|
||||||
txQueue *queue.TxQueue
|
txQueue *queue.TxQueue
|
||||||
ethTxClient EthTransactor
|
ethTxClient EthTransactor
|
||||||
notify bool
|
notify bool
|
||||||
completionTimeout time.Duration
|
completionTimeout time.Duration
|
||||||
rpcCallTimeout time.Duration
|
rpcCallTimeout time.Duration
|
||||||
|
networkID uint64
|
||||||
|
|
||||||
addrLock *AddrLocker
|
addrLock *AddrLocker
|
||||||
localNonce sync.Map
|
localNonce sync.Map
|
||||||
|
@ -43,10 +49,9 @@ type Manager struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a new Manager.
|
// NewManager returns a new Manager.
|
||||||
func NewManager(nodeManager common.NodeManager, accountManager Accounter) *Manager {
|
func NewManager(rpcClientProvider RPCClientProvider) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
nodeManager: nodeManager,
|
rpcClientProvider: rpcClientProvider,
|
||||||
accountManager: accountManager,
|
|
||||||
txQueue: queue.New(),
|
txQueue: queue.New(),
|
||||||
addrLock: &AddrLocker{},
|
addrLock: &AddrLocker{},
|
||||||
notify: true,
|
notify: true,
|
||||||
|
@ -64,9 +69,10 @@ func (m *Manager) DisableNotificactions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts accepting new transactions into the queue.
|
// Start starts accepting new transactions into the queue.
|
||||||
func (m *Manager) Start() {
|
func (m *Manager) Start(networkID uint64) {
|
||||||
m.log.Info("start Manager")
|
m.log.Info("start Manager")
|
||||||
m.ethTxClient = NewEthTxClient(m.nodeManager.RPCClient())
|
m.networkID = networkID
|
||||||
|
m.ethTxClient = NewEthTxClient(m.rpcClientProvider.RPCClient())
|
||||||
m.txQueue.Start()
|
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.
|
// 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)
|
m.log.Info("complete transaction", "id", id)
|
||||||
tx, err := m.txQueue.Get(id)
|
tx, err := m.txQueue.Get(id)
|
||||||
if err != nil {
|
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)
|
m.log.Warn("can't process transaction", "err", err)
|
||||||
return hash, err
|
return hash, err
|
||||||
}
|
}
|
||||||
config, err := m.nodeManager.NodeConfig()
|
|
||||||
if err != nil {
|
if err := m.validateAccount(tx, account); err != nil {
|
||||||
return hash, err
|
|
||||||
}
|
|
||||||
account, err := m.validateAccount(config, tx, password)
|
|
||||||
if err != nil {
|
|
||||||
m.txDone(tx, hash, err)
|
m.txDone(tx, hash, err)
|
||||||
return 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.log.Info("finally completed transaction", "id", tx.ID, "hash", hash, "err", err)
|
||||||
m.txDone(tx, hash, err)
|
m.txDone(tx, hash, err)
|
||||||
return hash, err
|
return hash, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) validateAccount(config *params.NodeConfig, tx *common.QueuedTx, password string) (*account.SelectedExtKey, error) {
|
// make sure that only account which created the tx can complete it
|
||||||
selectedAccount, err := m.accountManager.SelectedAccount()
|
func (m *Manager) validateAccount(tx *common.QueuedTx, selectedAccount *account.SelectedExtKey) error {
|
||||||
if err != nil {
|
if selectedAccount == nil {
|
||||||
m.log.Warn("failed to get a selected account", "err", err)
|
return account.ErrNoAccountSelected
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that only account which created the tx can complete it
|
// make sure that only account which created the tx can complete it
|
||||||
if tx.Args.From.Hex() != selectedAccount.Address.Hex() {
|
if tx.Args.From.Hex() != selectedAccount.Address.Hex() {
|
||||||
m.log.Warn("queued transaction does not belong to the selected account", "err", queue.ErrInvalidCompleteTxSender)
|
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 {
|
return nil
|
||||||
m.log.Warn("failed to verify account", "account", selectedAccount.Address.String(), "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return selectedAccount, 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.log.Info("complete transaction", "id", queuedTx.ID)
|
||||||
m.addrLock.LockAddr(queuedTx.Args.From)
|
m.addrLock.LockAddr(queuedTx.Args.From)
|
||||||
var localNonce uint64
|
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)
|
value := (*big.Int)(args.Value)
|
||||||
toAddr := gethcommon.Address{}
|
toAddr := gethcommon.Address{}
|
||||||
if args.To != nil {
|
if args.To != nil {
|
||||||
|
@ -260,19 +273,6 @@ func (m *Manager) completeTransaction(config *params.NodeConfig, selectedAccount
|
||||||
return signedTx.Hash(), nil
|
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
|
// DiscardTransaction discards a given transaction from transaction queue
|
||||||
func (m *Manager) DiscardTransaction(id common.QueuedTxID) error {
|
func (m *Manager) DiscardTransaction(id common.QueuedTxID) error {
|
||||||
tx, err := m.txQueue.Get(id)
|
tx, err := m.txQueue.Get(id)
|
||||||
|
|
|
@ -33,26 +33,22 @@ func TestTxQueueTestSuite(t *testing.T) {
|
||||||
|
|
||||||
type TxQueueTestSuite struct {
|
type TxQueueTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
nodeManagerMockCtrl *gomock.Controller
|
nodeManagerMockCtrl *gomock.Controller
|
||||||
nodeManagerMock *common.MockNodeManager
|
nodeManagerMock *common.MockNodeManager
|
||||||
accountManagerMockCtrl *gomock.Controller
|
server *gethrpc.Server
|
||||||
accountManagerMock *MockAccountManager
|
client *gethrpc.Client
|
||||||
server *gethrpc.Server
|
txServiceMockCtrl *gomock.Controller
|
||||||
client *gethrpc.Client
|
txServiceMock *fake.MockPublicTransactionPoolAPI
|
||||||
txServiceMockCtrl *gomock.Controller
|
nodeConfig *params.NodeConfig
|
||||||
txServiceMock *fake.MockPublicTransactionPoolAPI
|
|
||||||
nodeConfig *params.NodeConfig
|
|
||||||
|
|
||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TxQueueTestSuite) SetupTest() {
|
func (s *TxQueueTestSuite) SetupTest() {
|
||||||
s.nodeManagerMockCtrl = gomock.NewController(s.T())
|
s.nodeManagerMockCtrl = gomock.NewController(s.T())
|
||||||
s.accountManagerMockCtrl = gomock.NewController(s.T())
|
|
||||||
s.txServiceMockCtrl = gomock.NewController(s.T())
|
s.txServiceMockCtrl = gomock.NewController(s.T())
|
||||||
|
|
||||||
s.nodeManagerMock = common.NewMockNodeManager(s.nodeManagerMockCtrl)
|
s.nodeManagerMock = common.NewMockNodeManager(s.nodeManagerMockCtrl)
|
||||||
s.accountManagerMock = NewMockAccountManager(s.accountManagerMockCtrl)
|
|
||||||
|
|
||||||
s.server, s.txServiceMock = fake.NewTestServer(s.txServiceMockCtrl)
|
s.server, s.txServiceMock = fake.NewTestServer(s.txServiceMockCtrl)
|
||||||
s.client = gethrpc.DialInProc(s.server)
|
s.client = gethrpc.DialInProc(s.server)
|
||||||
|
@ -62,17 +58,16 @@ func (s *TxQueueTestSuite) SetupTest() {
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.nodeConfig = nodeConfig
|
s.nodeConfig = nodeConfig
|
||||||
|
|
||||||
s.manager = NewManager(s.nodeManagerMock, s.accountManagerMock)
|
s.manager = NewManager(s.nodeManagerMock)
|
||||||
s.manager.DisableNotificactions()
|
s.manager.DisableNotificactions()
|
||||||
s.manager.completionTimeout = time.Second
|
s.manager.completionTimeout = time.Second
|
||||||
s.manager.rpcCallTimeout = time.Second
|
s.manager.rpcCallTimeout = time.Second
|
||||||
s.manager.Start()
|
s.manager.Start(params.RopstenNetworkID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TxQueueTestSuite) TearDownTest() {
|
func (s *TxQueueTestSuite) TearDownTest() {
|
||||||
s.manager.Stop()
|
s.manager.Stop()
|
||||||
s.nodeManagerMockCtrl.Finish()
|
s.nodeManagerMockCtrl.Finish()
|
||||||
s.accountManagerMockCtrl.Finish()
|
|
||||||
s.txServiceMockCtrl.Finish()
|
s.txServiceMockCtrl.Finish()
|
||||||
s.server.Stop()
|
s.server.Stop()
|
||||||
s.client.Close()
|
s.client.Close()
|
||||||
|
@ -125,15 +120,7 @@ func (s *TxQueueTestSuite) rlpEncodeTx(tx *common.QueuedTx, config *params.NodeC
|
||||||
return hexutil.Bytes(data)
|
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() {
|
func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
||||||
password := TestConfig.Account1.Password
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
selectedAccount := &account.SelectedExtKey{
|
selectedAccount := &account.SelectedExtKey{
|
||||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||||
|
@ -169,7 +156,6 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
s.T().Run(testCase.name, func(t *testing.T) {
|
s.T().Run(testCase.name, func(t *testing.T) {
|
||||||
s.SetupTest()
|
s.SetupTest()
|
||||||
s.setupStatusBackend(selectedAccount, password, nil)
|
|
||||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||||
From: account.FromAddress(TestConfig.Account1.Address),
|
From: account.FromAddress(TestConfig.Account1.Address),
|
||||||
To: account.ToAddress(TestConfig.Account2.Address),
|
To: account.ToAddress(TestConfig.Account2.Address),
|
||||||
|
@ -185,7 +171,7 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
go func() {
|
go func() {
|
||||||
hash, err = s.manager.CompleteTransaction(tx.ID, password)
|
hash, err = s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
close(w)
|
close(w)
|
||||||
}()
|
}()
|
||||||
|
@ -202,13 +188,11 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
||||||
password := TestConfig.Account1.Password
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
selectedAccount := &account.SelectedExtKey{
|
selectedAccount := &account.SelectedExtKey{
|
||||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||||
AccountKey: &keystore.Key{PrivateKey: key},
|
AccountKey: &keystore.Key{PrivateKey: key},
|
||||||
}
|
}
|
||||||
s.setupStatusBackend(selectedAccount, password, nil)
|
|
||||||
|
|
||||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||||
From: account.FromAddress(TestConfig.Account1.Address),
|
From: account.FromAddress(TestConfig.Account1.Address),
|
||||||
|
@ -231,7 +215,7 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, err := s.manager.CompleteTransaction(tx.ID, password)
|
_, err := s.manager.CompleteTransaction(tx.ID, selectedAccount)
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -257,42 +241,19 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TxQueueTestSuite) TestAccountMismatch() {
|
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{
|
selectedAccount := &account.SelectedExtKey{
|
||||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
Address: account.FromAddress(TestConfig.Account2.Address),
|
||||||
AccountKey: &keystore.Key{PrivateKey: key},
|
|
||||||
}
|
}
|
||||||
s.setupStatusBackend(selectedAccount, password, keystore.ErrDecrypt)
|
|
||||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||||
From: account.FromAddress(TestConfig.Account1.Address),
|
From: account.FromAddress(TestConfig.Account1.Address),
|
||||||
To: account.ToAddress(TestConfig.Account2.Address),
|
To: account.ToAddress(TestConfig.Account2.Address),
|
||||||
})
|
})
|
||||||
|
|
||||||
s.NoError(s.manager.QueueTransaction(tx))
|
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
|
// Transaction should stay in the queue as mismatched accounts
|
||||||
// is a recoverable error.
|
// is a recoverable error.
|
||||||
|
@ -319,6 +280,54 @@ func (s *TxQueueTestSuite) TestDiscardTransaction() {
|
||||||
s.NoError(WaitClosed(w, time.Second))
|
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() {
|
func (s *TxQueueTestSuite) TestCompletionTimedOut() {
|
||||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
||||||
From: account.FromAddress(TestConfig.Account1.Address),
|
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
|
// as the last step, we verify that if tx failed nonce is not updated
|
||||||
func (s *TxQueueTestSuite) TestLocalNonce() {
|
func (s *TxQueueTestSuite) TestLocalNonce() {
|
||||||
txCount := 3
|
txCount := 3
|
||||||
password := TestConfig.Account1.Password
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
selectedAccount := &account.SelectedExtKey{
|
selectedAccount := &account.SelectedExtKey{
|
||||||
Address: account.FromAddress(TestConfig.Account1.Address),
|
Address: account.FromAddress(TestConfig.Account1.Address),
|
||||||
AccountKey: &keystore.Key{PrivateKey: key},
|
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)
|
nonce := hexutil.Uint64(0)
|
||||||
for i := 0; i < txCount; i++ {
|
for i := 0; i < txCount; i++ {
|
||||||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{
|
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.setupTransactionPoolAPI(tx, nonce, hexutil.Uint64(i), selectedAccount, nil)
|
||||||
s.NoError(s.manager.QueueTransaction(tx))
|
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)
|
rst := s.manager.WaitForTransaction(tx)
|
||||||
// simple sanity checks
|
// simple sanity checks
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
|
@ -373,7 +377,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
|
||||||
})
|
})
|
||||||
s.setupTransactionPoolAPI(tx, nonce, nonce, selectedAccount, nil)
|
s.setupTransactionPoolAPI(tx, nonce, nonce, selectedAccount, nil)
|
||||||
s.NoError(s.manager.QueueTransaction(tx))
|
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)
|
rst := s.manager.WaitForTransaction(tx)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
s.NoError(rst.Error)
|
s.NoError(rst.Error)
|
||||||
|
@ -388,7 +392,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
|
||||||
To: account.ToAddress(TestConfig.Account2.Address),
|
To: account.ToAddress(TestConfig.Account2.Address),
|
||||||
})
|
})
|
||||||
s.NoError(s.manager.QueueTransaction(tx))
|
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)
|
rst = s.manager.WaitForTransaction(tx)
|
||||||
s.EqualError(testErr, err.Error())
|
s.EqualError(testErr, err.Error())
|
||||||
s.EqualError(testErr, rst.Error.Error())
|
s.EqualError(testErr, rst.Error.Error())
|
||||||
|
|
|
@ -599,12 +599,12 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() {
|
||||||
txIDs = append(txIDs, "invalid-tx-id")
|
txIDs = append(txIDs, "invalid-tx-id")
|
||||||
|
|
||||||
// discard
|
// discard
|
||||||
discardResults := txQueueManager.DiscardTransactions(txIDs)
|
discardResults := s.Backend.DiscardTransactions(txIDs)
|
||||||
require.Len(discardResults, 1, "cannot discard txs: %v", discardResults)
|
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)
|
require.Error(discardResults["invalid-tx-id"].Error, "transaction hash not found", "cannot discard txs: %v", discardResults)
|
||||||
|
|
||||||
// try completing discarded transaction
|
// 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)")
|
require.Len(completeResults, testTxCount+1, "unexpected number of errors (call to CompleteTransaction should not succeed)")
|
||||||
|
|
||||||
for _, txResult := range completeResults {
|
for _, txResult := range completeResults {
|
||||||
|
|
Loading…
Reference in New Issue