[#795] : Remove geth/common package (#796)

This commit is contained in:
Adrià Cidre 2018-04-04 19:39:38 +02:00 committed by GitHub
parent 4d51f937c4
commit aa0f2ede6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 419 additions and 540 deletions

View File

@ -10,7 +10,6 @@ import (
"strings" "strings"
"github.com/status-im/status-go/geth/api" "github.com/status-im/status-go/geth/api"
"github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/geth/params" "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. // CompleteTransaction instructs API to complete sending of a given transaction.
func (cs *commandSet) CompleteTransaction(id, password string) (string, error) { 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 { if err != nil {
return "", err return "", err
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/status-im/status-go/geth/common"
. "github.com/status-im/status-go/t/utils" . "github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -30,8 +29,8 @@ func TestVerifyAccountPassword(t *testing.T) {
defer os.RemoveAll(emptyKeyStoreDir) //nolint: errcheck defer os.RemoveAll(emptyKeyStoreDir) //nolint: errcheck
// import account keys // import account keys
require.NoError(t, common.ImportTestAccount(keyStoreDir, GetAccount1PKFile())) require.NoError(t, ImportTestAccount(keyStoreDir, GetAccount1PKFile()))
require.NoError(t, common.ImportTestAccount(keyStoreDir, GetAccount2PKFile())) require.NoError(t, ImportTestAccount(keyStoreDir, GetAccount2PKFile()))
account1Address := gethcommon.BytesToAddress(gethcommon.FromHex(TestConfig.Account1.Address)) account1Address := gethcommon.BytesToAddress(gethcommon.FromHex(TestConfig.Account1.Address))
@ -103,7 +102,7 @@ func TestVerifyAccountPasswordWithAccountBeforeEIP55(t *testing.T) {
defer os.RemoveAll(keyStoreDir) //nolint: errcheck defer os.RemoveAll(keyStoreDir) //nolint: errcheck
// Import keys and make sure one was created before EIP55 introduction. // 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) require.NoError(t, err)
accManager := NewManager(nil) accManager := NewManager(nil)

View File

@ -8,7 +8,6 @@ import (
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"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/jail" "github.com/status-im/status-go/geth/jail"
"github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/node"
"github.com/status-im/status-go/geth/params" "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. // 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) return api.b.SendTransaction(ctx, args)
} }
// 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 string, password string) (gethcommon.Hash, error) {
return api.b.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 []string, password string) map[string]transactions.Result {
return api.b.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
func (api *StatusAPI) DiscardTransaction(id common.QueuedTxID) error { func (api *StatusAPI) DiscardTransaction(id string) error {
return api.b.txQueueManager.DiscardTransaction(id) return api.b.DiscardTransaction(id)
} }
// DiscardTransactions discards given multiple transactions from transaction queue // DiscardTransactions discards given multiple transactions from transaction queue
func (api *StatusAPI) DiscardTransactions(ids []common.QueuedTxID) map[common.QueuedTxID]common.RawDiscardTransactionResult { func (api *StatusAPI) DiscardTransactions(ids []string) map[string]error {
return api.b.txQueueManager.DiscardTransactions(ids) return api.b.DiscardTransactions(ids)
} }
// JailParse creates a new jail cell context, with the given chatID as identifier. // JailParse creates a new jail cell context, with the given chatID as identifier.

View File

@ -10,7 +10,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"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/jail" "github.com/status-im/status-go/geth/jail"
"github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/node"
"github.com/status-im/status-go/geth/notifications/push/fcm" "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. // 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 { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
tx := common.CreateTransaction(ctx, args) tx := transactions.Create(ctx, args)
if err = b.txQueueManager.QueueTransaction(tx); err != nil { if err = b.txQueueManager.QueueTransaction(tx); err != nil {
return hash, err 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 // 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) selectedAccount, err := b.getVerifiedAccount(password)
if err != nil { if err != nil {
_ = b.txQueueManager.NotifyErrored(id, err) _ = 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 // 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 []string, password string) map[string]transactions.Result {
results := make(map[common.QueuedTxID]common.TransactionResult) results := make(map[string]transactions.Result)
for _, txID := range ids { for _, txID := range ids {
txHash, txErr := b.CompleteTransaction(txID, password) txHash, txErr := b.CompleteTransaction(txID, password)
results[txID] = common.TransactionResult{ results[txID] = transactions.Result{
Hash: txHash, Hash: txHash,
Error: txErr, Error: txErr,
} }
@ -251,13 +250,21 @@ func (b *StatusBackend) CompleteTransactions(ids []common.QueuedTxID, password s
} }
// DiscardTransaction discards a given transaction from transaction queue // 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) return b.txQueueManager.DiscardTransaction(id)
} }
// DiscardTransactions discards given multiple transactions from transaction queue // DiscardTransactions discards given multiple transactions from transaction queue
func (b *StatusBackend) DiscardTransactions(ids []common.QueuedTxID) map[common.QueuedTxID]common.RawDiscardTransactionResult { func (b *StatusBackend) DiscardTransactions(ids []string) map[string]error {
return b.txQueueManager.DiscardTransactions(ids) 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 // registerHandlers attaches Status callback handlers to running node

View File

@ -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
}

View File

@ -5,7 +5,6 @@ import (
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "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. // 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) 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(),
}
}

View File

@ -2,7 +2,6 @@ package transactions
import ( import (
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/geth/signal" "github.com/status-im/status-go/geth/signal"
) )
@ -36,18 +35,18 @@ var txReturnCodes = map[error]int{
// SendTransactionEvent is a signal sent on a send transaction request // SendTransactionEvent is a signal sent on a send transaction request
type SendTransactionEvent struct { type SendTransactionEvent struct {
ID string `json:"id"` ID string `json:"id"`
Args common.SendTxArgs `json:"args"` Args SendTxArgs `json:"args"`
MessageID string `json:"message_id"` MessageID string `json:"message_id"`
} }
// NotifyOnEnqueue returns handler that processes incoming tx queue requests // NotifyOnEnqueue returns handler that processes incoming tx queue requests
func NotifyOnEnqueue(queuedTx *common.QueuedTx) { func NotifyOnEnqueue(queuedTx *QueuedTx) {
signal.Send(signal.Envelope{ signal.Send(signal.Envelope{
Type: EventTransactionQueued, Type: EventTransactionQueued,
Event: SendTransactionEvent{ Event: SendTransactionEvent{
ID: string(queuedTx.ID), ID: queuedTx.ID,
Args: queuedTx.Args, Args: queuedTx.Args,
MessageID: common.MessageIDFromContext(queuedTx.Context), MessageID: messageIDFromContext(queuedTx.Context),
}, },
}) })
} }
@ -55,14 +54,14 @@ func NotifyOnEnqueue(queuedTx *common.QueuedTx) {
// ReturnSendTransactionEvent is a JSON returned whenever transaction send is returned // ReturnSendTransactionEvent is a JSON returned whenever transaction send is returned
type ReturnSendTransactionEvent struct { type ReturnSendTransactionEvent struct {
ID string `json:"id"` ID string `json:"id"`
Args common.SendTxArgs `json:"args"` Args SendTxArgs `json:"args"`
MessageID string `json:"message_id"` MessageID string `json:"message_id"`
ErrorMessage string `json:"error_message"` ErrorMessage string `json:"error_message"`
ErrorCode int `json:"error_code,string"` ErrorCode int `json:"error_code,string"`
} }
// NotifyOnReturn returns handler that processes responses from internal tx manager // 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 // we don't want to notify a user if tx was sent successfully
if err == nil { if err == nil {
return return
@ -74,9 +73,9 @@ func NotifyOnReturn(queuedTx *common.QueuedTx, err error) {
signal.Send(signal.Envelope{ signal.Send(signal.Envelope{
Type: EventTransactionFailed, Type: EventTransactionFailed,
Event: ReturnSendTransactionEvent{ Event: ReturnSendTransactionEvent{
ID: string(queuedTx.ID), ID: queuedTx.ID,
Args: queuedTx.Args, Args: queuedTx.Args,
MessageID: common.MessageIDFromContext(queuedTx.Context), MessageID: messageIDFromContext(queuedTx.Context),
ErrorMessage: err.Error(), ErrorMessage: err.Error(),
ErrorCode: sendTransactionErrorCode(err), ErrorCode: sendTransactionErrorCode(err),
}, },

View File

@ -1,4 +1,4 @@
package queue package transactions
import ( import (
"errors" "errors"
@ -9,7 +9,6 @@ import (
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/geth/account" "github.com/status-im/status-go/geth/account"
"github.com/status-im/status-go/geth/common"
) )
const ( const (
@ -41,11 +40,11 @@ type empty struct{}
// TxQueue is capped container that holds pending transactions // TxQueue is capped container that holds pending transactions
type TxQueue struct { type TxQueue struct {
mu sync.RWMutex // to guard transactions map mu sync.RWMutex // to guard transactions map
transactions map[common.QueuedTxID]*common.QueuedTx transactions map[string]*QueuedTx
inprogress map[common.QueuedTxID]empty inprogress map[string]empty
// TODO(dshulyak) research why eviction is done in separate goroutine // TODO(dshulyak) research why eviction is done in separate goroutine
evictableIDs chan common.QueuedTxID evictableIDs chan string
enqueueTicker chan struct{} enqueueTicker chan struct{}
// when this channel is closed, all queue channels processing must cease (incoming queue, processing queued items etc) // 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 log log.Logger
} }
// New creates a transaction queue. // newQueue creates a transaction queue.
func New() *TxQueue { 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") logger.Info("initializing transaction queue")
return &TxQueue{ return &TxQueue{
transactions: make(map[common.QueuedTxID]*common.QueuedTx), transactions: make(map[string]*QueuedTx),
inprogress: make(map[common.QueuedTxID]empty), inprogress: make(map[string]empty),
evictableIDs: make(chan common.QueuedTxID, DefaultTxQueueCap), // will be used to evict in FIFO evictableIDs: make(chan string, DefaultTxQueueCap), // will be used to evict in FIFO
enqueueTicker: make(chan struct{}), enqueueTicker: make(chan struct{}),
log: logger, log: logger,
} }
@ -99,7 +98,7 @@ func (q *TxQueue) Stop() {
// evictionLoop frees up queue to accommodate another transaction item // evictionLoop frees up queue to accommodate another transaction item
func (q *TxQueue) evictionLoop() { func (q *TxQueue) evictionLoop() {
defer HaltOnPanic() defer haltOnPanic()
evict := func() { evict := func() {
if q.Count() >= DefaultTxQueueCap { // eviction is required to accommodate another/last item if q.Count() >= DefaultTxQueueCap { // eviction is required to accommodate another/last item
q.Remove(<-q.evictableIDs) q.Remove(<-q.evictableIDs)
@ -125,13 +124,13 @@ func (q *TxQueue) Reset() {
q.mu.Lock() q.mu.Lock()
defer q.mu.Unlock() defer q.mu.Unlock()
q.transactions = make(map[common.QueuedTxID]*common.QueuedTx) q.transactions = make(map[string]*QueuedTx)
q.evictableIDs = make(chan common.QueuedTxID, DefaultTxQueueCap) q.evictableIDs = make(chan string, DefaultTxQueueCap)
q.inprogress = make(map[common.QueuedTxID]empty) q.inprogress = make(map[string]empty)
} }
// Enqueue enqueues incoming transaction // 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.log.Info("enqueue transaction", "ID", tx.ID)
q.mu.RLock() q.mu.RLock()
if _, ok := q.transactions[tx.ID]; ok { 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 // 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() q.mu.RLock()
defer q.mu.RUnlock() 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. // 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() q.mu.Lock()
defer q.mu.Unlock() defer q.mu.Unlock()
if _, ok := q.transactions[id]; ok { if _, ok := q.transactions[id]; ok {
@ -181,20 +180,20 @@ func (q *TxQueue) LockInprogress(id common.QueuedTxID) error {
} }
// Remove removes transaction by transaction identifier // Remove removes transaction by transaction identifier
func (q *TxQueue) Remove(id common.QueuedTxID) { func (q *TxQueue) Remove(id string) {
q.mu.Lock() q.mu.Lock()
defer q.mu.Unlock() defer q.mu.Unlock()
q.remove(id) q.remove(id)
} }
func (q *TxQueue) remove(id common.QueuedTxID) { func (q *TxQueue) remove(id string) {
delete(q.transactions, id) delete(q.transactions, id)
delete(q.inprogress, id) delete(q.inprogress, id)
} }
// Done removes transaction from queue if no error or error is not transient // Done removes transaction from queue if no error or error is not transient
// and notify subscribers // 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() q.mu.Lock()
defer q.mu.Unlock() defer q.mu.Unlock()
tx, ok := q.transactions[id] tx, ok := q.transactions[id]
@ -205,16 +204,16 @@ func (q *TxQueue) Done(id common.QueuedTxID, hash gethcommon.Hash, err error) er
return nil 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) delete(q.inprogress, tx.ID)
// hash is updated only if err is nil, but transaction is not removed from a queue // hash is updated only if err is nil, but transaction is not removed from a queue
if err == nil { 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) q.remove(tx.ID)
return return
} }
if _, transient := transientErrs[err.Error()]; !transient { 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) q.remove(tx.ID)
} }
} }
@ -227,7 +226,7 @@ func (q *TxQueue) Count() int {
} }
// Has checks whether transaction with a given identifier exists in queue // 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() q.mu.RLock()
defer q.mu.RUnlock() defer q.mu.RUnlock()
_, ok := q.transactions[id] _, ok := q.transactions[id]

View File

@ -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
}
}

View File

@ -1,4 +1,4 @@
package queue package transactions
import ( import (
"context" "context"
@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/geth/common"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@ -22,7 +21,7 @@ type QueueTestSuite struct {
} }
func (s *QueueTestSuite) SetupTest() { func (s *QueueTestSuite) SetupTest() {
s.queue = New() s.queue = newQueue()
s.queue.Start() s.queue.Start()
} }
@ -31,7 +30,7 @@ func (s *QueueTestSuite) TearDownTest() {
} }
func (s *QueueTestSuite) TestLockInprogressTransaction() { func (s *QueueTestSuite) TestLockInprogressTransaction() {
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) tx := Create(context.Background(), SendTxArgs{})
s.NoError(s.queue.Enqueue(tx)) s.NoError(s.queue.Enqueue(tx))
enquedTx, err := s.queue.Get(tx.ID) enquedTx, err := s.queue.Get(tx.ID)
s.NoError(err) s.NoError(err)
@ -43,7 +42,7 @@ func (s *QueueTestSuite) TestLockInprogressTransaction() {
} }
func (s *QueueTestSuite) TestGetTransaction() { func (s *QueueTestSuite) TestGetTransaction() {
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) tx := Create(context.Background(), SendTxArgs{})
s.NoError(s.queue.Enqueue(tx)) s.NoError(s.queue.Enqueue(tx))
for i := 2; i > 0; i-- { for i := 2; i > 0; i-- {
enquedTx, err := s.queue.Get(tx.ID) enquedTx, err := s.queue.Get(tx.ID)
@ -53,16 +52,16 @@ func (s *QueueTestSuite) TestGetTransaction() {
} }
func (s *QueueTestSuite) TestAlreadyEnqueued() { func (s *QueueTestSuite) TestAlreadyEnqueued() {
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) tx := Create(context.Background(), SendTxArgs{})
s.NoError(s.queue.Enqueue(tx)) s.NoError(s.queue.Enqueue(tx))
s.Equal(ErrQueuedTxExist, s.queue.Enqueue(tx)) s.Equal(ErrQueuedTxExist, s.queue.Enqueue(tx))
// try to enqueue another tx to double check locking // 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)) s.NoError(s.queue.Enqueue(tx))
} }
func (s *QueueTestSuite) testDone(hash gethcommon.Hash, err error) *common.QueuedTx { func (s *QueueTestSuite) testDone(hash gethcommon.Hash, err error) *QueuedTx {
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) tx := Create(context.Background(), SendTxArgs{})
s.NoError(s.queue.Enqueue(tx)) s.NoError(s.queue.Enqueue(tx))
s.NoError(s.queue.Done(tx.ID, hash, err)) s.NoError(s.queue.Done(tx.ID, hash, err))
return tx return tx
@ -116,16 +115,16 @@ func (s QueueTestSuite) TestMultipleDone() {
} }
func (s *QueueTestSuite) TestEviction() { func (s *QueueTestSuite) TestEviction() {
var first *common.QueuedTx var first *QueuedTx
for i := 0; i < DefaultTxQueueCap; i++ { for i := 0; i < DefaultTxQueueCap; i++ {
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{}) tx := Create(context.Background(), SendTxArgs{})
if first == nil { if first == nil {
first = tx first = tx
} }
s.NoError(s.queue.Enqueue(tx)) s.NoError(s.queue.Enqueue(tx))
} }
s.Equal(DefaultTxQueueCap, s.queue.Count()) 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.NoError(s.queue.Enqueue(tx))
s.Equal(DefaultTxQueueCap, s.queue.Count()) s.Equal(DefaultTxQueueCap, s.queue.Count())
s.False(s.queue.Has(first.ID)) s.False(s.queue.Has(first.ID))

View File

@ -12,9 +12,8 @@ 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/rpc" "github.com/status-im/status-go/geth/rpc"
"github.com/status-im/status-go/geth/transactions/queue"
) )
const ( const (
@ -36,7 +35,7 @@ type RPCClientProvider interface {
// 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 {
rpcClientProvider RPCClientProvider rpcClientProvider RPCClientProvider
txQueue *queue.TxQueue txQueue *TxQueue
ethTxClient EthTransactor ethTxClient EthTransactor
notify bool notify bool
completionTimeout time.Duration completionTimeout time.Duration
@ -52,7 +51,7 @@ type Manager struct {
func NewManager(rpcClientProvider RPCClientProvider) *Manager { func NewManager(rpcClientProvider RPCClientProvider) *Manager {
return &Manager{ return &Manager{
rpcClientProvider: rpcClientProvider, rpcClientProvider: rpcClientProvider,
txQueue: queue.New(), txQueue: newQueue(),
addrLock: &AddrLocker{}, addrLock: &AddrLocker{},
notify: true, notify: true,
completionTimeout: DefaultTxSendCompletionTimeout, completionTimeout: DefaultTxSendCompletionTimeout,
@ -83,14 +82,14 @@ func (m *Manager) Stop() {
} }
// TransactionQueue returns a reference to the queue. // TransactionQueue returns a reference to the queue.
func (m *Manager) TransactionQueue() *queue.TxQueue { func (m *Manager) TransactionQueue() *TxQueue {
return m.txQueue return m.txQueue
} }
// QueueTransaction puts a transaction into the queue. // 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() { if !tx.Args.Valid() {
return common.ErrInvalidSendTxArgs return ErrInvalidSendTxArgs
} }
to := "<nil>" to := "<nil>"
if tx.Args.To != nil { if tx.Args.To != nil {
@ -106,8 +105,8 @@ func (m *Manager) QueueTransaction(tx *common.QueuedTx) error {
return nil return nil
} }
func (m *Manager) txDone(tx *common.QueuedTx, hash gethcommon.Hash, err error) { func (m *Manager) txDone(tx *QueuedTx, hash gethcommon.Hash, err error) {
if err := m.txQueue.Done(tx.ID, hash, err); err == queue.ErrQueuedTxIDNotFound { if err := m.txQueue.Done(tx.ID, hash, err); err == ErrQueuedTxIDNotFound {
m.log.Warn("transaction is already removed from a queue", "ID", tx.ID) m.log.Warn("transaction is already removed from a queue", "ID", tx.ID)
return 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 // WaitForTransaction adds a transaction to the queue and blocks
// until it's completed, discarded or times out. // 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) m.log.Info("wait for transaction", "id", tx.ID)
// now wait up until transaction is: // now wait up until transaction is:
// - completed (via CompleteQueuedTransaction), // - completed (via CompleteQueuedTransaction),
@ -135,7 +134,7 @@ func (m *Manager) WaitForTransaction(tx *common.QueuedTx) common.TransactionResu
} }
// NotifyErrored sends a notification for the given transaction // 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) tx, err := m.txQueue.Get(id)
if err != nil { if err != nil {
m.log.Warn("error getting a queued transaction", "err", err) 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. // 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) 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 {
@ -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 // 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 { if selectedAccount == nil {
return account.ErrNoAccountSelected return account.ErrNoAccountSelected
} }
// 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", ErrInvalidCompleteTxSender)
return queue.ErrInvalidCompleteTxSender return ErrInvalidCompleteTxSender
} }
return nil 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.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
@ -217,7 +216,7 @@ func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, q
} }
args := queuedTx.Args args := queuedTx.Args
if !args.Valid() { if !args.Valid() {
return hash, common.ErrInvalidSendTxArgs return hash, ErrInvalidSendTxArgs
} }
gasPrice := (*big.Int)(args.GasPrice) gasPrice := (*big.Int)(args.GasPrice)
if args.GasPrice == nil { if args.GasPrice == nil {
@ -280,7 +279,7 @@ func (m *Manager) completeTransaction(selectedAccount *account.SelectedExtKey, q
} }
// 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 string) error {
tx, err := m.txQueue.Get(id) tx, err := m.txQueue.Get(id)
if err != nil { if err != nil {
return err return err
@ -292,28 +291,11 @@ func (m *Manager) DiscardTransaction(id common.QueuedTxID) error {
return err 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. // SendTransactionRPCHandler is a handler for eth_sendTransaction method.
// It accepts one param which is a slice with a map of transaction params. // 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) { func (m *Manager) SendTransactionRPCHandler(ctx context.Context, args ...interface{}) (interface{}, error) {
m.log.Info("SendTransactionRPCHandler called") m.log.Info("SendTransactionRPCHandler called")
rpcCall := rpc.Call{Params: args} tx := Create(ctx, m.rpcCalltoSendTxArgs(args...))
tx := common.CreateTransaction(ctx, rpcCall.ToSendTxArgs())
if err := m.QueueTransaction(tx); err != nil { if err := m.QueueTransaction(tx); err != nil {
return nil, err return nil, err
} }
@ -323,3 +305,31 @@ func (m *Manager) SendTransactionRPCHandler(ctx context.Context, args ...interfa
} }
return rst.Hash.Hex(), nil 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(),
}
}

View File

@ -19,11 +19,9 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"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/params" "github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/rpc" "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/fake"
"github.com/status-im/status-go/geth/transactions/queue"
. "github.com/status-im/status-go/t/utils" . "github.com/status-im/status-go/t/utils"
) )
@ -75,7 +73,7 @@ var (
testNonce = hexutil.Uint64(10) 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. // 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. // And also set the expected gas and gas price for RLP encoding the expected tx.
var usedGas hexutil.Uint64 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) 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( newTx := types.NewTransaction(
uint64(*nonce), uint64(*nonce),
gethcommon.Address(*tx.Args.To), gethcommon.Address(*tx.Args.To),
@ -152,7 +150,7 @@ 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()
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ tx := Create(context.Background(), 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),
Gas: testCase.gas, Gas: testCase.gas,
@ -190,7 +188,7 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
AccountKey: &keystore.Key{PrivateKey: key}, AccountKey: &keystore.Key{PrivateKey: key},
} }
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ tx := Create(context.Background(), 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),
}) })
@ -216,7 +214,7 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() {
defer mu.Unlock() defer mu.Unlock()
if err == nil { if err == nil {
completedTx++ completedTx++
} else if err == queue.ErrQueuedTxInProgress { } else if err == ErrQueuedTxInProgress {
inprogressTx++ inprogressTx++
} else { } else {
s.Fail("tx failed with unexpected error: ", err.Error()) s.Fail("tx failed with unexpected error: ", err.Error())
@ -241,7 +239,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() {
Address: account.FromAddress(TestConfig.Account2.Address), Address: account.FromAddress(TestConfig.Account2.Address),
} }
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ tx := Create(context.Background(), 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),
}) })
@ -249,7 +247,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() {
s.NoError(s.manager.QueueTransaction(tx)) s.NoError(s.manager.QueueTransaction(tx))
_, err := s.manager.CompleteTransaction(tx.ID, selectedAccount) _, 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 // Transaction should stay in the queue as mismatched accounts
// is a recoverable error. // is a recoverable error.
@ -257,7 +255,7 @@ func (s *TxQueueTestSuite) TestAccountMismatch() {
} }
func (s *TxQueueTestSuite) TestDiscardTransaction() { func (s *TxQueueTestSuite) TestDiscardTransaction() {
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ tx := Create(context.Background(), 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),
}) })
@ -276,56 +274,8 @@ 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 := Create(context.Background(), 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),
}) })
@ -351,7 +301,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
} }
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 := Create(context.Background(), 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),
}) })
@ -367,7 +317,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
s.Equal(uint64(i)+1, resultNonce.(uint64)) s.Equal(uint64(i)+1, resultNonce.(uint64))
} }
nonce = hexutil.Uint64(5) nonce = hexutil.Uint64(5)
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ tx := Create(context.Background(), 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),
}) })
@ -383,7 +333,7 @@ func (s *TxQueueTestSuite) TestLocalNonce() {
testErr := errors.New("test") testErr := errors.New("test")
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), selectedAccount.Address, gethrpc.PendingBlockNumber).Return(nil, testErr) 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
}) })

View File

@ -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
}

View File

@ -1,4 +1,4 @@
package common package transactions
import ( import (
"testing" "testing"

View File

@ -1,18 +1,16 @@
package common package transactions
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath"
"reflect" "reflect"
"runtime/debug" "runtime/debug"
"github.com/ethereum/go-ethereum/log"
"github.com/pborman/uuid" "github.com/pborman/uuid"
"github.com/status-im/status-go/static" "github.com/status-im/status-go/geth/signal"
) )
const ( 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 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. //ErrTxQueueRunFailure - error running transaction queue
var logger = log.New("package", "status-go/geth/common") var ErrTxQueueRunFailure = errors.New("error running transaction queue")
// ImportTestAccount imports keystore from static resources, see "static/keys" folder // haltOnPanic recovers from panic, logs issue, sends upward notification, and exits
func ImportTestAccount(keystoreDir, accountFile string) error { func haltOnPanic() {
// make sure that keystore folder exists if r := recover(); r != nil {
if _, err := os.Stat(keystoreDir); os.IsNotExist(err) { err := fmt.Errorf("%v: %v", ErrTxQueueRunFailure, r)
os.MkdirAll(keystoreDir, os.ModePerm) // nolint: errcheck, gas
// 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) // messageIDFromContext returns message id from context (if exists)
func MessageIDFromContext(ctx context.Context) string { func messageIDFromContext(ctx context.Context) string {
if ctx == nil { if ctx == nil {
return "" return ""
} }
@ -54,10 +53,10 @@ func MessageIDFromContext(ctx context.Context) string {
return "" return ""
} }
// Fatalf is used to halt the execution. // fatalf is used to halt the execution.
// When called the function prints stack end exits. // When called the function prints stack end exits.
// Failure is logged into both StdErr and StdOut. // Failure is logged into both StdErr and StdOut.
func Fatalf(reason interface{}, args ...interface{}) { func fatalf(reason interface{}, args ...interface{}) {
// decide on output stream // decide on output stream
w := io.MultiWriter(os.Stdout, os.Stderr) w := io.MultiWriter(os.Stdout, os.Stderr)
outf, _ := os.Stdout.Stat() // nolint: gas outf, _ := os.Stdout.Stat() // nolint: gas
@ -79,12 +78,12 @@ func Fatalf(reason interface{}, args ...interface{}) {
os.Exit(1) os.Exit(1)
} }
// CreateTransaction returns a transaction object. // Create returns a transaction object.
func CreateTransaction(ctx context.Context, args SendTxArgs) *QueuedTx { func Create(ctx context.Context, args SendTxArgs) *QueuedTx {
return &QueuedTx{ return &QueuedTx{
ID: QueuedTxID(uuid.New()), ID: uuid.New(),
Context: ctx, Context: ctx,
Args: args, Args: args,
Result: make(chan TransactionResult, 1), Result: make(chan Result, 1),
} }
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/NaySoftware/go-fcm" "github.com/NaySoftware/go-fcm"
"github.com/ethereum/go-ethereum/log" "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/geth/params"
"github.com/status-im/status-go/profiling" "github.com/status-im/status-go/profiling"
"gopkg.in/go-playground/validator.v9" "gopkg.in/go-playground/validator.v9"
@ -197,7 +196,7 @@ func Logout() *C.char {
//CompleteTransaction instructs backend to complete sending of a given transaction //CompleteTransaction instructs backend to complete sending of a given transaction
//export CompleteTransaction //export CompleteTransaction
func CompleteTransaction(id, password *C.char) *C.char { 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 := "" errString := ""
if err != nil { if err != nil {
@ -205,7 +204,7 @@ func CompleteTransaction(id, password *C.char) *C.char {
errString = err.Error() errString = err.Error()
} }
out := common.CompleteTransactionResult{ out := CompleteTransactionResult{
ID: C.GoString(id), ID: C.GoString(id),
Hash: txHash.Hex(), Hash: txHash.Hex(),
Error: errString, Error: errString,
@ -222,30 +221,30 @@ func CompleteTransaction(id, password *C.char) *C.char {
//CompleteTransactions instructs backend to complete sending of multiple transactions //CompleteTransactions instructs backend to complete sending of multiple transactions
//export CompleteTransactions //export CompleteTransactions
func CompleteTransactions(ids, password *C.char) *C.char { func CompleteTransactions(ids, password *C.char) *C.char {
out := common.CompleteTransactionsResult{} out := CompleteTransactionsResult{}
out.Results = make(map[string]common.CompleteTransactionResult) out.Results = make(map[string]CompleteTransactionResult)
parsedIDs, err := ParseJSONArray(C.GoString(ids)) parsedIDs, err := ParseJSONArray(C.GoString(ids))
if err != nil { if err != nil {
out.Results["none"] = common.CompleteTransactionResult{ out.Results["none"] = CompleteTransactionResult{
Error: err.Error(), Error: err.Error(),
} }
} else { } else {
txIDs := make([]common.QueuedTxID, len(parsedIDs)) txIDs := make([]string, len(parsedIDs))
for i, id := range parsedIDs { for i, id := range parsedIDs {
txIDs[i] = common.QueuedTxID(id) txIDs[i] = id
} }
results := statusAPI.CompleteTransactions(txIDs, C.GoString(password)) results := statusAPI.CompleteTransactions(txIDs, C.GoString(password))
for txID, result := range results { for txID, result := range results {
txResult := common.CompleteTransactionResult{ txResult := CompleteTransactionResult{
ID: string(txID), ID: txID,
Hash: result.Hash.Hex(), Hash: result.Hash.Hex(),
} }
if result.Error != nil { if result.Error != nil {
txResult.Error = result.Error.Error() 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 //DiscardTransaction discards a given transaction from transaction queue
//export DiscardTransaction //export DiscardTransaction
func DiscardTransaction(id *C.char) *C.char { func DiscardTransaction(id *C.char) *C.char {
err := statusAPI.DiscardTransaction(common.QueuedTxID(C.GoString(id))) err := statusAPI.DiscardTransaction(C.GoString(id))
errString := "" errString := ""
if err != nil { if err != nil {
@ -269,7 +268,7 @@ func DiscardTransaction(id *C.char) *C.char {
errString = err.Error() errString = err.Error()
} }
out := common.DiscardTransactionResult{ out := DiscardTransactionResult{
ID: C.GoString(id), ID: C.GoString(id),
Error: errString, Error: errString,
} }
@ -285,29 +284,26 @@ func DiscardTransaction(id *C.char) *C.char {
//DiscardTransactions discards given multiple transactions from transaction queue //DiscardTransactions discards given multiple transactions from transaction queue
//export DiscardTransactions //export DiscardTransactions
func DiscardTransactions(ids *C.char) *C.char { func DiscardTransactions(ids *C.char) *C.char {
out := common.DiscardTransactionsResult{} out := DiscardTransactionsResult{}
out.Results = make(map[string]common.DiscardTransactionResult) out.Results = make(map[string]DiscardTransactionResult)
parsedIDs, err := ParseJSONArray(C.GoString(ids)) parsedIDs, err := ParseJSONArray(C.GoString(ids))
if err != nil { if err != nil {
out.Results["none"] = common.DiscardTransactionResult{ out.Results["none"] = DiscardTransactionResult{
Error: err.Error(), Error: err.Error(),
} }
} else { } else {
txIDs := make([]common.QueuedTxID, len(parsedIDs)) txIDs := make([]string, len(parsedIDs))
for i, id := range parsedIDs { for i, id := range parsedIDs {
txIDs[i] = common.QueuedTxID(id) txIDs[i] = id
} }
results := statusAPI.DiscardTransactions(txIDs) results := statusAPI.DiscardTransactions(txIDs)
for txID, result := range results { for txID, err := range results {
txResult := common.DiscardTransactionResult{ out.Results[txID] = DiscardTransactionResult{
ID: string(txID), ID: txID,
Error: err.Error(),
} }
if result.Error != nil {
txResult.Error = result.Error.Error()
}
out.Results[string(txID)] = txResult
} }
} }

View File

@ -31,11 +31,9 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"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/params" "github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/signal" "github.com/status-im/status-go/geth/signal"
"github.com/status-im/status-go/geth/transactions" "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/static"
. "github.com/status-im/status-go/t/utils" //nolint: golint . "github.com/status-im/status-go/t/utils" //nolint: golint
) )
@ -70,10 +68,10 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
// prepare accounts // prepare accounts
testKeyDir := filepath.Join(testChainDir, "keystore") testKeyDir := filepath.Join(testChainDir, "keystore")
if err := common.ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil { if err := ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil {
panic(err) panic(err)
} }
if err := common.ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil { if err := ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil {
panic(err) panic(err)
} }
@ -175,10 +173,10 @@ func testVerifyAccountPassword(t *testing.T) bool {
} }
defer os.RemoveAll(tmpDir) // nolint: errcheck defer os.RemoveAll(tmpDir) // nolint: errcheck
if err = common.ImportTestAccount(tmpDir, GetAccount1PKFile()); err != nil { if err = ImportTestAccount(tmpDir, GetAccount1PKFile()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err = common.ImportTestAccount(tmpDir, GetAccount2PKFile()); err != nil { if err = ImportTestAccount(tmpDir, GetAccount2PKFile()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -798,7 +796,7 @@ func testCompleteTransaction(t *testing.T) bool {
event := envelope.Event.(map[string]interface{}) event := envelope.Event.(map[string]interface{})
t.Logf("transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string)) 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)) rawResponse := CompleteTransaction(C.CString(event["id"].(string)), C.CString(TestConfig.Account1.Password))
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &completeTxResponse); err != nil { 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 // 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), 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 // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
sendTx := func() { sendTx := func() {
txHashCheck, err := statusAPI.SendTransaction(context.TODO(), common.SendTxArgs{ txHashCheck, err := statusAPI.SendTransaction(context.TODO(), transactions.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),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), Value: (*hexutil.Big)(big.NewInt(1000000000000)),
@ -912,14 +910,14 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocyc
// complete // complete
resultsString := CompleteTransactions(C.CString(string(updatedTxIDStrings)), C.CString(TestConfig.Account1.Password)) 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 { if err := json.Unmarshal([]byte(C.GoString(resultsString)), &resultsStruct); err != nil {
t.Error(err) t.Error(err)
return return
} }
results := resultsStruct.Results 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) t.Errorf("cannot complete txs: %v", results)
return 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 time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
for _, txID := range parsedIDs { 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) t.Errorf("txqueue should not have test tx at this point (it should be completed): %s", txID)
return return
} }
@ -1010,13 +1008,13 @@ func testDiscardTransaction(t *testing.T) bool { //nolint: gocyclo
txID = event["id"].(string) txID = event["id"].(string)
t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txID) 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) t.Errorf("txqueue should still have test tx: %s", txID)
return return
} }
// discard // discard
discardResponse := common.DiscardTransactionResult{} discardResponse := DiscardTransactionResult{}
rawResponse := DiscardTransaction(C.CString(txID)) rawResponse := DiscardTransaction(C.CString(txID))
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &discardResponse); err != nil { 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 // try completing discarded transaction
_, err := statusAPI.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password) _, err := statusAPI.CompleteTransaction(string(txID), TestConfig.Account1.Password)
if err != queue.ErrQueuedTxIDNotFound { if err != transactions.ErrQueuedTxIDNotFound {
t.Error("expects tx not found, but call to CompleteTransaction succeeded") t.Error("expects tx not found, but call to CompleteTransaction succeeded")
return return
} }
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates 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) t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txID)
return return
} }
@ -1066,7 +1064,7 @@ func testDiscardTransaction(t *testing.T) bool { //nolint: gocyclo
}) })
// this call blocks, and should return when DiscardQueuedTransaction() is called // 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), Value: (*hexutil.Big)(big.NewInt(1000000000000)),
@ -1123,7 +1121,7 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl
txID = event["id"].(string) txID = event["id"].(string)
t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txID) 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) t.Errorf("txqueue should still have test tx: %s", txID)
return 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 // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
sendTx := func() { sendTx := func() {
txHashCheck, err := statusAPI.SendTransaction(context.TODO(), common.SendTxArgs{ txHashCheck, err := statusAPI.SendTransaction(context.TODO(), transactions.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),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), Value: (*hexutil.Big)(big.NewInt(1000000000000)),
@ -1186,21 +1184,21 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool { //nolint: gocycl
// discard // discard
discardResultsString := DiscardTransactions(C.CString(string(updatedTxIDStrings))) discardResultsString := DiscardTransactions(C.CString(string(updatedTxIDStrings)))
discardResultsStruct := common.DiscardTransactionsResult{} discardResultsStruct := DiscardTransactionsResult{}
if err := json.Unmarshal([]byte(C.GoString(discardResultsString)), &discardResultsStruct); err != nil { if err := json.Unmarshal([]byte(C.GoString(discardResultsString)), &discardResultsStruct); err != nil {
t.Error(err) t.Error(err)
return return
} }
discardResults := discardResultsStruct.Results 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) t.Errorf("cannot discard txs: %v", discardResults)
return return
} }
// try completing discarded transaction // try completing discarded transaction
completeResultsString := CompleteTransactions(C.CString(string(updatedTxIDStrings)), C.CString(TestConfig.Account1.Password)) 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 { if err := json.Unmarshal([]byte(C.GoString(completeResultsString)), &completeResultsStruct); err != nil {
t.Error(err) t.Error(err)
return 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) t.Errorf("tx id not set in result: expected id is %s", txID)
return return
} }
if txResult.Error != queue.ErrQueuedTxIDNotFound.Error() { if txResult.Error != transactions.ErrQueuedTxIDNotFound.Error() {
t.Errorf("invalid error for %s", txResult.Hash) t.Errorf("invalid error for %s", txResult.Hash)
return 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 time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
for _, txID := range parsedIDs { 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) t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txID)
return return
} }
@ -1412,10 +1410,10 @@ func startTestNode(t *testing.T) <-chan struct{} {
// inject test accounts // inject test accounts
testKeyDir := filepath.Join(testDir, "keystore") testKeyDir := filepath.Join(testDir, "keystore")
if err := common.ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil { if err := ImportTestAccount(testKeyDir, GetAccount1PKFile()); err != nil {
panic(err) panic(err)
} }
if err := common.ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil { if err := ImportTestAccount(testKeyDir, GetAccount2PKFile()); err != nil {
panic(err) panic(err)
} }

View File

@ -75,3 +75,26 @@ type NotifyResult struct {
Status bool `json:"status"` Status bool `json:"status"`
Error string `json:"error,omitempty"` 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"`
}

View File

@ -9,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"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/jail" "github.com/status-im/status-go/geth/jail"
"github.com/status-im/status-go/geth/node" "github.com/status-im/status-go/geth/node"
"github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/geth/params"
@ -148,13 +147,13 @@ func (s *APIBackendTestSuite) TestRaceConditions() {
}, },
func(config *params.NodeConfig) { func(config *params.NodeConfig) {
log.Info("CompleteTransactions()") log.Info("CompleteTransactions()")
ids := []common.QueuedTxID{"id1", "id2"} ids := []string{"id1", "id2"}
s.T().Logf("CompleteTransactions(), result: %v", s.Backend.CompleteTransactions(ids, "password")) s.T().Logf("CompleteTransactions(), result: %v", s.Backend.CompleteTransactions(ids, "password"))
progress <- struct{}{} progress <- struct{}{}
}, },
func(config *params.NodeConfig) { func(config *params.NodeConfig) {
log.Info("DiscardTransactions()") log.Info("DiscardTransactions()")
ids := []common.QueuedTxID{"id1", "id2"} ids := []string{"id1", "id2"}
s.T().Logf("DiscardTransactions(), result: %v", s.Backend.DiscardTransactions(ids)) s.T().Logf("DiscardTransactions(), result: %v", s.Backend.DiscardTransactions(ids))
progress <- struct{}{} progress <- struct{}{}
}, },

View File

@ -10,7 +10,6 @@ import (
"time" "time"
gethcommon "github.com/ethereum/go-ethereum/common" 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/jail"
"github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/signal" "github.com/status-im/status-go/geth/signal"
@ -135,7 +134,7 @@ func (s *JailRPCTestSuite) TestContractDeployment() {
txID := event["id"].(string) txID := event["id"].(string)
var txErr error 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"]) { if s.NoError(txErr, event["id"]) {
s.T().Logf("contract transaction complete, URL: %s", "https://ropsten.etherscan.io/tx/"+txHash.Hex()) 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 //var txHash common.Hash
txID := event["id"].(string) 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.NoError(e, "cannot complete queued transaction[%v]: %v", event["id"], e)
s.T().Logf("Transaction complete: https://ropsten.etherscan.io/tx/%s", txHash.Hex()) s.T().Logf("Transaction complete: https://ropsten.etherscan.io/tx/%s", txHash.Hex())

View File

@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/les"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"github.com/status-im/status-go/geth/api" "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/node"
"github.com/status-im/status-go/geth/signal" "github.com/status-im/status-go/geth/signal"
@ -142,10 +141,10 @@ func (s *BackendTestSuite) TxQueueManager() *transactions.Manager {
func importTestAccounts(keyStoreDir string) (err error) { func importTestAccounts(keyStoreDir string) (err error) {
logger.Debug("Import accounts to", "dir", keyStoreDir) logger.Debug("Import accounts to", "dir", keyStoreDir)
err = common.ImportTestAccount(keyStoreDir, GetAccount1PKFile()) err = ImportTestAccount(keyStoreDir, GetAccount1PKFile())
if err != nil { if err != nil {
return return
} }
return common.ImportTestAccount(keyStoreDir, GetAccount2PKFile()) return ImportTestAccount(keyStoreDir, GetAccount2PKFile())
} }

View File

@ -15,17 +15,15 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"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/params" "github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/signal" "github.com/status-im/status-go/geth/signal"
"github.com/status-im/status-go/geth/transactions" "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" e2e "github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils" . "github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
type initFunc func([]byte, *common.SendTxArgs) type initFunc func([]byte, *transactions.SendTxArgs)
func TestTransactionsTestSuite(t *testing.T) { func TestTransactionsTestSuite(t *testing.T) {
suite.Run(t, new(TransactionsTestSuite)) suite.Run(t, new(TransactionsTestSuite))
@ -55,7 +53,7 @@ func (s *TransactionsTestSuite) TestCallRPCSendTransaction() {
if sg.Type == transactions.EventTransactionQueued { if sg.Type == transactions.EventTransactionQueued {
event := sg.Event.(map[string]interface{}) event := sg.Event.(map[string]interface{})
txID := event["id"].(string) 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) s.NoError(err, "cannot complete queued transaction %s", txID)
close(transactionCompleted) close(transactionCompleted)
@ -109,11 +107,11 @@ func (s *TransactionsTestSuite) TestCallRPCSendTransactionUpstream() {
txID := event["id"].(string) txID := event["id"].(string)
// Complete with a wrong passphrase. // 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") s.EqualError(err, keystore.ErrDecrypt.Error(), "should return an error as the passphrase was invalid")
// Complete with a correct passphrase. // 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) s.NoError(err, "cannot complete queued transaction %s", txID)
close(transactionCompleted) close(transactionCompleted)
@ -144,7 +142,7 @@ func (s *TransactionsTestSuite) TestCallRPCSendTransactionUpstream() {
// TestSendContractCompat tries to send transaction using the legacy "Data" // TestSendContractCompat tries to send transaction using the legacy "Data"
// field, which is supported for backward compatibility reasons. // field, which is supported for backward compatibility reasons.
func (s *TransactionsTestSuite) TestSendContractTxCompat() { func (s *TransactionsTestSuite) TestSendContractTxCompat() {
initFunc := func(byteCode []byte, args *common.SendTxArgs) { initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Data = (hexutil.Bytes)(byteCode) args.Data = (hexutil.Bytes)(byteCode)
} }
s.testSendContractTx(initFunc, nil, "") s.testSendContractTx(initFunc, nil, "")
@ -155,7 +153,7 @@ func (s *TransactionsTestSuite) TestSendContractTxCompat() {
// they have different values. // they have different values.
func (s *TransactionsTestSuite) TestSendContractTxCollision() { func (s *TransactionsTestSuite) TestSendContractTxCollision() {
// Scenario 1: Both fields are filled and have the same value, expect success // 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.Input = (hexutil.Bytes)(byteCode)
args.Data = (hexutil.Bytes)(byteCode) args.Data = (hexutil.Bytes)(byteCode)
} }
@ -171,15 +169,15 @@ func (s *TransactionsTestSuite) TestSendContractTxCollision() {
return inverse return inverse
} }
initFunc2 := func(byteCode []byte, args *common.SendTxArgs) { initFunc2 := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Input = (hexutil.Bytes)(byteCode) args.Input = (hexutil.Bytes)(byteCode)
args.Data = (hexutil.Bytes)(inverted(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() { func (s *TransactionsTestSuite) TestSendContractTx() {
initFunc := func(byteCode []byte, args *common.SendTxArgs) { initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Input = (hexutil.Bytes)(byteCode) args.Input = (hexutil.Bytes)(byteCode)
} }
s.testSendContractTx(initFunc, nil, "") 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) // 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") log.Info("trying to complete with no user logged in")
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), string(event["id"].(string)),
TestConfig.Account1.Password, TestConfig.Account1.Password,
) )
s.EqualError( s.EqualError(
@ -224,12 +222,12 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc
err = s.Backend.SelectAccount(sampleAddress, TestConfig.Account1.Password) err = s.Backend.SelectAccount(sampleAddress, TestConfig.Account1.Password)
s.NoError(err) s.NoError(err)
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), string(event["id"].(string)),
TestConfig.Account1.Password, TestConfig.Account1.Password,
) )
s.EqualError( s.EqualError(
err, err,
queue.ErrInvalidCompleteTxSender.Error(), transactions.ErrInvalidCompleteTxSender.Error(),
fmt.Sprintf("expected error on queued transaction[%v] not thrown", event["id"]), 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") log.Info("trying to complete with correct user, this should succeed")
s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)) s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), string(event["id"].(string)),
TestConfig.Account1.Password, TestConfig.Account1.Password,
) )
s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"])) 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) s.NoError(err)
gas := uint64(params.DefaultGas) gas := uint64(params.DefaultGas)
args := common.SendTxArgs{ args := transactions.SendTxArgs{
From: account.FromAddress(TestConfig.Account1.Address), From: account.FromAddress(TestConfig.Account1.Address),
To: nil, // marker, contract creation is expected To: nil, // marker, contract creation is expected
//Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), gethcommon.Ether)), //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) // 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") log.Info("trying to complete with no user logged in")
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), string(event["id"].(string)),
TestConfig.Account1.Password, TestConfig.Account1.Password,
) )
s.EqualError( s.EqualError(
@ -320,10 +318,10 @@ func (s *TransactionsTestSuite) TestSendEther() {
err = s.Backend.SelectAccount(sampleAddress, TestConfig.Account1.Password) err = s.Backend.SelectAccount(sampleAddress, TestConfig.Account1.Password)
s.NoError(err) s.NoError(err)
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), TestConfig.Account1.Password) string(event["id"].(string)), TestConfig.Account1.Password)
s.EqualError( s.EqualError(
err, err,
queue.ErrInvalidCompleteTxSender.Error(), transactions.ErrInvalidCompleteTxSender.Error(),
fmt.Sprintf("expected error on queued transaction[%v] not thrown", event["id"]), 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") log.Info("trying to complete with correct user, this should succeed")
s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)) s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), string(event["id"].(string)),
TestConfig.Account1.Password, TestConfig.Account1.Password,
) )
s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"])) 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 // 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), 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)) log.Info("transaction queued (will be completed shortly)", "id", event["id"].(string))
txHash, err = s.Backend.CompleteTransaction( txHash, err = s.Backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)), string(event["id"].(string)),
TestConfig.Account1.Password, TestConfig.Account1.Password,
) )
s.NoError(err, "cannot complete queued transaction[%v]", event["id"]) 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. // This call blocks, up until Complete Transaction is called.
// Explicitly not setting Gas to get it estimated. // 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
GasPrice: (*hexutil.Big)(big.NewInt(28000000000)), GasPrice: (*hexutil.Big)(big.NewInt(28000000000)),
@ -438,7 +436,7 @@ func (s *TransactionsTestSuite) TestDoubleCompleteQueuedTransactions() {
if envelope.Type == transactions.EventTransactionQueued { if envelope.Type == transactions.EventTransactionQueued {
event := envelope.Event.(map[string]interface{}) 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) log.Info("transaction queued (will be failed and completed on the second call)", "id", txID)
// try with wrong password // 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) // 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), Value: (*hexutil.Big)(big.NewInt(1000000000000)),
@ -514,7 +512,7 @@ func (s *TransactionsTestSuite) TestDiscardQueuedTransaction() {
if envelope.Type == transactions.EventTransactionQueued { if envelope.Type == transactions.EventTransactionQueued {
event := envelope.Event.(map[string]interface{}) 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) log.Info("transaction queued (will be discarded soon)", "id", txID)
s.True(s.Backend.TxQueueManager().TransactionQueue().Has(txID), "txqueue should still have test tx") 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 // 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), 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)) s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
testTxCount := 3 testTxCount := 3
txIDs := make(chan common.QueuedTxID, testTxCount) txIDs := make(chan string, testTxCount)
allTestTxDiscarded := make(chan struct{}) allTestTxDiscarded := make(chan struct{})
// replace transaction notification handler // replace transaction notification handler
@ -605,7 +603,7 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() {
s.NoError(err) s.NoError(err)
if envelope.Type == transactions.EventTransactionQueued { if envelope.Type == transactions.EventTransactionQueued {
event := envelope.Event.(map[string]interface{}) 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) log.Info("transaction queued (will be discarded soon)", "id", txID)
s.True(s.Backend.TxQueueManager().TransactionQueue().Has(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 // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
sendTx := func() { 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), Value: (*hexutil.Big)(big.NewInt(1000000000000)),
@ -647,13 +645,13 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() {
txQueueManager := s.Backend.TxQueueManager() txQueueManager := s.Backend.TxQueueManager()
// wait for transactions, and discard immediately // wait for transactions, and discard immediately
discardTxs := func(txIDs []common.QueuedTxID) { discardTxs := func(txIDs []string) {
txIDs = append(txIDs, "invalid-tx-id") txIDs = append(txIDs, "invalid-tx-id")
// discard // discard
discardResults := s.Backend.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"], "transaction hash not found", "cannot discard txs: %v", discardResults)
// try completing discarded transaction // try completing discarded transaction
completeResults := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password) completeResults := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
@ -675,7 +673,7 @@ func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() {
} }
} }
go func() { go func() {
ids := make([]common.QueuedTxID, testTxCount) ids := make([]string, testTxCount)
for i := 0; i < testTxCount; i++ { for i := 0; i < testTxCount; i++ {
ids[i] = <-txIDs ids[i] = <-txIDs
} }
@ -710,7 +708,7 @@ func (s *TransactionsTestSuite) TestNonExistentQueuedTransactions() {
// try completing non-existing transaction // try completing non-existing transaction
_, err := s.Backend.CompleteTransaction("some-bad-transaction-id", TestConfig.Account1.Password) _, err := s.Backend.CompleteTransaction("some-bad-transaction-id", TestConfig.Account1.Password)
s.Error(err, "error expected and not received") s.Error(err, "error expected and not received")
s.EqualError(err, queue.ErrQueuedTxIDNotFound.Error()) s.EqualError(err, transactions.ErrQueuedTxIDNotFound.Error())
} }
func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() { func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() {
@ -719,7 +717,7 @@ func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() {
var m sync.Mutex var m sync.Mutex
txCount := 0 txCount := 0
txIDs := [queue.DefaultTxQueueCap + 5 + 10]common.QueuedTxID{} txIDs := [transactions.DefaultTxQueueCap + 5 + 10]string{}
signal.SetDefaultNodeNotificationHandler(func(rawSignal string) { signal.SetDefaultNodeNotificationHandler(func(rawSignal string) {
var sg signal.Envelope var sg signal.Envelope
@ -730,7 +728,7 @@ func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() {
event := sg.Event.(map[string]interface{}) event := sg.Event.(map[string]interface{})
txID := event["id"].(string) txID := event["id"].(string)
m.Lock() m.Lock()
txIDs[txCount] = common.QueuedTxID(txID) txIDs[txCount] = string(txID)
txCount++ txCount++
m.Unlock() m.Unlock()
} }
@ -746,17 +744,17 @@ func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() {
s.Zero(txQueue.Count(), "transaction count should be zero") s.Zero(txQueue.Count(), "transaction count should be zero")
for j := 0; j < 10; j++ { 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) time.Sleep(2 * time.Second)
s.Equal(10, txQueue.Count(), "transaction count should be 10") 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 for i := 0; i < transactions.DefaultTxQueueCap+5; i++ { // stress test by hitting with lots of goroutines
go s.Backend.SendTransaction(context.TODO(), common.SendTxArgs{}) // nolint: errcheck go s.Backend.SendTransaction(context.TODO(), transactions.SendTxArgs{}) // nolint: errcheck
} }
time.Sleep(5 * time.Second) 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() m.Lock()
for _, txID := range txIDs { for _, txID := range txIDs {
@ -796,7 +794,7 @@ func (s *TransactionsTestSuite) setupUpstreamNode() {
} }
func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) { func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) {
txIDs := make(chan common.QueuedTxID, testTxCount) txIDs := make(chan string, testTxCount)
allTestTxCompleted := make(chan struct{}) allTestTxCompleted := make(chan struct{})
require := s.Require() require := s.Require()
@ -809,7 +807,7 @@ func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) {
if envelope.Type == transactions.EventTransactionQueued { if envelope.Type == transactions.EventTransactionQueued {
event := envelope.Event.(map[string]interface{}) 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) log.Info("transaction queued (will be completed in a single call, once aggregated)", "id", txID)
txIDs <- 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 // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
sendTx := func() { 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), From: account.FromAddress(TestConfig.Account1.Address),
To: account.ToAddress(TestConfig.Account2.Address), To: account.ToAddress(TestConfig.Account2.Address),
Value: (*hexutil.Big)(big.NewInt(1000000000000)), 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 // 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") txIDs = append(txIDs, "invalid-tx-id")
results := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password) results := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
s.Len(results, testTxCount+1) s.Len(results, testTxCount+1)
@ -856,7 +854,7 @@ func (s *TransactionsTestSuite) sendConcurrentTransactions(testTxCount int) {
} }
} }
go func() { go func() {
ids := make([]common.QueuedTxID, testTxCount) ids := make([]string, testTxCount)
for i := 0; i < testTxCount; i++ { for i := 0; i < testTxCount; i++ {
ids[i] = <-txIDs ids[i] = <-txIDs
} }

View File

@ -2,10 +2,12 @@ package utils
import ( import (
"bytes" "bytes"
"encoding/json"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -16,8 +18,8 @@ import (
"github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log" "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/geth/params"
"github.com/status-im/status-go/static"
_ "github.com/stretchr/testify/suite" // required to register testify flags _ "github.com/stretchr/testify/suite" // required to register testify flags
) )
@ -32,7 +34,7 @@ var (
ErrTimeout = errors.New("timeout") ErrTimeout = errors.New("timeout")
// TestConfig defines the default config usable at package-level. // TestConfig defines the default config usable at package-level.
TestConfig *common.TestConfig TestConfig *testConfig
// RootDir is the main application directory // RootDir is the main application directory
RootDir string RootDir string
@ -75,7 +77,7 @@ func init() {
// setup auxiliary directories // setup auxiliary directories
TestDataDir = filepath.Join(RootDir, ".ethereumtest") TestDataDir = filepath.Join(RootDir, ".ethereumtest")
TestConfig, err = common.LoadTestConfig(GetNetworkID()) TestConfig, err = loadTestConfig(GetNetworkID())
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -282,3 +284,66 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
} }
return nodeConfig, nil 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
}