From aa0f2ede6d196ffc369ce615716bc47d8aa5f8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Cidre?= Date: Wed, 4 Apr 2018 19:39:38 +0200 Subject: [PATCH] [#795] : Remove geth/common package (#796) --- cmd/statusd/debug/commands.go | 3 +- geth/account/accounts_test.go | 7 +- geth/api/api.go | 15 +- geth/api/backend.go | 27 ++-- geth/common/types.go | 170 -------------------- geth/rpc/call.go | 29 ---- geth/transactions/notifications.go | 29 ++-- geth/transactions/{queue => }/queue.go | 49 +++--- geth/transactions/queue/utils.go | 29 ---- geth/transactions/{queue => }/queue_test.go | 23 ++- geth/transactions/txqueue_manager.go | 82 +++++----- geth/transactions/txqueue_manager_test.go | 74 ++------- geth/transactions/types.go | 70 ++++++++ geth/{common => transactions}/types_test.go | 2 +- geth/{common => transactions}/utils.go | 55 ++++--- lib/library.go | 46 +++--- lib/library_test_utils.go | 52 +++--- lib/types.go | 23 +++ t/e2e/api/backend_test.go | 5 +- t/e2e/jail/jail_rpc_test.go | 5 +- t/e2e/suites.go | 5 +- t/e2e/transactions/transactions_test.go | 88 +++++----- t/utils/utils.go | 71 +++++++- 23 files changed, 419 insertions(+), 540 deletions(-) delete mode 100644 geth/common/types.go rename geth/transactions/{queue => }/queue.go (79%) delete mode 100644 geth/transactions/queue/utils.go rename geth/transactions/{queue => }/queue_test.go (81%) create mode 100644 geth/transactions/types.go rename geth/{common => transactions}/types_test.go (98%) rename geth/{common => transactions}/utils.go (50%) diff --git a/cmd/statusd/debug/commands.go b/cmd/statusd/debug/commands.go index b9affa10e..7bd694ea8 100644 --- a/cmd/statusd/debug/commands.go +++ b/cmd/statusd/debug/commands.go @@ -10,7 +10,6 @@ import ( "strings" "github.com/status-im/status-go/geth/api" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/params" ) @@ -170,7 +169,7 @@ func (cs *commandSet) Logout() error { // CompleteTransaction instructs API to complete sending of a given transaction. func (cs *commandSet) CompleteTransaction(id, password string) (string, error) { - txHash, err := cs.statusAPI.CompleteTransaction(common.QueuedTxID(id), password) + txHash, err := cs.statusAPI.CompleteTransaction(id, password) if err != nil { return "", err } diff --git a/geth/account/accounts_test.go b/geth/account/accounts_test.go index 5ee0f01dd..61c261f1c 100644 --- a/geth/account/accounts_test.go +++ b/geth/account/accounts_test.go @@ -13,7 +13,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/golang/mock/gomock" - "github.com/status-im/status-go/geth/common" . "github.com/status-im/status-go/t/utils" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -30,8 +29,8 @@ func TestVerifyAccountPassword(t *testing.T) { defer os.RemoveAll(emptyKeyStoreDir) //nolint: errcheck // import account keys - require.NoError(t, common.ImportTestAccount(keyStoreDir, GetAccount1PKFile())) - require.NoError(t, common.ImportTestAccount(keyStoreDir, GetAccount2PKFile())) + require.NoError(t, ImportTestAccount(keyStoreDir, GetAccount1PKFile())) + require.NoError(t, ImportTestAccount(keyStoreDir, GetAccount2PKFile())) account1Address := gethcommon.BytesToAddress(gethcommon.FromHex(TestConfig.Account1.Address)) @@ -103,7 +102,7 @@ func TestVerifyAccountPasswordWithAccountBeforeEIP55(t *testing.T) { defer os.RemoveAll(keyStoreDir) //nolint: errcheck // Import keys and make sure one was created before EIP55 introduction. - err = common.ImportTestAccount(keyStoreDir, "test-account3-before-eip55.pk") + err = ImportTestAccount(keyStoreDir, "test-account3-before-eip55.pk") require.NoError(t, err) accManager := NewManager(nil) diff --git a/geth/api/api.go b/geth/api/api.go index bedd3722c..3717393a9 100644 --- a/geth/api/api.go +++ b/geth/api/api.go @@ -8,7 +8,6 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/geth/account" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/jail" "github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/params" @@ -147,28 +146,28 @@ func (api *StatusAPI) Logout() error { } // SendTransaction creates a new transaction and waits until it's complete. -func (api *StatusAPI) SendTransaction(ctx context.Context, args common.SendTxArgs) (gethcommon.Hash, error) { +func (api *StatusAPI) SendTransaction(ctx context.Context, args transactions.SendTxArgs) (gethcommon.Hash, error) { return api.b.SendTransaction(ctx, args) } // 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 string, password string) (gethcommon.Hash, error) { 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 { +func (api *StatusAPI) CompleteTransactions(ids []string, password string) map[string]transactions.Result { return api.b.CompleteTransactions(ids, password) } // DiscardTransaction discards a given transaction from transaction queue -func (api *StatusAPI) DiscardTransaction(id common.QueuedTxID) error { - return api.b.txQueueManager.DiscardTransaction(id) +func (api *StatusAPI) DiscardTransaction(id string) error { + return api.b.DiscardTransaction(id) } // DiscardTransactions discards given multiple transactions from transaction queue -func (api *StatusAPI) DiscardTransactions(ids []common.QueuedTxID) map[common.QueuedTxID]common.RawDiscardTransactionResult { - return api.b.txQueueManager.DiscardTransactions(ids) +func (api *StatusAPI) DiscardTransactions(ids []string) map[string]error { + return api.b.DiscardTransactions(ids) } // JailParse creates a new jail cell context, with the given chatID as identifier. diff --git a/geth/api/backend.go b/geth/api/backend.go index f4878ec07..b3559ebaf 100644 --- a/geth/api/backend.go +++ b/geth/api/backend.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/geth/account" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/jail" "github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/notifications/push/fcm" @@ -193,11 +192,11 @@ func (b *StatusBackend) CallRPC(inputJSON string) string { } // SendTransaction creates a new transaction and waits until it's complete. -func (b *StatusBackend) SendTransaction(ctx context.Context, args common.SendTxArgs) (hash gethcommon.Hash, err error) { +func (b *StatusBackend) SendTransaction(ctx context.Context, args transactions.SendTxArgs) (hash gethcommon.Hash, err error) { if ctx == nil { ctx = context.Background() } - tx := common.CreateTransaction(ctx, args) + tx := transactions.Create(ctx, args) if err = b.txQueueManager.QueueTransaction(tx); err != nil { return hash, err } @@ -227,7 +226,7 @@ func (b *StatusBackend) getVerifiedAccount(password string) (*account.SelectedEx } // CompleteTransaction instructs backend to complete sending of a given transaction -func (b *StatusBackend) CompleteTransaction(id common.QueuedTxID, password string) (hash gethcommon.Hash, err error) { +func (b *StatusBackend) CompleteTransaction(id string, password string) (hash gethcommon.Hash, err error) { selectedAccount, err := b.getVerifiedAccount(password) if err != nil { _ = b.txQueueManager.NotifyErrored(id, err) @@ -238,11 +237,11 @@ func (b *StatusBackend) CompleteTransaction(id common.QueuedTxID, password strin } // CompleteTransactions instructs backend to complete sending of multiple transactions -func (b *StatusBackend) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult { - results := make(map[common.QueuedTxID]common.TransactionResult) +func (b *StatusBackend) CompleteTransactions(ids []string, password string) map[string]transactions.Result { + results := make(map[string]transactions.Result) for _, txID := range ids { txHash, txErr := b.CompleteTransaction(txID, password) - results[txID] = common.TransactionResult{ + results[txID] = transactions.Result{ Hash: txHash, Error: txErr, } @@ -251,13 +250,21 @@ func (b *StatusBackend) CompleteTransactions(ids []common.QueuedTxID, password s } // DiscardTransaction discards a given transaction from transaction queue -func (b *StatusBackend) DiscardTransaction(id common.QueuedTxID) error { +func (b *StatusBackend) DiscardTransaction(id string) error { return b.txQueueManager.DiscardTransaction(id) } // DiscardTransactions discards given multiple transactions from transaction queue -func (b *StatusBackend) DiscardTransactions(ids []common.QueuedTxID) map[common.QueuedTxID]common.RawDiscardTransactionResult { - return b.txQueueManager.DiscardTransactions(ids) +func (b *StatusBackend) DiscardTransactions(ids []string) map[string]error { + results := make(map[string]error) + for _, txID := range ids { + err := b.DiscardTransaction(txID) + if err != nil { + results[txID] = err + } + } + + return results } // registerHandlers attaches Status callback handlers to running node diff --git a/geth/common/types.go b/geth/common/types.go deleted file mode 100644 index 0fcc556ed..000000000 --- a/geth/common/types.go +++ /dev/null @@ -1,170 +0,0 @@ -package common - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "os" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/status-im/status-go/geth/params" - "github.com/status-im/status-go/static" -) - -// errors -var ( - ErrDeprecatedMethod = errors.New("Method is depricated and will be removed in future release") - ErrInvalidSendTxArgs = errors.New("Transaction arguments are invalid (are both 'input' and 'data' fields used?)") -) - -// TransactionResult is a JSON returned from transaction complete function (used internally) -type TransactionResult struct { - Hash common.Hash - Error error -} - -// RawDiscardTransactionResult is list of results from CompleteTransactions() (used internally) -type RawDiscardTransactionResult struct { - Error error -} - -// QueuedTxID queued transaction identifier -type QueuedTxID string - -// QueuedTx holds enough information to complete the queued transaction. -type QueuedTx struct { - ID QueuedTxID - Context context.Context - Args SendTxArgs - Result chan TransactionResult -} - -// SendTxArgs represents the arguments to submit a new transaction into the transaction pool. -// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom -// over the exact layout of this struct. -type SendTxArgs struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Value *hexutil.Big `json:"value"` - Nonce *hexutil.Uint64 `json:"nonce"` - // We keep both "input" and "data" for backward compatibility. - // "input" is a preferred field. - // see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107` - Input hexutil.Bytes `json:"input"` - Data hexutil.Bytes `json:"data"` -} - -// Valid checks whether this structure is filled in correctly. -func (args SendTxArgs) Valid() bool { - // if at least one of the fields is empty, it is a valid struct - if isNilOrEmpty(args.Input) || isNilOrEmpty(args.Data) { - return true - } - - // we only allow both fields to present if they have the same data - return bytes.Equal(args.Input, args.Data) -} - -// GetInput returns either Input or Data field's value dependent on what is filled. -func (args SendTxArgs) GetInput() hexutil.Bytes { - if !isNilOrEmpty(args.Input) { - return args.Input - } - - return args.Data -} - -func isNilOrEmpty(bytes hexutil.Bytes) bool { - return bytes == nil || len(bytes) == 0 -} - -// StopRPCCallError defines a error type specific for killing a execution process. -type StopRPCCallError struct { - Err error -} - -// Error returns the internal error associated with the critical error. -func (c StopRPCCallError) Error() string { - return c.Err.Error() -} - -// CompleteTransactionResult is a JSON returned from transaction complete function (used in exposed method) -type CompleteTransactionResult struct { - ID string `json:"id"` - Hash string `json:"hash"` - Error string `json:"error"` -} - -// CompleteTransactionsResult is list of results from CompleteTransactions() (used in exposed method) -type CompleteTransactionsResult struct { - Results map[string]CompleteTransactionResult `json:"results"` -} - -// DiscardTransactionResult is a JSON returned from transaction discard function -type DiscardTransactionResult struct { - ID string `json:"id"` - Error string `json:"error"` -} - -// DiscardTransactionsResult is a list of results from DiscardTransactions() -type DiscardTransactionsResult struct { - Results map[string]DiscardTransactionResult `json:"results"` -} - -type account struct { - Address string - Password string -} - -// TestConfig contains shared (among different test packages) parameters -type TestConfig struct { - Node struct { - SyncSeconds time.Duration - HTTPPort int - WSPort int - } - Account1 account - Account2 account - Account3 account -} - -// NotifyResult is a JSON returned from notify message -type NotifyResult struct { - Status bool `json:"status"` - Error string `json:"error,omitempty"` -} - -const passphraseEnvName = "ACCOUNT_PASSWORD" - -// LoadTestConfig loads test configuration values from disk -func LoadTestConfig(networkID int) (*TestConfig, error) { - var testConfig TestConfig - - configData := static.MustAsset("config/test-data.json") - if err := json.Unmarshal(configData, &testConfig); err != nil { - return nil, err - } - - if networkID == params.StatusChainNetworkID { - accountsData := static.MustAsset("config/status-chain-accounts.json") - if err := json.Unmarshal(accountsData, &testConfig); err != nil { - return nil, err - } - } else { - accountsData := static.MustAsset("config/public-chain-accounts.json") - if err := json.Unmarshal(accountsData, &testConfig); err != nil { - return nil, err - } - - pass := os.Getenv(passphraseEnvName) - testConfig.Account1.Password = pass - testConfig.Account2.Password = pass - } - - return &testConfig, nil -} diff --git a/geth/rpc/call.go b/geth/rpc/call.go index b82179f14..5aa61a9ff 100644 --- a/geth/rpc/call.go +++ b/geth/rpc/call.go @@ -5,7 +5,6 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/status-im/status-go/geth/common" ) // Call represents a unit of a rpc request which is to be executed. @@ -144,31 +143,3 @@ func (c Call) ParseGasPrice() *hexutil.Big { return (*hexutil.Big)(parsedValue) } - -// ToSendTxArgs converts Call to SendTxArgs. -func (c Call) ToSendTxArgs() common.SendTxArgs { - var err error - var fromAddr, toAddr gethcommon.Address - - fromAddr, err = c.ParseFromAddress() - if err != nil { - fromAddr = gethcommon.HexToAddress("0x0") - } - - toAddr, err = c.ParseToAddress() - if err != nil { - toAddr = gethcommon.HexToAddress("0x0") - } - - input := c.ParseInput() - data := c.ParseData() - return common.SendTxArgs{ - To: &toAddr, - From: fromAddr, - Value: c.ParseValue(), - Input: input, - Data: data, - Gas: c.ParseGas(), - GasPrice: c.ParseGasPrice(), - } -} diff --git a/geth/transactions/notifications.go b/geth/transactions/notifications.go index b82f7b69c..4434b3e0f 100644 --- a/geth/transactions/notifications.go +++ b/geth/transactions/notifications.go @@ -2,7 +2,6 @@ package transactions import ( "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/signal" ) @@ -35,34 +34,34 @@ var txReturnCodes = map[error]int{ // SendTransactionEvent is a signal sent on a send transaction request type SendTransactionEvent struct { - ID string `json:"id"` - Args common.SendTxArgs `json:"args"` - MessageID string `json:"message_id"` + ID string `json:"id"` + Args SendTxArgs `json:"args"` + MessageID string `json:"message_id"` } // NotifyOnEnqueue returns handler that processes incoming tx queue requests -func NotifyOnEnqueue(queuedTx *common.QueuedTx) { +func NotifyOnEnqueue(queuedTx *QueuedTx) { signal.Send(signal.Envelope{ Type: EventTransactionQueued, Event: SendTransactionEvent{ - ID: string(queuedTx.ID), + ID: queuedTx.ID, Args: queuedTx.Args, - MessageID: common.MessageIDFromContext(queuedTx.Context), + MessageID: messageIDFromContext(queuedTx.Context), }, }) } // ReturnSendTransactionEvent is a JSON returned whenever transaction send is returned type ReturnSendTransactionEvent struct { - ID string `json:"id"` - Args common.SendTxArgs `json:"args"` - MessageID string `json:"message_id"` - ErrorMessage string `json:"error_message"` - ErrorCode int `json:"error_code,string"` + ID string `json:"id"` + Args SendTxArgs `json:"args"` + MessageID string `json:"message_id"` + ErrorMessage string `json:"error_message"` + ErrorCode int `json:"error_code,string"` } // NotifyOnReturn returns handler that processes responses from internal tx manager -func NotifyOnReturn(queuedTx *common.QueuedTx, err error) { +func NotifyOnReturn(queuedTx *QueuedTx, err error) { // we don't want to notify a user if tx was sent successfully if err == nil { return @@ -74,9 +73,9 @@ func NotifyOnReturn(queuedTx *common.QueuedTx, err error) { signal.Send(signal.Envelope{ Type: EventTransactionFailed, Event: ReturnSendTransactionEvent{ - ID: string(queuedTx.ID), + ID: queuedTx.ID, Args: queuedTx.Args, - MessageID: common.MessageIDFromContext(queuedTx.Context), + MessageID: messageIDFromContext(queuedTx.Context), ErrorMessage: err.Error(), ErrorCode: sendTransactionErrorCode(err), }, diff --git a/geth/transactions/queue/queue.go b/geth/transactions/queue.go similarity index 79% rename from geth/transactions/queue/queue.go rename to geth/transactions/queue.go index f7763ef29..b8f24a410 100644 --- a/geth/transactions/queue/queue.go +++ b/geth/transactions/queue.go @@ -1,4 +1,4 @@ -package queue +package transactions import ( "errors" @@ -9,7 +9,6 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/geth/account" - "github.com/status-im/status-go/geth/common" ) const ( @@ -41,11 +40,11 @@ type empty struct{} // TxQueue is capped container that holds pending transactions type TxQueue struct { mu sync.RWMutex // to guard transactions map - transactions map[common.QueuedTxID]*common.QueuedTx - inprogress map[common.QueuedTxID]empty + transactions map[string]*QueuedTx + inprogress map[string]empty // TODO(dshulyak) research why eviction is done in separate goroutine - evictableIDs chan common.QueuedTxID + evictableIDs chan string enqueueTicker chan struct{} // when this channel is closed, all queue channels processing must cease (incoming queue, processing queued items etc) @@ -54,16 +53,16 @@ type TxQueue struct { log log.Logger } -// New creates a transaction queue. -func New() *TxQueue { +// newQueue creates a transaction queue. +func newQueue() *TxQueue { - logger := log.New("package", "status-go/geth/transactions/queue.TxQueue") + logger := log.New("package", "status-go/geth/transactions.TxQueue") logger.Info("initializing transaction queue") return &TxQueue{ - transactions: make(map[common.QueuedTxID]*common.QueuedTx), - inprogress: make(map[common.QueuedTxID]empty), - evictableIDs: make(chan common.QueuedTxID, DefaultTxQueueCap), // will be used to evict in FIFO + transactions: make(map[string]*QueuedTx), + inprogress: make(map[string]empty), + evictableIDs: make(chan string, DefaultTxQueueCap), // will be used to evict in FIFO enqueueTicker: make(chan struct{}), log: logger, } @@ -99,7 +98,7 @@ func (q *TxQueue) Stop() { // evictionLoop frees up queue to accommodate another transaction item func (q *TxQueue) evictionLoop() { - defer HaltOnPanic() + defer haltOnPanic() evict := func() { if q.Count() >= DefaultTxQueueCap { // eviction is required to accommodate another/last item q.Remove(<-q.evictableIDs) @@ -125,13 +124,13 @@ func (q *TxQueue) Reset() { q.mu.Lock() defer q.mu.Unlock() - q.transactions = make(map[common.QueuedTxID]*common.QueuedTx) - q.evictableIDs = make(chan common.QueuedTxID, DefaultTxQueueCap) - q.inprogress = make(map[common.QueuedTxID]empty) + q.transactions = make(map[string]*QueuedTx) + q.evictableIDs = make(chan string, DefaultTxQueueCap) + q.inprogress = make(map[string]empty) } // Enqueue enqueues incoming transaction -func (q *TxQueue) Enqueue(tx *common.QueuedTx) error { +func (q *TxQueue) Enqueue(tx *QueuedTx) error { q.log.Info("enqueue transaction", "ID", tx.ID) q.mu.RLock() if _, ok := q.transactions[tx.ID]; ok { @@ -156,7 +155,7 @@ func (q *TxQueue) Enqueue(tx *common.QueuedTx) error { } // Get returns transaction by transaction identifier -func (q *TxQueue) Get(id common.QueuedTxID) (*common.QueuedTx, error) { +func (q *TxQueue) Get(id string) (*QueuedTx, error) { q.mu.RLock() defer q.mu.RUnlock() @@ -167,7 +166,7 @@ func (q *TxQueue) Get(id common.QueuedTxID) (*common.QueuedTx, error) { } // LockInprogress returns error if transaction is already inprogress. -func (q *TxQueue) LockInprogress(id common.QueuedTxID) error { +func (q *TxQueue) LockInprogress(id string) error { q.mu.Lock() defer q.mu.Unlock() if _, ok := q.transactions[id]; ok { @@ -181,20 +180,20 @@ func (q *TxQueue) LockInprogress(id common.QueuedTxID) error { } // Remove removes transaction by transaction identifier -func (q *TxQueue) Remove(id common.QueuedTxID) { +func (q *TxQueue) Remove(id string) { q.mu.Lock() defer q.mu.Unlock() q.remove(id) } -func (q *TxQueue) remove(id common.QueuedTxID) { +func (q *TxQueue) remove(id string) { delete(q.transactions, id) delete(q.inprogress, id) } // Done removes transaction from queue if no error or error is not transient // and notify subscribers -func (q *TxQueue) Done(id common.QueuedTxID, hash gethcommon.Hash, err error) error { +func (q *TxQueue) Done(id string, hash gethcommon.Hash, err error) error { q.mu.Lock() defer q.mu.Unlock() tx, ok := q.transactions[id] @@ -205,16 +204,16 @@ func (q *TxQueue) Done(id common.QueuedTxID, hash gethcommon.Hash, err error) er return nil } -func (q *TxQueue) done(tx *common.QueuedTx, hash gethcommon.Hash, err error) { +func (q *TxQueue) done(tx *QueuedTx, hash gethcommon.Hash, err error) { delete(q.inprogress, tx.ID) // hash is updated only if err is nil, but transaction is not removed from a queue if err == nil { - q.transactions[tx.ID].Result <- common.TransactionResult{Hash: hash, Error: err} + q.transactions[tx.ID].Result <- Result{Hash: hash, Error: err} q.remove(tx.ID) return } if _, transient := transientErrs[err.Error()]; !transient { - q.transactions[tx.ID].Result <- common.TransactionResult{Error: err} + q.transactions[tx.ID].Result <- Result{Error: err} q.remove(tx.ID) } } @@ -227,7 +226,7 @@ func (q *TxQueue) Count() int { } // Has checks whether transaction with a given identifier exists in queue -func (q *TxQueue) Has(id common.QueuedTxID) bool { +func (q *TxQueue) Has(id string) bool { q.mu.RLock() defer q.mu.RUnlock() _, ok := q.transactions[id] diff --git a/geth/transactions/queue/utils.go b/geth/transactions/queue/utils.go deleted file mode 100644 index 2165e6dc1..000000000 --- a/geth/transactions/queue/utils.go +++ /dev/null @@ -1,29 +0,0 @@ -package queue - -import ( - "errors" - "fmt" - - "github.com/status-im/status-go/geth/common" - "github.com/status-im/status-go/geth/signal" -) - -//ErrTxQueueRunFailure - error running transaction queue -var ErrTxQueueRunFailure = errors.New("error running transaction queue") - -// HaltOnPanic recovers from panic, logs issue, sends upward notification, and exits -func HaltOnPanic() { - if r := recover(); r != nil { - err := fmt.Errorf("%v: %v", ErrTxQueueRunFailure, r) - - // send signal up to native app - signal.Send(signal.Envelope{ - Type: signal.EventNodeCrashed, - Event: signal.NodeCrashEvent{ - Error: err, - }, - }) - - common.Fatalf(err) // os.exit(1) is called internally - } -} diff --git a/geth/transactions/queue/queue_test.go b/geth/transactions/queue_test.go similarity index 81% rename from geth/transactions/queue/queue_test.go rename to geth/transactions/queue_test.go index 47a32ff5b..e2e847a6e 100644 --- a/geth/transactions/queue/queue_test.go +++ b/geth/transactions/queue_test.go @@ -1,4 +1,4 @@ -package queue +package transactions import ( "context" @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/status-im/status-go/geth/common" "github.com/stretchr/testify/suite" ) @@ -22,7 +21,7 @@ type QueueTestSuite struct { } func (s *QueueTestSuite) SetupTest() { - s.queue = New() + s.queue = newQueue() s.queue.Start() } @@ -31,7 +30,7 @@ func (s *QueueTestSuite) TearDownTest() { } func (s *QueueTestSuite) TestLockInprogressTransaction() { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) + tx := Create(context.Background(), SendTxArgs{}) s.NoError(s.queue.Enqueue(tx)) enquedTx, err := s.queue.Get(tx.ID) s.NoError(err) @@ -43,7 +42,7 @@ func (s *QueueTestSuite) TestLockInprogressTransaction() { } func (s *QueueTestSuite) TestGetTransaction() { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) + tx := Create(context.Background(), SendTxArgs{}) s.NoError(s.queue.Enqueue(tx)) for i := 2; i > 0; i-- { enquedTx, err := s.queue.Get(tx.ID) @@ -53,16 +52,16 @@ func (s *QueueTestSuite) TestGetTransaction() { } func (s *QueueTestSuite) TestAlreadyEnqueued() { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) + tx := Create(context.Background(), SendTxArgs{}) s.NoError(s.queue.Enqueue(tx)) s.Equal(ErrQueuedTxExist, s.queue.Enqueue(tx)) // try to enqueue another tx to double check locking - tx = common.CreateTransaction(context.Background(), common.SendTxArgs{}) + tx = Create(context.Background(), SendTxArgs{}) s.NoError(s.queue.Enqueue(tx)) } -func (s *QueueTestSuite) testDone(hash gethcommon.Hash, err error) *common.QueuedTx { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) +func (s *QueueTestSuite) testDone(hash gethcommon.Hash, err error) *QueuedTx { + tx := Create(context.Background(), SendTxArgs{}) s.NoError(s.queue.Enqueue(tx)) s.NoError(s.queue.Done(tx.ID, hash, err)) return tx @@ -116,16 +115,16 @@ func (s QueueTestSuite) TestMultipleDone() { } func (s *QueueTestSuite) TestEviction() { - var first *common.QueuedTx + var first *QueuedTx for i := 0; i < DefaultTxQueueCap; i++ { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) + tx := Create(context.Background(), SendTxArgs{}) if first == nil { first = tx } s.NoError(s.queue.Enqueue(tx)) } s.Equal(DefaultTxQueueCap, s.queue.Count()) - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) + tx := Create(context.Background(), SendTxArgs{}) s.NoError(s.queue.Enqueue(tx)) s.Equal(DefaultTxQueueCap, s.queue.Count()) s.False(s.queue.Has(first.ID)) diff --git a/geth/transactions/txqueue_manager.go b/geth/transactions/txqueue_manager.go index e755846f6..0920b48de 100644 --- a/geth/transactions/txqueue_manager.go +++ b/geth/transactions/txqueue_manager.go @@ -12,9 +12,8 @@ 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/rpc" - "github.com/status-im/status-go/geth/transactions/queue" ) const ( @@ -36,7 +35,7 @@ type RPCClientProvider interface { // Manager provides means to manage internal Status Backend (injected into LES) type Manager struct { rpcClientProvider RPCClientProvider - txQueue *queue.TxQueue + txQueue *TxQueue ethTxClient EthTransactor notify bool completionTimeout time.Duration @@ -52,7 +51,7 @@ type Manager struct { func NewManager(rpcClientProvider RPCClientProvider) *Manager { return &Manager{ rpcClientProvider: rpcClientProvider, - txQueue: queue.New(), + txQueue: newQueue(), addrLock: &AddrLocker{}, notify: true, completionTimeout: DefaultTxSendCompletionTimeout, @@ -83,14 +82,14 @@ func (m *Manager) Stop() { } // TransactionQueue returns a reference to the queue. -func (m *Manager) TransactionQueue() *queue.TxQueue { +func (m *Manager) TransactionQueue() *TxQueue { return m.txQueue } // QueueTransaction puts a transaction into the queue. -func (m *Manager) QueueTransaction(tx *common.QueuedTx) error { +func (m *Manager) QueueTransaction(tx *QueuedTx) error { if !tx.Args.Valid() { - return common.ErrInvalidSendTxArgs + return ErrInvalidSendTxArgs } to := "" if tx.Args.To != nil { @@ -106,8 +105,8 @@ func (m *Manager) QueueTransaction(tx *common.QueuedTx) error { return nil } -func (m *Manager) txDone(tx *common.QueuedTx, hash gethcommon.Hash, err error) { - if err := m.txQueue.Done(tx.ID, hash, err); err == queue.ErrQueuedTxIDNotFound { +func (m *Manager) txDone(tx *QueuedTx, hash gethcommon.Hash, err error) { + if err := m.txQueue.Done(tx.ID, hash, err); err == ErrQueuedTxIDNotFound { m.log.Warn("transaction is already removed from a queue", "ID", tx.ID) return } @@ -118,7 +117,7 @@ func (m *Manager) txDone(tx *common.QueuedTx, hash gethcommon.Hash, err error) { // WaitForTransaction adds a transaction to the queue and blocks // until it's completed, discarded or times out. -func (m *Manager) WaitForTransaction(tx *common.QueuedTx) common.TransactionResult { +func (m *Manager) WaitForTransaction(tx *QueuedTx) Result { m.log.Info("wait for transaction", "id", tx.ID) // now wait up until transaction is: // - completed (via CompleteQueuedTransaction), @@ -135,7 +134,7 @@ 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 { +func (m *Manager) NotifyErrored(id string, inputError error) error { tx, err := m.txQueue.Get(id) if err != nil { m.log.Warn("error getting a queued transaction", "err", err) @@ -150,7 +149,7 @@ func (m *Manager) NotifyErrored(id common.QueuedTxID, inputError error) error { } // CompleteTransaction instructs backend to complete sending of a given transaction. -func (m *Manager) CompleteTransaction(id common.QueuedTxID, account *account.SelectedExtKey) (hash gethcommon.Hash, err error) { +func (m *Manager) CompleteTransaction(id string, account *account.SelectedExtKey) (hash gethcommon.Hash, err error) { m.log.Info("complete transaction", "id", id) tx, err := m.txQueue.Get(id) if err != nil { @@ -173,21 +172,21 @@ func (m *Manager) CompleteTransaction(id common.QueuedTxID, account *account.Sel } // make sure that only account which created the tx can complete it -func (m *Manager) validateAccount(tx *common.QueuedTx, selectedAccount *account.SelectedExtKey) error { +func (m *Manager) validateAccount(tx *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 queue.ErrInvalidCompleteTxSender + m.log.Warn("queued transaction does not belong to the selected account", "err", ErrInvalidCompleteTxSender) + return ErrInvalidCompleteTxSender } return nil } -func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, queuedTx *common.QueuedTx) (hash gethcommon.Hash, err error) { +func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, queuedTx *QueuedTx) (hash gethcommon.Hash, err error) { m.log.Info("complete transaction", "id", queuedTx.ID) m.addrLock.LockAddr(queuedTx.Args.From) var localNonce uint64 @@ -217,7 +216,7 @@ func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, q } args := queuedTx.Args if !args.Valid() { - return hash, common.ErrInvalidSendTxArgs + return hash, ErrInvalidSendTxArgs } gasPrice := (*big.Int)(args.GasPrice) if args.GasPrice == nil { @@ -280,7 +279,7 @@ func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, q } // DiscardTransaction discards a given transaction from transaction queue -func (m *Manager) DiscardTransaction(id common.QueuedTxID) error { +func (m *Manager) DiscardTransaction(id string) error { tx, err := m.txQueue.Get(id) if err != nil { return err @@ -292,28 +291,11 @@ func (m *Manager) DiscardTransaction(id common.QueuedTxID) error { return err } -// DiscardTransactions discards given multiple transactions from transaction queue -func (m *Manager) DiscardTransactions(ids []common.QueuedTxID) map[common.QueuedTxID]common.RawDiscardTransactionResult { - results := make(map[common.QueuedTxID]common.RawDiscardTransactionResult) - - for _, txID := range ids { - err := m.DiscardTransaction(txID) - if err != nil { - results[txID] = common.RawDiscardTransactionResult{ - Error: err, - } - } - } - - return results -} - // SendTransactionRPCHandler is a handler for eth_sendTransaction method. // It accepts one param which is a slice with a map of transaction params. func (m *Manager) SendTransactionRPCHandler(ctx context.Context, args ...interface{}) (interface{}, error) { m.log.Info("SendTransactionRPCHandler called") - rpcCall := rpc.Call{Params: args} - tx := common.CreateTransaction(ctx, rpcCall.ToSendTxArgs()) + tx := Create(ctx, m.rpcCalltoSendTxArgs(args...)) if err := m.QueueTransaction(tx); err != nil { return nil, err } @@ -323,3 +305,31 @@ func (m *Manager) SendTransactionRPCHandler(ctx context.Context, args ...interfa } return rst.Hash.Hex(), nil } + +func (m *Manager) rpcCalltoSendTxArgs(args ...interface{}) SendTxArgs { + var err error + var fromAddr, toAddr gethcommon.Address + + rpcCall := rpc.Call{Params: args} + fromAddr, err = rpcCall.ParseFromAddress() + if err != nil { + fromAddr = gethcommon.HexToAddress("0x0") + } + + toAddr, err = rpcCall.ParseToAddress() + if err != nil { + toAddr = gethcommon.HexToAddress("0x0") + } + + input := rpcCall.ParseInput() + data := rpcCall.ParseData() + return SendTxArgs{ + To: &toAddr, + From: fromAddr, + Value: rpcCall.ParseValue(), + Input: input, + Data: data, + Gas: rpcCall.ParseGas(), + GasPrice: rpcCall.ParseGasPrice(), + } +} diff --git a/geth/transactions/txqueue_manager_test.go b/geth/transactions/txqueue_manager_test.go index 345aa1345..ad35a6ed1 100644 --- a/geth/transactions/txqueue_manager_test.go +++ b/geth/transactions/txqueue_manager_test.go @@ -19,11 +19,9 @@ import ( "github.com/stretchr/testify/suite" "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/fake" - "github.com/status-im/status-go/geth/transactions/queue" . "github.com/status-im/status-go/t/utils" ) @@ -75,7 +73,7 @@ var ( testNonce = hexutil.Uint64(10) ) -func (s *TxQueueTestSuite) setupTransactionPoolAPI(tx *common.QueuedTx, returnNonce, resultNonce hexutil.Uint64, account *account.SelectedExtKey, txErr error) { +func (s *TxQueueTestSuite) setupTransactionPoolAPI(tx *QueuedTx, returnNonce, resultNonce hexutil.Uint64, account *account.SelectedExtKey, txErr error) { // 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 @@ -99,7 +97,7 @@ func (s *TxQueueTestSuite) setupTransactionPoolAPI(tx *common.QueuedTx, returnNo s.txServiceMock.EXPECT().SendRawTransaction(gomock.Any(), data).Return(gethcommon.Hash{}, txErr) } -func (s *TxQueueTestSuite) rlpEncodeTx(tx *common.QueuedTx, config *params.NodeConfig, account *account.SelectedExtKey, nonce *hexutil.Uint64, gas hexutil.Uint64, gasPrice *big.Int) hexutil.Bytes { +func (s *TxQueueTestSuite) rlpEncodeTx(tx *QueuedTx, config *params.NodeConfig, account *account.SelectedExtKey, nonce *hexutil.Uint64, gas hexutil.Uint64, gasPrice *big.Int) hexutil.Bytes { newTx := types.NewTransaction( uint64(*nonce), gethcommon.Address(*tx.Args.To), @@ -152,7 +150,7 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() { for _, testCase := range testCases { s.T().Run(testCase.name, func(t *testing.T) { s.SetupTest() - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Gas: testCase.gas, @@ -190,7 +188,7 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() { AccountKey: &keystore.Key{PrivateKey: key}, } - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) @@ -216,7 +214,7 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() { defer mu.Unlock() if err == nil { completedTx++ - } else if err == queue.ErrQueuedTxInProgress { + } else if err == ErrQueuedTxInProgress { inprogressTx++ } else { s.Fail("tx failed with unexpected error: ", err.Error()) @@ -241,7 +239,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() { Address: account.FromAddress(TestConfig.Account2.Address), } - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) @@ -249,7 +247,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() { s.NoError(s.manager.QueueTransaction(tx)) _, err := s.manager.CompleteTransaction(tx.ID, selectedAccount) - s.Equal(err, queue.ErrInvalidCompleteTxSender) + s.Equal(err, ErrInvalidCompleteTxSender) // Transaction should stay in the queue as mismatched accounts // is a recoverable error. @@ -257,7 +255,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() { } func (s *TxQueueTestSuite) TestDiscardTransaction() { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) @@ -276,56 +274,8 @@ 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{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) @@ -351,7 +301,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() { } nonce := hexutil.Uint64(0) for i := 0; i < txCount; i++ { - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) @@ -367,7 +317,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() { s.Equal(uint64(i)+1, resultNonce.(uint64)) } nonce = hexutil.Uint64(5) - tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx := Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) @@ -383,7 +333,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() { testErr := errors.New("test") s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), selectedAccount.Address, gethrpc.PendingBlockNumber).Return(nil, testErr) - tx = common.CreateTransaction(context.Background(), common.SendTxArgs{ + tx = Create(context.Background(), SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), }) diff --git a/geth/transactions/types.go b/geth/transactions/types.go new file mode 100644 index 000000000..7cb9ef993 --- /dev/null +++ b/geth/transactions/types.go @@ -0,0 +1,70 @@ +package transactions + +import ( + "bytes" + "context" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// errors +var ( + ErrInvalidSendTxArgs = errors.New("Transaction arguments are invalid (are both 'input' and 'data' fields used?)") +) + +// Result is a JSON returned from transaction complete function (used internally) +type Result struct { + Hash common.Hash + Error error +} + +// QueuedTx holds enough information to complete the queued transaction. +type QueuedTx struct { + ID string + Context context.Context + Args SendTxArgs + Result chan Result +} + +// SendTxArgs represents the arguments to submit a new transaction into the transaction pool. +// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom +// over the exact layout of this struct. +type SendTxArgs struct { + From common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Nonce *hexutil.Uint64 `json:"nonce"` + // We keep both "input" and "data" for backward compatibility. + // "input" is a preferred field. + // see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107` + Input hexutil.Bytes `json:"input"` + Data hexutil.Bytes `json:"data"` +} + +// Valid checks whether this structure is filled in correctly. +func (args SendTxArgs) Valid() bool { + // if at least one of the fields is empty, it is a valid struct + if isNilOrEmpty(args.Input) || isNilOrEmpty(args.Data) { + return true + } + + // we only allow both fields to present if they have the same data + return bytes.Equal(args.Input, args.Data) +} + +// GetInput returns either Input or Data field's value dependent on what is filled. +func (args SendTxArgs) GetInput() hexutil.Bytes { + if !isNilOrEmpty(args.Input) { + return args.Input + } + + return args.Data +} + +func isNilOrEmpty(bytes hexutil.Bytes) bool { + return bytes == nil || len(bytes) == 0 +} diff --git a/geth/common/types_test.go b/geth/transactions/types_test.go similarity index 98% rename from geth/common/types_test.go rename to geth/transactions/types_test.go index c34512fcc..3476eede6 100644 --- a/geth/common/types_test.go +++ b/geth/transactions/types_test.go @@ -1,4 +1,4 @@ -package common +package transactions import ( "testing" diff --git a/geth/common/utils.go b/geth/transactions/utils.go similarity index 50% rename from geth/common/utils.go rename to geth/transactions/utils.go index 5bab57ef0..939bc2cbb 100644 --- a/geth/common/utils.go +++ b/geth/transactions/utils.go @@ -1,18 +1,16 @@ -package common +package transactions import ( "context" + "errors" "fmt" "io" - "io/ioutil" "os" - "path/filepath" "reflect" "runtime/debug" - "github.com/ethereum/go-ethereum/log" "github.com/pborman/uuid" - "github.com/status-im/status-go/static" + "github.com/status-im/status-go/geth/signal" ) const ( @@ -23,27 +21,28 @@ const ( type contextKey string // in order to make sure that our context key does not collide with keys from other packages -// All general log messages in this package should be routed through this logger. -var logger = log.New("package", "status-go/geth/common") +//ErrTxQueueRunFailure - error running transaction queue +var ErrTxQueueRunFailure = errors.New("error running transaction queue") -// ImportTestAccount imports keystore from static resources, see "static/keys" folder -func ImportTestAccount(keystoreDir, accountFile string) error { - // make sure that keystore folder exists - if _, err := os.Stat(keystoreDir); os.IsNotExist(err) { - os.MkdirAll(keystoreDir, os.ModePerm) // nolint: errcheck, gas +// haltOnPanic recovers from panic, logs issue, sends upward notification, and exits +func haltOnPanic() { + if r := recover(); r != nil { + err := fmt.Errorf("%v: %v", ErrTxQueueRunFailure, r) + + // send signal up to native app + signal.Send(signal.Envelope{ + Type: signal.EventNodeCrashed, + Event: signal.NodeCrashEvent{ + Error: err, + }, + }) + + fatalf(err) // os.exit(1) is called internally } - - dst := filepath.Join(keystoreDir, accountFile) - err := ioutil.WriteFile(dst, static.MustAsset("keys/"+accountFile), 0644) - if err != nil { - logger.Warn("cannot copy test account PK", "error", err) - } - - return err } -// MessageIDFromContext returns message id from context (if exists) -func MessageIDFromContext(ctx context.Context) string { +// messageIDFromContext returns message id from context (if exists) +func messageIDFromContext(ctx context.Context) string { if ctx == nil { return "" } @@ -54,10 +53,10 @@ func MessageIDFromContext(ctx context.Context) string { return "" } -// Fatalf is used to halt the execution. +// fatalf is used to halt the execution. // When called the function prints stack end exits. // Failure is logged into both StdErr and StdOut. -func Fatalf(reason interface{}, args ...interface{}) { +func fatalf(reason interface{}, args ...interface{}) { // decide on output stream w := io.MultiWriter(os.Stdout, os.Stderr) outf, _ := os.Stdout.Stat() // nolint: gas @@ -79,12 +78,12 @@ func Fatalf(reason interface{}, args ...interface{}) { os.Exit(1) } -// CreateTransaction returns a transaction object. -func CreateTransaction(ctx context.Context, args SendTxArgs) *QueuedTx { +// Create returns a transaction object. +func Create(ctx context.Context, args SendTxArgs) *QueuedTx { return &QueuedTx{ - ID: QueuedTxID(uuid.New()), + ID: uuid.New(), Context: ctx, Args: args, - Result: make(chan TransactionResult, 1), + Result: make(chan Result, 1), } } diff --git a/lib/library.go b/lib/library.go index 022697651..737305dbc 100644 --- a/lib/library.go +++ b/lib/library.go @@ -8,7 +8,6 @@ import ( "github.com/NaySoftware/go-fcm" "github.com/ethereum/go-ethereum/log" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/profiling" "gopkg.in/go-playground/validator.v9" @@ -197,7 +196,7 @@ func Logout() *C.char { //CompleteTransaction instructs backend to complete sending of a given transaction //export CompleteTransaction func CompleteTransaction(id, password *C.char) *C.char { - txHash, err := statusAPI.CompleteTransaction(common.QueuedTxID(C.GoString(id)), C.GoString(password)) + txHash, err := statusAPI.CompleteTransaction(C.GoString(id), C.GoString(password)) errString := "" if err != nil { @@ -205,7 +204,7 @@ func CompleteTransaction(id, password *C.char) *C.char { errString = err.Error() } - out := common.CompleteTransactionResult{ + out := CompleteTransactionResult{ ID: C.GoString(id), Hash: txHash.Hex(), Error: errString, @@ -222,30 +221,30 @@ func CompleteTransaction(id, password *C.char) *C.char { //CompleteTransactions instructs backend to complete sending of multiple transactions //export CompleteTransactions func CompleteTransactions(ids, password *C.char) *C.char { - out := common.CompleteTransactionsResult{} - out.Results = make(map[string]common.CompleteTransactionResult) + out := CompleteTransactionsResult{} + out.Results = make(map[string]CompleteTransactionResult) parsedIDs, err := ParseJSONArray(C.GoString(ids)) if err != nil { - out.Results["none"] = common.CompleteTransactionResult{ + out.Results["none"] = CompleteTransactionResult{ Error: err.Error(), } } else { - txIDs := make([]common.QueuedTxID, len(parsedIDs)) + txIDs := make([]string, len(parsedIDs)) for i, id := range parsedIDs { - txIDs[i] = common.QueuedTxID(id) + txIDs[i] = id } results := statusAPI.CompleteTransactions(txIDs, C.GoString(password)) for txID, result := range results { - txResult := common.CompleteTransactionResult{ - ID: string(txID), + txResult := CompleteTransactionResult{ + ID: txID, Hash: result.Hash.Hex(), } if result.Error != nil { txResult.Error = result.Error.Error() } - out.Results[string(txID)] = txResult + out.Results[txID] = txResult } } @@ -261,7 +260,7 @@ func CompleteTransactions(ids, password *C.char) *C.char { //DiscardTransaction discards a given transaction from transaction queue //export DiscardTransaction func DiscardTransaction(id *C.char) *C.char { - err := statusAPI.DiscardTransaction(common.QueuedTxID(C.GoString(id))) + err := statusAPI.DiscardTransaction(C.GoString(id)) errString := "" if err != nil { @@ -269,7 +268,7 @@ func DiscardTransaction(id *C.char) *C.char { errString = err.Error() } - out := common.DiscardTransactionResult{ + out := DiscardTransactionResult{ ID: C.GoString(id), Error: errString, } @@ -285,29 +284,26 @@ func DiscardTransaction(id *C.char) *C.char { //DiscardTransactions discards given multiple transactions from transaction queue //export DiscardTransactions func DiscardTransactions(ids *C.char) *C.char { - out := common.DiscardTransactionsResult{} - out.Results = make(map[string]common.DiscardTransactionResult) + out := DiscardTransactionsResult{} + out.Results = make(map[string]DiscardTransactionResult) parsedIDs, err := ParseJSONArray(C.GoString(ids)) if err != nil { - out.Results["none"] = common.DiscardTransactionResult{ + out.Results["none"] = DiscardTransactionResult{ Error: err.Error(), } } else { - txIDs := make([]common.QueuedTxID, len(parsedIDs)) + txIDs := make([]string, len(parsedIDs)) for i, id := range parsedIDs { - txIDs[i] = common.QueuedTxID(id) + txIDs[i] = id } results := statusAPI.DiscardTransactions(txIDs) - for txID, result := range results { - txResult := common.DiscardTransactionResult{ - ID: string(txID), + for txID, err := range results { + out.Results[txID] = DiscardTransactionResult{ + ID: txID, + Error: err.Error(), } - if result.Error != nil { - txResult.Error = result.Error.Error() - } - out.Results[string(txID)] = txResult } } diff --git a/lib/library_test_utils.go b/lib/library_test_utils.go index ca3416fff..0239b1d7d 100644 --- a/lib/library_test_utils.go +++ b/lib/library_test_utils.go @@ -31,11 +31,9 @@ import ( "github.com/stretchr/testify/require" "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/signal" "github.com/status-im/status-go/geth/transactions" - "github.com/status-im/status-go/geth/transactions/queue" "github.com/status-im/status-go/static" . "github.com/status-im/status-go/t/utils" //nolint: golint ) @@ -70,10 +68,10 @@ func testExportedAPI(t *testing.T, done chan struct{}) { // prepare accounts testKeyDir := filepath.Join(testChainDir, "keystore") - if err := common.ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil { + if err := ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil { panic(err) } - if err := common.ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil { + if err := ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil { panic(err) } @@ -175,10 +173,10 @@ func testVerifyAccountPassword(t *testing.T) bool { } defer os.RemoveAll(tmpDir) // nolint: errcheck - if err = common.ImportTestAccount(tmpDir, GetAccount1PKFile()); err != nil { + if err = ImportTestAccount(tmpDir, GetAccount1PKFile()); err != nil { t.Fatal(err) } - if err = common.ImportTestAccount(tmpDir, GetAccount2PKFile()); err != nil { + if err = ImportTestAccount(tmpDir, GetAccount2PKFile()); err != nil { t.Fatal(err) } @@ -798,7 +796,7 @@ func testCompleteTransaction(t *testing.T) bool { event := envelope.Event.(map[string]interface{}) t.Logf("transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string)) - completeTxResponse := common.CompleteTransactionResult{} + completeTxResponse := CompleteTransactionResult{} rawResponse := CompleteTransaction(C.CString(event["id"].(string)), C.CString(TestConfig.Account1.Password)) if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &completeTxResponse); err != nil { @@ -818,7 +816,7 @@ func testCompleteTransaction(t *testing.T) bool { }) // this call blocks, up until Complete Transaction is called - txCheckHash, err := statusAPI.SendTransaction(context.TODO(), common.SendTxArgs{ + txCheckHash, err := statusAPI.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -883,7 +881,7 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocyc // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called sendTx := func() { - txHashCheck, err := statusAPI.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := statusAPI.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -912,14 +910,14 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocyc // complete resultsString := CompleteTransactions(C.CString(string(updatedTxIDStrings)), C.CString(TestConfig.Account1.Password)) - resultsStruct := common.CompleteTransactionsResult{} + resultsStruct := CompleteTransactionsResult{} if err := json.Unmarshal([]byte(C.GoString(resultsString)), &resultsStruct); err != nil { t.Error(err) return } results := resultsStruct.Results - if len(results) != (testTxCount+1) || results["invalid-tx-id"].Error != queue.ErrQueuedTxIDNotFound.Error() { + if len(results) != (testTxCount+1) || results["invalid-tx-id"].Error != transactions.ErrQueuedTxIDNotFound.Error() { t.Errorf("cannot complete txs: %v", results) return } @@ -944,7 +942,7 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocyc time.Sleep(1 * time.Second) // make sure that tx complete signal propagates for _, txID := range parsedIDs { - if txQueue.Has(common.QueuedTxID(txID)) { + if txQueue.Has(string(txID)) { t.Errorf("txqueue should not have test tx at this point (it should be completed): %s", txID) return } @@ -1010,13 +1008,13 @@ func testDiscardTransaction(t *testing.T) bool { //nolint: gocyclo txID = event["id"].(string) t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txID) - if !txQueue.Has(common.QueuedTxID(txID)) { + if !txQueue.Has(string(txID)) { t.Errorf("txqueue should still have test tx: %s", txID) return } // discard - discardResponse := common.DiscardTransactionResult{} + discardResponse := DiscardTransactionResult{} rawResponse := DiscardTransaction(C.CString(txID)) if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &discardResponse); err != nil { @@ -1029,14 +1027,14 @@ func testDiscardTransaction(t *testing.T) bool { //nolint: gocyclo } // try completing discarded transaction - _, err := statusAPI.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password) - if err != queue.ErrQueuedTxIDNotFound { + _, err := statusAPI.CompleteTransaction(string(txID), TestConfig.Account1.Password) + if err != transactions.ErrQueuedTxIDNotFound { t.Error("expects tx not found, but call to CompleteTransaction succeeded") return } time.Sleep(1 * time.Second) // make sure that tx complete signal propagates - if txQueue.Has(common.QueuedTxID(txID)) { + if txQueue.Has(string(txID)) { t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txID) return } @@ -1066,7 +1064,7 @@ func testDiscardTransaction(t *testing.T) bool { //nolint: gocyclo }) // this call blocks, and should return when DiscardQueuedTransaction() is called - txHashCheck, err := statusAPI.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := statusAPI.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -1123,7 +1121,7 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl txID = event["id"].(string) t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txID) - if !txQueue.Has(common.QueuedTxID(txID)) { + if !txQueue.Has(string(txID)) { t.Errorf("txqueue should still have test tx: %s", txID) return } @@ -1157,7 +1155,7 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called sendTx := func() { - txHashCheck, err := statusAPI.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := statusAPI.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -1186,21 +1184,21 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl // discard discardResultsString := DiscardTransactions(C.CString(string(updatedTxIDStrings))) - discardResultsStruct := common.DiscardTransactionsResult{} + discardResultsStruct := DiscardTransactionsResult{} if err := json.Unmarshal([]byte(C.GoString(discardResultsString)), &discardResultsStruct); err != nil { t.Error(err) return } discardResults := discardResultsStruct.Results - if len(discardResults) != 1 || discardResults["invalid-tx-id"].Error != queue.ErrQueuedTxIDNotFound.Error() { + if len(discardResults) != 1 || discardResults["invalid-tx-id"].Error != transactions.ErrQueuedTxIDNotFound.Error() { t.Errorf("cannot discard txs: %v", discardResults) return } // try completing discarded transaction completeResultsString := CompleteTransactions(C.CString(string(updatedTxIDStrings)), C.CString(TestConfig.Account1.Password)) - completeResultsStruct := common.CompleteTransactionsResult{} + completeResultsStruct := CompleteTransactionsResult{} if err := json.Unmarshal([]byte(C.GoString(completeResultsString)), &completeResultsStruct); err != nil { t.Error(err) return @@ -1215,7 +1213,7 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl t.Errorf("tx id not set in result: expected id is %s", txID) return } - if txResult.Error != queue.ErrQueuedTxIDNotFound.Error() { + if txResult.Error != transactions.ErrQueuedTxIDNotFound.Error() { t.Errorf("invalid error for %s", txResult.Hash) return } @@ -1227,7 +1225,7 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl time.Sleep(1 * time.Second) // make sure that tx complete signal propagates for _, txID := range parsedIDs { - if txQueue.Has(common.QueuedTxID(txID)) { + if txQueue.Has(string(txID)) { t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txID) return } @@ -1412,10 +1410,10 @@ func startTestNode(t *testing.T) <-chan struct{} { // inject test accounts testKeyDir := filepath.Join(testDir, "keystore") - if err := common.ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil { + if err := ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil { panic(err) } - if err := common.ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil { + if err := ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil { panic(err) } diff --git a/lib/types.go b/lib/types.go index b8bd9fdf6..97d43b64a 100644 --- a/lib/types.go +++ b/lib/types.go @@ -75,3 +75,26 @@ type NotifyResult struct { Status bool `json:"status"` Error string `json:"error,omitempty"` } + +// CompleteTransactionResult is a JSON returned from transaction complete function (used in exposed method) +type CompleteTransactionResult struct { + ID string `json:"id"` + Hash string `json:"hash"` + Error string `json:"error"` +} + +// CompleteTransactionsResult is list of results from CompleteTransactions() (used in exposed method) +type CompleteTransactionsResult struct { + Results map[string]CompleteTransactionResult `json:"results"` +} + +// DiscardTransactionResult is a JSON returned from transaction discard function +type DiscardTransactionResult struct { + ID string `json:"id"` + Error string `json:"error"` +} + +// DiscardTransactionsResult is a list of results from DiscardTransactions() +type DiscardTransactionsResult struct { + Results map[string]DiscardTransactionResult `json:"results"` +} diff --git a/t/e2e/api/backend_test.go b/t/e2e/api/backend_test.go index 149d9ddf1..e16c2b569 100644 --- a/t/e2e/api/backend_test.go +++ b/t/e2e/api/backend_test.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/geth/account" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/jail" "github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/params" @@ -148,13 +147,13 @@ func (s *APIBackendTestSuite) TestRaceConditions() { }, func(config *params.NodeConfig) { log.Info("CompleteTransactions()") - ids := []common.QueuedTxID{"id1", "id2"} + ids := []string{"id1", "id2"} s.T().Logf("CompleteTransactions(), result: %v", s.Backend.CompleteTransactions(ids, "password")) progress <- struct{}{} }, func(config *params.NodeConfig) { log.Info("DiscardTransactions()") - ids := []common.QueuedTxID{"id1", "id2"} + ids := []string{"id1", "id2"} s.T().Logf("DiscardTransactions(), result: %v", s.Backend.DiscardTransactions(ids)) progress <- struct{}{} }, diff --git a/t/e2e/jail/jail_rpc_test.go b/t/e2e/jail/jail_rpc_test.go index 43e756227..2fb6c0ad0 100644 --- a/t/e2e/jail/jail_rpc_test.go +++ b/t/e2e/jail/jail_rpc_test.go @@ -10,7 +10,6 @@ import ( "time" gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/jail" "github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/geth/signal" @@ -135,7 +134,7 @@ func (s *JailRPCTestSuite) TestContractDeployment() { txID := event["id"].(string) var txErr error - txHash, txErr = s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password) + txHash, txErr = s.Backend.CompleteTransaction(txID, TestConfig.Account1.Password) if s.NoError(txErr, event["id"]) { s.T().Logf("contract transaction complete, URL: %s", "https://ropsten.etherscan.io/tx/"+txHash.Hex()) } @@ -291,7 +290,7 @@ func (s *JailRPCTestSuite) TestJailVMPersistence() { //var txHash common.Hash txID := event["id"].(string) - txHash, e := s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password) + txHash, e := s.Backend.CompleteTransaction(txID, TestConfig.Account1.Password) s.NoError(e, "cannot complete queued transaction[%v]: %v", event["id"], e) s.T().Logf("Transaction complete: https://ropsten.etherscan.io/tx/%s", txHash.Hex()) diff --git a/t/e2e/suites.go b/t/e2e/suites.go index 60fee254b..e0b261123 100644 --- a/t/e2e/suites.go +++ b/t/e2e/suites.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/go-ethereum/les" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" "github.com/status-im/status-go/geth/api" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/signal" @@ -142,10 +141,10 @@ func (s *BackendTestSuite) TxQueueManager() *transactions.Manager { func importTestAccounts(keyStoreDir string) (err error) { logger.Debug("Import accounts to", "dir", keyStoreDir) - err = common.ImportTestAccount(keyStoreDir, GetAccount1PKFile()) + err = ImportTestAccount(keyStoreDir, GetAccount1PKFile()) if err != nil { return } - return common.ImportTestAccount(keyStoreDir, GetAccount2PKFile()) + return ImportTestAccount(keyStoreDir, GetAccount2PKFile()) } diff --git a/t/e2e/transactions/transactions_test.go b/t/e2e/transactions/transactions_test.go index b35a35acd..b417bd0dd 100644 --- a/t/e2e/transactions/transactions_test.go +++ b/t/e2e/transactions/transactions_test.go @@ -15,17 +15,15 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" "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/signal" "github.com/status-im/status-go/geth/transactions" - "github.com/status-im/status-go/geth/transactions/queue" e2e "github.com/status-im/status-go/t/e2e" . "github.com/status-im/status-go/t/utils" "github.com/stretchr/testify/suite" ) -type initFunc func([]byte, *common.SendTxArgs) +type initFunc func([]byte, *transactions.SendTxArgs) func TestTransactionsTestSuite(t *testing.T) { suite.Run(t, new(TransactionsTestSuite)) @@ -55,7 +53,7 @@ func (s *TransactionsTestSuite) TestCallRPCSendTransaction() { if sg.Type == transactions.EventTransactionQueued { event := sg.Event.(map[string]interface{}) txID := event["id"].(string) - txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password) + txHash, err = s.Backend.CompleteTransaction(string(txID), TestConfig.Account1.Password) s.NoError(err, "cannot complete queued transaction %s", txID) close(transactionCompleted) @@ -109,11 +107,11 @@ func (s *TransactionsTestSuite) TestCallRPCSendTransactionUpstream() { txID := event["id"].(string) // Complete with a wrong passphrase. - txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), "some-invalid-passphrase") + txHash, err = s.Backend.CompleteTransaction(string(txID), "some-invalid-passphrase") s.EqualError(err, keystore.ErrDecrypt.Error(), "should return an error as the passphrase was invalid") // Complete with a correct passphrase. - txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account2.Password) + txHash, err = s.Backend.CompleteTransaction(string(txID), TestConfig.Account2.Password) s.NoError(err, "cannot complete queued transaction %s", txID) close(transactionCompleted) @@ -144,7 +142,7 @@ func (s *TransactionsTestSuite) TestCallRPCSendTransactionUpstream() { // TestSendContractCompat tries to send transaction using the legacy "Data" // field, which is supported for backward compatibility reasons. func (s *TransactionsTestSuite) TestSendContractTxCompat() { - initFunc := func(byteCode []byte, args *common.SendTxArgs) { + initFunc := func(byteCode []byte, args *transactions.SendTxArgs) { args.Data = (hexutil.Bytes)(byteCode) } s.testSendContractTx(initFunc, nil, "") @@ -155,7 +153,7 @@ func (s *TransactionsTestSuite) TestSendContractTxCompat() { // they have different values. func (s *TransactionsTestSuite) TestSendContractTxCollision() { // Scenario 1: Both fields are filled and have the same value, expect success - initFunc := func(byteCode []byte, args *common.SendTxArgs) { + initFunc := func(byteCode []byte, args *transactions.SendTxArgs) { args.Input = (hexutil.Bytes)(byteCode) args.Data = (hexutil.Bytes)(byteCode) } @@ -171,15 +169,15 @@ func (s *TransactionsTestSuite) TestSendContractTxCollision() { return inverse } - initFunc2 := func(byteCode []byte, args *common.SendTxArgs) { + initFunc2 := func(byteCode []byte, args *transactions.SendTxArgs) { args.Input = (hexutil.Bytes)(byteCode) args.Data = (hexutil.Bytes)(inverted(byteCode)) } - s.testSendContractTx(initFunc2, common.ErrInvalidSendTxArgs, "expected error when invalid tx args are sent") + s.testSendContractTx(initFunc2, transactions.ErrInvalidSendTxArgs, "expected error when invalid tx args are sent") } func (s *TransactionsTestSuite) TestSendContractTx() { - initFunc := func(byteCode []byte, args *common.SendTxArgs) { + initFunc := func(byteCode []byte, args *transactions.SendTxArgs) { args.Input = (hexutil.Bytes)(byteCode) } s.testSendContractTx(initFunc, nil, "") @@ -210,7 +208,7 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc // the first call will fail (we are not logged in, but trying to complete tx) log.Info("trying to complete with no user logged in") txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), + string(event["id"].(string)), TestConfig.Account1.Password, ) s.EqualError( @@ -224,12 +222,12 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc err = s.Backend.SelectAccount(sampleAddress, TestConfig.Account1.Password) s.NoError(err) txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), + string(event["id"].(string)), TestConfig.Account1.Password, ) s.EqualError( err, - queue.ErrInvalidCompleteTxSender.Error(), + transactions.ErrInvalidCompleteTxSender.Error(), fmt.Sprintf("expected error on queued transaction[%v] not thrown", event["id"]), ) @@ -237,7 +235,7 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc log.Info("trying to complete with correct user, this should succeed") s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)) txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), + string(event["id"].(string)), TestConfig.Account1.Password, ) s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"])) @@ -253,7 +251,7 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc s.NoError(err) gas := uint64(params.DefaultGas) - args := common.SendTxArgs{ + args := transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: nil, // marker, contract creation is expected //Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), gethcommon.Ether)), @@ -306,7 +304,7 @@ func (s *TransactionsTestSuite) TestSendEther() { // the first call will fail (we are not logged in, but trying to complete tx) log.Info("trying to complete with no user logged in") txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), + string(event["id"].(string)), TestConfig.Account1.Password, ) s.EqualError( @@ -320,10 +318,10 @@ func (s *TransactionsTestSuite) TestSendEther() { err = s.Backend.SelectAccount(sampleAddress, TestConfig.Account1.Password) s.NoError(err) txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), TestConfig.Account1.Password) + string(event["id"].(string)), TestConfig.Account1.Password) s.EqualError( err, - queue.ErrInvalidCompleteTxSender.Error(), + transactions.ErrInvalidCompleteTxSender.Error(), fmt.Sprintf("expected error on queued transaction[%v] not thrown", event["id"]), ) @@ -331,7 +329,7 @@ func (s *TransactionsTestSuite) TestSendEther() { log.Info("trying to complete with correct user, this should succeed") s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)) txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), + string(event["id"].(string)), TestConfig.Account1.Password, ) s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"])) @@ -342,7 +340,7 @@ func (s *TransactionsTestSuite) TestSendEther() { }) // this call blocks, up until Complete Transaction is called - txHashCheck, err := s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -387,7 +385,7 @@ func (s *TransactionsTestSuite) TestSendEtherTxUpstream() { log.Info("transaction queued (will be completed shortly)", "id", event["id"].(string)) txHash, err = s.Backend.CompleteTransaction( - common.QueuedTxID(event["id"].(string)), + string(event["id"].(string)), TestConfig.Account1.Password, ) s.NoError(err, "cannot complete queued transaction[%v]", event["id"]) @@ -399,7 +397,7 @@ func (s *TransactionsTestSuite) TestSendEtherTxUpstream() { // This call blocks, up until Complete Transaction is called. // Explicitly not setting Gas to get it estimated. - txHashCheck, err := s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), GasPrice: (*hexutil.Big)(big.NewInt(28000000000)), @@ -438,7 +436,7 @@ func (s *TransactionsTestSuite) TestDoubleCompleteQueuedTransactions() { if envelope.Type == transactions.EventTransactionQueued { event := envelope.Event.(map[string]interface{}) - txID := common.QueuedTxID(event["id"].(string)) + txID := string(event["id"].(string)) log.Info("transaction queued (will be failed and completed on the second call)", "id", txID) // try with wrong password @@ -472,7 +470,7 @@ func (s *TransactionsTestSuite) TestDoubleCompleteQueuedTransactions() { }) // this call blocks, and should return on *second* attempt to CompleteTransaction (w/ the correct password) - txHashCheck, err := s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -514,7 +512,7 @@ func (s *TransactionsTestSuite) TestDiscardQueuedTransaction() { if envelope.Type == transactions.EventTransactionQueued { event := envelope.Event.(map[string]interface{}) - txID := common.QueuedTxID(event["id"].(string)) + txID := string(event["id"].(string)) log.Info("transaction queued (will be discarded soon)", "id", txID) s.True(s.Backend.TxQueueManager().TransactionQueue().Has(txID), "txqueue should still have test tx") @@ -550,7 +548,7 @@ func (s *TransactionsTestSuite) TestDiscardQueuedTransaction() { }) // this call blocks, and should return when DiscardQueuedTransaction() is called - txHashCheck, err := s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -594,7 +592,7 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() { s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)) testTxCount := 3 - txIDs := make(chan common.QueuedTxID, testTxCount) + txIDs := make(chan string, testTxCount) allTestTxDiscarded := make(chan struct{}) // replace transaction notification handler @@ -605,7 +603,7 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() { s.NoError(err) if envelope.Type == transactions.EventTransactionQueued { event := envelope.Event.(map[string]interface{}) - txID := common.QueuedTxID(event["id"].(string)) + txID := string(event["id"].(string)) log.Info("transaction queued (will be discarded soon)", "id", txID) s.True(s.Backend.TxQueueManager().TransactionQueue().Has(txID), @@ -635,7 +633,7 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() { // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called sendTx := func() { - txHashCheck, err := s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -647,13 +645,13 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() { txQueueManager := s.Backend.TxQueueManager() // wait for transactions, and discard immediately - discardTxs := func(txIDs []common.QueuedTxID) { + discardTxs := func(txIDs []string) { txIDs = append(txIDs, "invalid-tx-id") // discard 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) + require.Error(discardResults["invalid-tx-id"], "transaction hash not found", "cannot discard txs: %v", discardResults) // try completing discarded transaction completeResults := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password) @@ -675,7 +673,7 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() { } } go func() { - ids := make([]common.QueuedTxID, testTxCount) + ids := make([]string, testTxCount) for i := 0; i < testTxCount; i++ { ids[i] = <-txIDs } @@ -710,7 +708,7 @@ func (s *TransactionsTestSuite) TestNonExistentQueuedTransactions() { // try completing non-existing transaction _, err := s.Backend.CompleteTransaction("some-bad-transaction-id", TestConfig.Account1.Password) s.Error(err, "error expected and not received") - s.EqualError(err, queue.ErrQueuedTxIDNotFound.Error()) + s.EqualError(err, transactions.ErrQueuedTxIDNotFound.Error()) } func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() { @@ -719,7 +717,7 @@ func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() { var m sync.Mutex txCount := 0 - txIDs := [queue.DefaultTxQueueCap + 5 + 10]common.QueuedTxID{} + txIDs := [transactions.DefaultTxQueueCap + 5 + 10]string{} signal.SetDefaultNodeNotificationHandler(func(rawSignal string) { var sg signal.Envelope @@ -730,7 +728,7 @@ func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() { event := sg.Event.(map[string]interface{}) txID := event["id"].(string) m.Lock() - txIDs[txCount] = common.QueuedTxID(txID) + txIDs[txCount] = string(txID) txCount++ m.Unlock() } @@ -746,17 +744,17 @@ func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() { s.Zero(txQueue.Count(), "transaction count should be zero") for j := 0; j < 10; j++ { - go s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{}) // nolint: errcheck + go s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{}) // nolint: errcheck } time.Sleep(2 * time.Second) s.Equal(10, txQueue.Count(), "transaction count should be 10") - for i := 0; i < queue.DefaultTxQueueCap+5; i++ { // stress test by hitting with lots of goroutines - go s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{}) // nolint: errcheck + for i := 0; i < transactions.DefaultTxQueueCap+5; i++ { // stress test by hitting with lots of goroutines + go s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{}) // nolint: errcheck } time.Sleep(5 * time.Second) - s.True(txQueue.Count() <= queue.DefaultTxQueueCap, "transaction count should be %d (or %d): got %d", queue.DefaultTxQueueCap, queue.DefaultTxQueueCap-1, txQueue.Count()) + s.True(txQueue.Count() <= transactions.DefaultTxQueueCap, "transaction count should be %d (or %d): got %d", transactions.DefaultTxQueueCap, transactions.DefaultTxQueueCap-1, txQueue.Count()) m.Lock() for _, txID := range txIDs { @@ -796,7 +794,7 @@ func (s *TransactionsTestSuite) setupUpstreamNode() { } func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) { - txIDs := make(chan common.QueuedTxID, testTxCount) + txIDs := make(chan string, testTxCount) allTestTxCompleted := make(chan struct{}) require := s.Require() @@ -809,7 +807,7 @@ func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) { if envelope.Type == transactions.EventTransactionQueued { event := envelope.Event.(map[string]interface{}) - txID := common.QueuedTxID(event["id"].(string)) + txID := string(event["id"].(string)) log.Info("transaction queued (will be completed in a single call, once aggregated)", "id", txID) txIDs <- txID @@ -818,7 +816,7 @@ func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) { // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called sendTx := func() { - txHashCheck, err := s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{ + txHashCheck, err := s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{ From: account.FromAddress(TestConfig.Account1.Address), To: account.ToAddress(TestConfig.Account2.Address), Value: (*hexutil.Big)(big.NewInt(1000000000000)), @@ -828,7 +826,7 @@ func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) { } // wait for transactions, and complete them in a single call - completeTxs := func(txIDs []common.QueuedTxID) { + completeTxs := func(txIDs []string) { txIDs = append(txIDs, "invalid-tx-id") results := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password) s.Len(results, testTxCount+1) @@ -856,7 +854,7 @@ func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) { } } go func() { - ids := make([]common.QueuedTxID, testTxCount) + ids := make([]string, testTxCount) for i := 0; i < testTxCount; i++ { ids[i] = <-txIDs } diff --git a/t/utils/utils.go b/t/utils/utils.go index b49c50181..38d45de9d 100644 --- a/t/utils/utils.go +++ b/t/utils/utils.go @@ -2,10 +2,12 @@ package utils import ( "bytes" + "encoding/json" "errors" "flag" "fmt" "io" + "io/ioutil" "os" "path/filepath" "runtime" @@ -16,8 +18,8 @@ import ( "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" - "github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/params" + "github.com/status-im/status-go/static" _ "github.com/stretchr/testify/suite" // required to register testify flags ) @@ -32,7 +34,7 @@ var ( ErrTimeout = errors.New("timeout") // TestConfig defines the default config usable at package-level. - TestConfig *common.TestConfig + TestConfig *testConfig // RootDir is the main application directory RootDir string @@ -75,7 +77,7 @@ func init() { // setup auxiliary directories TestDataDir = filepath.Join(RootDir, ".ethereumtest") - TestConfig, err = common.LoadTestConfig(GetNetworkID()) + TestConfig, err = loadTestConfig(GetNetworkID()) if err != nil { panic(err) } @@ -282,3 +284,66 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) { } return nodeConfig, nil } + +type account struct { + Address string + Password string +} + +// testConfig contains shared (among different test packages) parameters +type testConfig struct { + Node struct { + SyncSeconds time.Duration + HTTPPort int + WSPort int + } + Account1 account + Account2 account + Account3 account +} + +const passphraseEnvName = "ACCOUNT_PASSWORD" + +// loadTestConfig loads test configuration values from disk +func loadTestConfig(networkID int) (*testConfig, error) { + var config testConfig + + configData := static.MustAsset("config/test-data.json") + if err := json.Unmarshal(configData, &config); err != nil { + return nil, err + } + + if networkID == params.StatusChainNetworkID { + accountsData := static.MustAsset("config/status-chain-accounts.json") + if err := json.Unmarshal(accountsData, &config); err != nil { + return nil, err + } + } else { + accountsData := static.MustAsset("config/public-chain-accounts.json") + if err := json.Unmarshal(accountsData, &config); err != nil { + return nil, err + } + + pass := os.Getenv(passphraseEnvName) + config.Account1.Password = pass + config.Account2.Password = pass + } + + return &config, nil +} + +// ImportTestAccount imports keystore from static resources, see "static/keys" folder +func ImportTestAccount(keystoreDir, accountFile string) error { + // make sure that keystore folder exists + if _, err := os.Stat(keystoreDir); os.IsNotExist(err) { + os.MkdirAll(keystoreDir, os.ModePerm) // nolint: errcheck, gas + } + + dst := filepath.Join(keystoreDir, accountFile) + err := ioutil.WriteFile(dst, static.MustAsset("keys/"+accountFile), 0644) + if err != nil { + logger.Warn("cannot copy test account PK", "error", err) + } + + return err +}