2017-10-11 14:20:51 +00:00
package transactions
2017-05-16 12:09:52 +00:00
import (
2017-10-21 17:04:07 +00:00
"context"
2017-05-16 12:09:52 +00:00
"encoding/json"
"fmt"
"math/big"
"reflect"
2017-10-11 14:20:51 +00:00
"testing"
2017-05-16 12:09:52 +00:00
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
2017-10-11 14:20:51 +00:00
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/e2e"
2017-09-26 13:44:26 +00:00
"github.com/status-im/status-go/geth/account"
2017-05-16 12:09:52 +00:00
"github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/geth/params"
2017-09-25 18:22:57 +00:00
"github.com/status-im/status-go/geth/signal"
2017-09-27 00:50:41 +00:00
"github.com/status-im/status-go/geth/txqueue"
2017-10-11 14:20:51 +00:00
. "github.com/status-im/status-go/testing"
"github.com/stretchr/testify/suite"
2017-05-16 12:09:52 +00:00
)
2017-10-11 14:20:51 +00:00
func TestTransactionsTestSuite ( t * testing . T ) {
suite . Run ( t , new ( TransactionsTestSuite ) )
}
type TransactionsTestSuite struct {
e2e . BackendTestSuite
}
func ( s * TransactionsTestSuite ) TestCallRPCSendTransaction ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-10-11 14:20:51 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-10-11 14:20:51 +00:00
err := s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password )
s . NoError ( err )
transactionCompleted := make ( chan struct { } )
var txHash gethcommon . Hash
signal . SetDefaultNodeNotificationHandler ( func ( rawSignal string ) {
2017-10-20 09:06:22 +00:00
var sg signal . Envelope
err := json . Unmarshal ( [ ] byte ( rawSignal ) , & sg )
2017-10-11 14:20:51 +00:00
s . NoError ( err )
2017-10-20 09:06:22 +00:00
if sg . Type == txqueue . EventTransactionQueued {
event := sg . Event . ( map [ string ] interface { } )
2017-10-11 14:20:51 +00:00
txID := event [ "id" ] . ( string )
txHash , err = s . Backend . CompleteTransaction ( common . QueuedTxID ( txID ) , TestConfig . Account1 . Password )
s . NoError ( err , "cannot complete queued transaction %s" , txID )
close ( transactionCompleted )
}
} )
result := s . Backend . CallRPC ( ` {
"jsonrpc" : "2.0" ,
"id" : 1 ,
"method" : "eth_sendTransaction" ,
"params" : [ {
"from" : "` + TestConfig.Account1.Address + `" ,
"to" : "0xd46e8dd67c5d32be8058bb8eb970870f07244567" ,
"value" : "0x9184e72a"
} ]
} ` )
s . NotContains ( result , "error" )
select {
case <- transactionCompleted :
case <- time . After ( time . Minute ) :
s . FailNow ( "sending transaction timed out" )
}
s . Equal ( ` { "jsonrpc":"2.0","id":1,"result":" ` + txHash . String ( ) + ` "} ` , result )
}
func ( s * TransactionsTestSuite ) TestCallRPCSendTransactionUpstream ( ) {
2017-10-26 12:33:42 +00:00
if GetNetworkID ( ) == params . StatusChainNetworkID {
s . T ( ) . Skip ( )
}
2017-10-26 13:11:24 +00:00
addr , err := GetRemoteURL ( )
2017-10-25 12:37:42 +00:00
s . NoError ( err )
s . StartTestBackend ( e2e . WithUpstream ( addr ) )
2017-10-11 14:20:51 +00:00
defer s . StopTestBackend ( )
2017-10-25 12:37:42 +00:00
err = s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account2 . Address , TestConfig . Account2 . Password )
2017-10-11 14:20:51 +00:00
s . NoError ( err )
transactionCompleted := make ( chan struct { } )
var txHash gethcommon . Hash
signal . SetDefaultNodeNotificationHandler ( func ( rawSignal string ) {
2017-10-24 16:48:31 +00:00
var signalEnvelope signal . Envelope
err := json . Unmarshal ( [ ] byte ( rawSignal ) , & signalEnvelope )
2017-10-11 14:20:51 +00:00
s . NoError ( err )
2017-10-24 16:48:31 +00:00
if signalEnvelope . Type == txqueue . EventTransactionQueued {
event := signalEnvelope . Event . ( map [ string ] interface { } )
2017-10-11 14:20:51 +00:00
txID := event [ "id" ] . ( string )
// Complete with a wrong passphrase.
txHash , err = s . Backend . CompleteTransaction ( common . QueuedTxID ( txID ) , "some-invalid-passphrase" )
s . EqualError ( err , keystore . ErrDecrypt . Error ( ) , "should return an error as the passphrase was invalid" )
// Complete with a correct passphrase.
txHash , err = s . Backend . CompleteTransaction ( common . QueuedTxID ( txID ) , TestConfig . Account2 . Password )
s . NoError ( err , "cannot complete queued transaction %s" , txID )
close ( transactionCompleted )
}
} )
result := s . Backend . CallRPC ( ` {
"jsonrpc" : "2.0" ,
"id" : 1 ,
"method" : "eth_sendTransaction" ,
"params" : [ {
"from" : "` + TestConfig.Account2.Address + `" ,
"to" : "` + TestConfig.Account1.Address + `" ,
"value" : "0x9184e72a"
} ]
} ` )
s . NotContains ( result , "error" )
select {
case <- transactionCompleted :
case <- time . After ( time . Minute ) :
s . FailNow ( "sending transaction timed out" )
}
s . Equal ( ` { "jsonrpc":"2.0","id":1,"result":" ` + txHash . String ( ) + ` "} ` , result )
}
// FIXME(tiabc): Sometimes it fails due to "no suitable peers found".
func ( s * TransactionsTestSuite ) TestSendContractTx ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-05-16 12:09:52 +00:00
2017-10-11 14:20:51 +00:00
sampleAddress , _ , _ , err := s . Backend . AccountManager ( ) . CreateAccount ( TestConfig . Account1 . Password )
s . NoError ( err )
2017-05-16 12:09:52 +00:00
2017-09-15 10:35:31 +00:00
completeQueuedTransaction := make ( chan struct { } )
2017-05-16 12:09:52 +00:00
// replace transaction notification handler
2017-09-04 12:56:58 +00:00
var txHash gethcommon . Hash
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) { // nolint :dupl
var envelope signal . Envelope
2017-10-26 10:15:40 +00:00
err = json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
2017-05-16 12:09:52 +00:00
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionQueued {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction queued (will be completed shortly)" , "id" , event [ "id" ] . ( string ) )
// 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" )
2017-10-11 14:20:51 +00:00
txHash , err = s . Backend . CompleteTransaction (
2017-09-04 12:56:58 +00:00
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . EqualError (
err ,
2017-09-26 13:44:26 +00:00
account . ErrNoAccountSelected . Error ( ) ,
2017-09-04 12:56:58 +00:00
fmt . Sprintf ( "expected error on queued transaction[%v] not thrown" , event [ "id" ] ) ,
)
2017-05-16 12:09:52 +00:00
// the second call will also fail (we are logged in as different user)
log . Info ( "trying to complete with invalid user" )
2017-10-11 14:20:51 +00:00
err = s . Backend . AccountManager ( ) . SelectAccount ( sampleAddress , TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
s . NoError ( err )
2017-10-11 14:20:51 +00:00
txHash , err = s . Backend . CompleteTransaction (
2017-09-04 12:56:58 +00:00
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . EqualError (
err ,
2017-09-27 00:50:41 +00:00
txqueue . ErrInvalidCompleteTxSender . Error ( ) ,
2017-09-04 12:56:58 +00:00
fmt . Sprintf ( "expected error on queued transaction[%v] not thrown" , event [ "id" ] ) ,
)
2017-05-16 12:09:52 +00:00
// the third call will work as expected (as we are logged in with correct credentials)
2017-09-04 12:56:58 +00:00
log . Info ( "trying to complete with correct user, this should succeed" )
2017-10-11 14:20:51 +00:00
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
txHash , err = s . Backend . CompleteTransaction (
2017-09-04 12:56:58 +00:00
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
2017-05-16 12:09:52 +00:00
s . NoError ( err , fmt . Sprintf ( "cannot complete queued transaction[%v]" , event [ "id" ] ) )
2017-09-15 10:35:31 +00:00
log . Info ( "contract transaction complete" , "URL" , "https://ropsten.etherscan.io/tx/" + txHash . Hex ( ) )
2017-05-16 12:09:52 +00:00
close ( completeQueuedTransaction )
return
}
} )
// this call blocks, up until Complete Transaction is called
byteCode , err := hexutil . Decode ( ` 0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029 ` )
2017-10-11 14:20:51 +00:00
s . NoError ( err )
2017-05-16 12:09:52 +00:00
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-05-16 12:09:52 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : nil , // marker, contract creation is expected
//Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), gethcommon.Ether)),
Gas : ( * hexutil . Big ) ( big . NewInt ( params . DefaultGas ) ) ,
Data : byteCode ,
} )
s . NoError ( err , "cannot send transaction" )
2017-09-15 10:35:31 +00:00
select {
case <- completeQueuedTransaction :
case <- time . After ( 2 * time . Minute ) :
s . FailNow ( "completing transaction timed out" )
}
2017-05-16 12:09:52 +00:00
s . Equal ( txHashCheck . Hex ( ) , txHash . Hex ( ) , "transaction hash returned from SendTransaction is invalid" )
s . False ( reflect . DeepEqual ( txHashCheck , gethcommon . Hash { } ) , "transaction was never queued or completed" )
2017-09-04 12:56:58 +00:00
s . Zero ( s . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
}
2017-10-23 11:05:52 +00:00
func ( s * TransactionsTestSuite ) TestSendEther ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-10-16 21:54:56 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-10-23 11:05:52 +00:00
2017-10-16 21:54:56 +00:00
backend := s . LightEthereumService ( ) . StatusBackend
s . NotNil ( backend )
// create an account
sampleAddress , _ , _ , err := s . Backend . AccountManager ( ) . CreateAccount ( TestConfig . Account1 . Password )
s . NoError ( err )
completeQueuedTransaction := make ( chan struct { } )
// replace transaction notification handler
var txHash = gethcommon . Hash { }
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) { // nolint: dupl
var envelope signal . Envelope
2017-10-26 10:15:40 +00:00
err = json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
2017-10-16 21:54:56 +00:00
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
if envelope . Type == txqueue . EventTransactionQueued {
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction queued (will be completed shortly)" , "id" , event [ "id" ] . ( string ) )
// the first call will fail (we are not logged in, but trying to complete tx)
log . Info ( "trying to complete with no user logged in" )
txHash , err = s . Backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . EqualError (
err ,
account . ErrNoAccountSelected . Error ( ) ,
fmt . Sprintf ( "expected error on queued transaction[%v] not thrown" , event [ "id" ] ) ,
)
// the second call will also fail (we are logged in as different user)
log . Info ( "trying to complete with invalid user" )
err = s . Backend . AccountManager ( ) . SelectAccount ( sampleAddress , TestConfig . Account1 . Password )
s . NoError ( err )
txHash , err = s . Backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) , TestConfig . Account1 . Password )
s . EqualError (
err ,
txqueue . ErrInvalidCompleteTxSender . Error ( ) ,
fmt . Sprintf ( "expected error on queued transaction[%v] not thrown" , event [ "id" ] ) ,
)
// the third call will work as expected (as we are logged in with correct credentials)
log . Info ( "trying to complete with correct user, this should succeed" )
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
txHash , err = s . Backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . NoError ( err , fmt . Sprintf ( "cannot complete queued transaction[%v]" , event [ "id" ] ) )
close ( completeQueuedTransaction )
return
}
} )
// this call blocks, up until Complete Transaction is called
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-10-16 21:54:56 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : common . ToAddress ( TestConfig . Account2 . Address ) ,
Value : ( * hexutil . Big ) ( big . NewInt ( 1000000000000 ) ) ,
} )
s . NoError ( err , "cannot send transaction" )
select {
case <- completeQueuedTransaction :
case <- time . After ( 2 * time . Minute ) :
s . FailNow ( "completing transaction timed out" )
}
s . Equal ( txHashCheck . Hex ( ) , txHash . Hex ( ) , "transaction hash returned from SendTransaction is invalid" )
2017-05-16 12:09:52 +00:00
s . False ( reflect . DeepEqual ( txHashCheck , gethcommon . Hash { } ) , "transaction was never queued or completed" )
2017-10-11 14:20:51 +00:00
s . Zero ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestSendEtherTxUpstream ( ) {
2017-10-26 12:33:42 +00:00
if GetNetworkID ( ) == params . StatusChainNetworkID {
s . T ( ) . Skip ( )
}
2017-10-26 13:11:24 +00:00
addr , err := GetRemoteURL ( )
2017-10-25 12:37:42 +00:00
s . NoError ( err )
s . StartTestBackend ( e2e . WithUpstream ( addr ) )
2017-09-19 11:19:18 +00:00
defer s . StopTestBackend ( )
2017-10-25 12:37:42 +00:00
err = s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password )
2017-09-19 11:19:18 +00:00
s . NoError ( err )
completeQueuedTransaction := make ( chan struct { } )
// replace transaction notification handler
var txHash = gethcommon . Hash { }
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) { // nolint: dupl
var envelope signal . Envelope
2017-10-26 10:15:40 +00:00
err = json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
2017-09-19 11:19:18 +00:00
s . NoError ( err , "cannot unmarshal JSON: %s" , jsonEvent )
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionQueued {
2017-09-19 11:19:18 +00:00
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction queued (will be completed shortly)" , "id" , event [ "id" ] . ( string ) )
2017-10-11 14:20:51 +00:00
txHash , err = s . Backend . CompleteTransaction (
2017-09-19 11:19:18 +00:00
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . NoError ( err , "cannot complete queued transaction[%v]" , event [ "id" ] )
log . Info ( "contract transaction complete" , "URL" , "https://ropsten.etherscan.io/tx/" + txHash . Hex ( ) )
close ( completeQueuedTransaction )
}
} )
// This call blocks, up until Complete Transaction is called.
// Explicitly not setting Gas to get it estimated.
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-09-19 11:19:18 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : common . ToAddress ( TestConfig . Account2 . Address ) ,
GasPrice : ( * hexutil . Big ) ( big . NewInt ( 28000000000 ) ) ,
Value : ( * hexutil . Big ) ( big . NewInt ( 1000000000000 ) ) ,
} )
s . NoError ( err , "cannot send transaction" )
select {
case <- completeQueuedTransaction :
case <- time . After ( 1 * time . Minute ) :
s . FailNow ( "completing transaction timed out" )
}
s . Equal ( txHash . Hex ( ) , txHashCheck . Hex ( ) , "transaction hash returned from SendTransaction is invalid" )
2017-10-11 14:20:51 +00:00
s . Zero ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-09-19 11:19:18 +00:00
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestDoubleCompleteQueuedTransactions ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-05-16 12:09:52 +00:00
backend := s . LightEthereumService ( ) . StatusBackend
2017-10-11 14:20:51 +00:00
s . NotNil ( backend )
2017-05-16 12:09:52 +00:00
// log into account from which transactions will be sent
2017-10-11 14:20:51 +00:00
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-05-16 12:09:52 +00:00
2017-09-15 10:35:31 +00:00
completeQueuedTransaction := make ( chan struct { } )
2017-05-16 12:09:52 +00:00
// replace transaction notification handler
txFailedEventCalled := false
txHash := gethcommon . Hash { }
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) {
var envelope signal . Envelope
2017-05-16 12:09:52 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionQueued {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
2017-09-04 12:56:58 +00:00
txID := common . QueuedTxID ( event [ "id" ] . ( string ) )
2017-05-16 12:09:52 +00:00
log . Info ( "transaction queued (will be failed and completed on the second call)" , "id" , txID )
// try with wrong password
// make sure that tx is NOT removed from the queue (by re-trying with the correct password)
2017-10-11 14:20:51 +00:00
_ , err = s . Backend . CompleteTransaction ( txID , TestConfig . Account1 . Password + "wrong" )
2017-05-16 12:09:52 +00:00
s . EqualError ( err , keystore . ErrDecrypt . Error ( ) )
2017-09-04 12:56:58 +00:00
s . Equal ( 1 , s . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "txqueue cannot be empty, as tx has failed" )
2017-05-16 12:09:52 +00:00
// now try to complete transaction, but with the correct password
2017-10-11 14:20:51 +00:00
txHash , err = s . Backend . CompleteTransaction ( txID , TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
s . NoError ( err )
log . Info ( "transaction complete" , "URL" , "https://rinkeby.etherscan.io/tx/" + txHash . Hex ( ) )
close ( completeQueuedTransaction )
}
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionFailed {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction return event received" , "id" , event [ "id" ] . ( string ) )
receivedErrMessage := event [ "error_message" ] . ( string )
expectedErrMessage := "could not decrypt key with given passphrase"
s . Equal ( receivedErrMessage , expectedErrMessage )
receivedErrCode := event [ "error_code" ] . ( string )
s . Equal ( "2" , receivedErrCode )
txFailedEventCalled = true
}
} )
2017-09-04 12:56:58 +00:00
// this call blocks, and should return on *second* attempt to CompleteTransaction (w/ the correct password)
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-05-16 12:09:52 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : common . ToAddress ( TestConfig . Account2 . Address ) ,
Value : ( * hexutil . Big ) ( big . NewInt ( 1000000000000 ) ) ,
} )
s . NoError ( err , "cannot send transaction" )
2017-09-15 10:35:31 +00:00
select {
case <- completeQueuedTransaction :
case <- time . After ( time . Minute ) :
s . FailNow ( "test timed out" )
}
2017-05-16 12:09:52 +00:00
s . Equal ( txHashCheck . Hex ( ) , txHash . Hex ( ) , "transaction hash returned from SendTransaction is invalid" )
s . False ( reflect . DeepEqual ( txHashCheck , gethcommon . Hash { } ) , "transaction was never queued or completed" )
2017-10-11 14:20:51 +00:00
s . Zero ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
s . True ( txFailedEventCalled , "expected tx failure signal is not received" )
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestDiscardQueuedTransaction ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-05-16 12:09:52 +00:00
backend := s . LightEthereumService ( ) . StatusBackend
2017-10-11 14:20:51 +00:00
s . NotNil ( backend )
2017-05-16 12:09:52 +00:00
// reset queue
2017-10-11 14:20:51 +00:00
s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Reset ( )
2017-05-16 12:09:52 +00:00
// log into account from which transactions will be sent
2017-10-11 14:20:51 +00:00
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-05-16 12:09:52 +00:00
2017-09-15 10:35:31 +00:00
completeQueuedTransaction := make ( chan struct { } )
2017-05-16 12:09:52 +00:00
// replace transaction notification handler
txFailedEventCalled := false
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) {
var envelope signal . Envelope
2017-05-16 12:09:52 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionQueued {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
2017-09-04 12:56:58 +00:00
txID := common . QueuedTxID ( event [ "id" ] . ( string ) )
2017-05-16 12:09:52 +00:00
log . Info ( "transaction queued (will be discarded soon)" , "id" , txID )
2017-10-11 14:20:51 +00:00
s . True ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) , "txqueue should still have test tx" )
2017-05-16 12:09:52 +00:00
// discard
2017-10-11 14:20:51 +00:00
err := s . Backend . DiscardTransaction ( txID )
2017-05-16 12:09:52 +00:00
s . NoError ( err , "cannot discard tx" )
// try completing discarded transaction
2017-10-11 14:20:51 +00:00
_ , err = s . Backend . CompleteTransaction ( txID , TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
s . EqualError ( err , "transaction hash not found" , "expects tx not found, but call to CompleteTransaction succeeded" )
time . Sleep ( 1 * time . Second ) // make sure that tx complete signal propagates
2017-10-11 14:20:51 +00:00
s . False ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) ,
2017-05-16 12:09:52 +00:00
fmt . Sprintf ( "txqueue should not have test tx at this point (it should be discarded): %s" , txID ) )
close ( completeQueuedTransaction )
}
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionFailed {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction return event received" , "id" , event [ "id" ] . ( string ) )
receivedErrMessage := event [ "error_message" ] . ( string )
2017-09-27 00:50:41 +00:00
expectedErrMessage := txqueue . ErrQueuedTxDiscarded . Error ( )
2017-05-16 12:09:52 +00:00
s . Equal ( receivedErrMessage , expectedErrMessage )
receivedErrCode := event [ "error_code" ] . ( string )
s . Equal ( "4" , receivedErrCode )
txFailedEventCalled = true
}
} )
2017-09-04 12:56:58 +00:00
// this call blocks, and should return when DiscardQueuedTransaction() is called
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-05-16 12:09:52 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : common . ToAddress ( TestConfig . Account2 . Address ) ,
Value : ( * hexutil . Big ) ( big . NewInt ( 1000000000000 ) ) ,
} )
2017-09-27 00:50:41 +00:00
s . EqualError ( err , txqueue . ErrQueuedTxDiscarded . Error ( ) , "transaction is expected to be discarded" )
2017-05-16 12:09:52 +00:00
2017-09-15 10:35:31 +00:00
select {
case <- completeQueuedTransaction :
case <- time . After ( time . Minute ) :
s . FailNow ( "test timed out" )
}
2017-05-16 12:09:52 +00:00
s . True ( reflect . DeepEqual ( txHashCheck , gethcommon . Hash { } ) , "transaction returned hash, while it shouldn't" )
2017-10-11 14:20:51 +00:00
s . Zero ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
s . True ( txFailedEventCalled , "expected tx failure signal is not received" )
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestCompleteMultipleQueuedTransactions ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-09-04 12:56:58 +00:00
s . TxQueueManager ( ) . TransactionQueue ( ) . Reset ( )
2017-05-16 12:09:52 +00:00
// log into account from which transactions will be sent
2017-10-11 14:20:51 +00:00
err := s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password )
s . NoError ( err )
2017-05-16 12:09:52 +00:00
testTxCount := 3
2017-09-04 12:56:58 +00:00
txIDs := make ( chan common . QueuedTxID , testTxCount )
2017-09-15 10:35:31 +00:00
allTestTxCompleted := make ( chan struct { } )
2017-05-16 12:09:52 +00:00
// replace transaction notification handler
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) {
var envelope signal . Envelope
2017-05-16 12:09:52 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
2017-09-04 12:56:58 +00:00
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionQueued {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
2017-09-04 12:56:58 +00:00
txID := common . QueuedTxID ( event [ "id" ] . ( string ) )
2017-05-16 12:09:52 +00:00
log . Info ( "transaction queued (will be completed in a single call, once aggregated)" , "id" , txID )
txIDs <- txID
}
} )
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
sendTx := func ( ) {
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-05-16 12:09:52 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : common . ToAddress ( TestConfig . Account2 . Address ) ,
Value : ( * hexutil . Big ) ( big . NewInt ( 1000000000000 ) ) ,
} )
s . NoError ( err , "cannot send transaction" )
s . False ( reflect . DeepEqual ( txHashCheck , gethcommon . Hash { } ) , "transaction returned empty hash" )
}
// wait for transactions, and complete them in a single call
2017-09-04 12:56:58 +00:00
completeTxs := func ( txIDs [ ] common . QueuedTxID ) {
txIDs = append ( txIDs , "invalid-tx-id" )
2017-10-11 14:20:51 +00:00
results := s . Backend . CompleteTransactions ( txIDs , TestConfig . Account1 . Password )
s . Len ( results , testTxCount + 1 )
s . EqualError ( results [ "invalid-tx-id" ] . Error , "transaction hash not found" )
2017-05-16 12:09:52 +00:00
2017-09-02 17:04:23 +00:00
for txID , txResult := range results {
2017-10-11 14:20:51 +00:00
s . False (
2017-09-02 17:04:23 +00:00
txResult . Error != nil && txID != "invalid-tx-id" ,
"invalid error for %s" , txID ,
)
2017-10-11 14:20:51 +00:00
s . False (
2017-09-04 12:56:58 +00:00
txResult . Hash == ( gethcommon . Hash { } ) && txID != "invalid-tx-id" ,
2017-09-02 17:04:23 +00:00
"invalid hash (expected non empty hash): %s" , txID ,
)
2017-09-15 10:35:31 +00:00
log . Info ( "transaction complete" , "URL" , "https://ropsten.etherscan.io/tx/" + txResult . Hash . Hex ( ) )
2017-05-16 12:09:52 +00:00
}
time . Sleep ( 1 * time . Second ) // make sure that tx complete signal propagates
2017-09-04 12:56:58 +00:00
for _ , txID := range txIDs {
2017-10-11 14:20:51 +00:00
s . False (
s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) ,
2017-09-02 17:04:23 +00:00
"txqueue should not have test tx at this point (it should be completed)" ,
)
2017-05-16 12:09:52 +00:00
}
}
go func ( ) {
2017-09-04 12:56:58 +00:00
ids := make ( [ ] common . QueuedTxID , testTxCount )
2017-05-16 12:09:52 +00:00
for i := 0 ; i < testTxCount ; i ++ {
2017-09-04 12:56:58 +00:00
ids [ i ] = <- txIDs
2017-05-16 12:09:52 +00:00
}
2017-09-04 12:56:58 +00:00
completeTxs ( ids )
2017-09-15 10:35:31 +00:00
close ( allTestTxCompleted )
2017-05-16 12:09:52 +00:00
} ( )
// send multiple transactions
for i := 0 ; i < testTxCount ; i ++ {
go sendTx ( )
}
select {
case <- allTestTxCompleted :
2017-07-13 06:54:10 +00:00
case <- time . After ( 30 * time . Second ) :
2017-09-15 10:35:31 +00:00
s . FailNow ( "test timed out" )
2017-05-16 12:09:52 +00:00
}
2017-10-11 14:20:51 +00:00
s . Zero ( s . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "queue should be empty" )
2017-05-16 12:09:52 +00:00
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestDiscardMultipleQueuedTransactions ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
2017-10-23 21:39:13 +00:00
EnsureNodeSync ( s . Backend . NodeManager ( ) )
2017-05-16 12:09:52 +00:00
backend := s . LightEthereumService ( ) . StatusBackend
2017-10-11 14:20:51 +00:00
s . NotNil ( backend )
2017-05-16 12:09:52 +00:00
// reset queue
2017-10-11 14:20:51 +00:00
s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Reset ( )
2017-05-16 12:09:52 +00:00
// log into account from which transactions will be sent
2017-10-11 14:20:51 +00:00
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-05-16 12:09:52 +00:00
testTxCount := 3
2017-09-04 12:56:58 +00:00
txIDs := make ( chan common . QueuedTxID , testTxCount )
2017-09-15 10:35:31 +00:00
allTestTxDiscarded := make ( chan struct { } )
2017-05-16 12:09:52 +00:00
// replace transaction notification handler
txFailedEventCallCount := 0
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( jsonEvent string ) {
var envelope signal . Envelope
2017-05-16 12:09:52 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err )
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionQueued {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
2017-09-04 12:56:58 +00:00
txID := common . QueuedTxID ( event [ "id" ] . ( string ) )
2017-05-16 12:09:52 +00:00
log . Info ( "transaction queued (will be discarded soon)" , "id" , txID )
2017-10-11 14:20:51 +00:00
s . True ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) ,
2017-09-04 12:56:58 +00:00
"txqueue should still have test tx" )
2017-05-16 12:09:52 +00:00
txIDs <- txID
}
2017-09-27 00:50:41 +00:00
if envelope . Type == txqueue . EventTransactionFailed {
2017-05-16 12:09:52 +00:00
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction return event received" , "id" , event [ "id" ] . ( string ) )
receivedErrMessage := event [ "error_message" ] . ( string )
2017-09-27 00:50:41 +00:00
expectedErrMessage := txqueue . ErrQueuedTxDiscarded . Error ( )
2017-05-16 12:09:52 +00:00
s . Equal ( receivedErrMessage , expectedErrMessage )
receivedErrCode := event [ "error_code" ] . ( string )
s . Equal ( "4" , receivedErrCode )
txFailedEventCallCount ++
if txFailedEventCallCount == testTxCount {
2017-09-15 10:35:31 +00:00
close ( allTestTxDiscarded )
2017-05-16 12:09:52 +00:00
}
}
} )
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
sendTx := func ( ) {
2017-10-20 09:06:22 +00:00
txHashCheck , err := s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs {
2017-05-16 12:09:52 +00:00
From : common . FromAddress ( TestConfig . Account1 . Address ) ,
To : common . ToAddress ( TestConfig . Account2 . Address ) ,
Value : ( * hexutil . Big ) ( big . NewInt ( 1000000000000 ) ) ,
} )
2017-09-27 00:50:41 +00:00
s . EqualError ( err , txqueue . ErrQueuedTxDiscarded . Error ( ) )
2017-05-16 12:09:52 +00:00
s . True ( reflect . DeepEqual ( txHashCheck , gethcommon . Hash { } ) , "transaction returned hash, while it shouldn't" )
}
// wait for transactions, and discard immediately
2017-09-04 12:56:58 +00:00
discardTxs := func ( txIDs [ ] common . QueuedTxID ) {
txIDs = append ( txIDs , "invalid-tx-id" )
2017-05-16 12:09:52 +00:00
// discard
2017-10-11 14:20:51 +00:00
discardResults := s . Backend . DiscardTransactions ( txIDs )
s . Len ( discardResults , 1 , "cannot discard txs: %v" , discardResults )
s . Error ( discardResults [ "invalid-tx-id" ] . Error , "transaction hash not found" , "cannot discard txs: %v" , discardResults )
2017-05-16 12:09:52 +00:00
// try completing discarded transaction
2017-10-11 14:20:51 +00:00
completeResults := s . Backend . CompleteTransactions ( txIDs , TestConfig . Account1 . Password )
s . Len ( completeResults , testTxCount + 1 , "unexpected number of errors (call to CompleteTransaction should not succeed)" )
2017-07-13 06:54:10 +00:00
2017-05-16 12:09:52 +00:00
for _ , txResult := range completeResults {
2017-10-11 14:20:51 +00:00
s . Error ( txResult . Error , "transaction hash not found" , "invalid error for %s" , txResult . Hash . Hex ( ) )
s . Equal ( "0x0000000000000000000000000000000000000000000000000000000000000000" , txResult . Hash . Hex ( ) , "invalid hash (expected zero): %s" , txResult . Hash . Hex ( ) )
2017-05-16 12:09:52 +00:00
}
time . Sleep ( 1 * time . Second ) // make sure that tx complete signal propagates
2017-09-04 12:56:58 +00:00
for _ , txID := range txIDs {
2017-10-11 14:20:51 +00:00
s . False (
s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) ,
2017-09-04 12:56:58 +00:00
"txqueue should not have test tx at this point (it should be discarded): %s" ,
txID ,
)
2017-05-16 12:09:52 +00:00
}
}
go func ( ) {
2017-09-04 12:56:58 +00:00
ids := make ( [ ] common . QueuedTxID , testTxCount )
2017-05-16 12:09:52 +00:00
for i := 0 ; i < testTxCount ; i ++ {
2017-09-04 12:56:58 +00:00
ids [ i ] = <- txIDs
2017-05-16 12:09:52 +00:00
}
2017-09-04 12:56:58 +00:00
discardTxs ( ids )
2017-05-16 12:09:52 +00:00
} ( )
// send multiple transactions
for i := 0 ; i < testTxCount ; i ++ {
go sendTx ( )
}
select {
case <- allTestTxDiscarded :
2017-08-04 16:14:17 +00:00
case <- time . After ( 1 * time . Minute ) :
2017-10-11 14:20:51 +00:00
s . FailNow ( "test timed out" )
2017-05-16 12:09:52 +00:00
}
2017-10-11 14:20:51 +00:00
s . Zero ( s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestNonExistentQueuedTransactions ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
backend := s . LightEthereumService ( ) . StatusBackend
2017-10-11 14:20:51 +00:00
s . NotNil ( backend )
2017-05-16 12:09:52 +00:00
// log into account from which transactions will be sent
2017-10-11 14:20:51 +00:00
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-05-16 12:09:52 +00:00
// replace transaction notification handler
2017-09-25 18:22:57 +00:00
signal . SetDefaultNodeNotificationHandler ( func ( string ) { } )
2017-05-16 12:09:52 +00:00
// try completing non-existing transaction
2017-10-11 14:20:51 +00:00
_ , err := s . Backend . CompleteTransaction ( "some-bad-transaction-id" , TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
s . Error ( err , "error expected and not received" )
2017-09-27 00:50:41 +00:00
s . EqualError ( err , txqueue . ErrQueuedTxIDNotFound . Error ( ) )
2017-05-16 12:09:52 +00:00
}
2017-10-11 14:20:51 +00:00
func ( s * TransactionsTestSuite ) TestEvictionOfQueuedTransactions ( ) {
2017-10-23 16:03:07 +00:00
s . StartTestBackend ( )
2017-05-16 12:09:52 +00:00
defer s . StopTestBackend ( )
backend := s . LightEthereumService ( ) . StatusBackend
2017-10-11 14:20:51 +00:00
s . NotNil ( backend )
2017-05-16 12:09:52 +00:00
// reset queue
2017-10-11 14:20:51 +00:00
s . Backend . TxQueueManager ( ) . TransactionQueue ( ) . Reset ( )
2017-05-16 12:09:52 +00:00
// log into account from which transactions will be sent
2017-10-11 14:20:51 +00:00
s . NoError ( s . Backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-05-16 12:09:52 +00:00
2017-10-11 14:20:51 +00:00
txQueue := s . Backend . TxQueueManager ( ) . TransactionQueue ( )
2017-05-16 12:09:52 +00:00
var i = 0
2017-09-27 00:50:41 +00:00
txIDs := [ txqueue . DefaultTxQueueCap + 5 + 10 ] common . QueuedTxID { }
2017-10-11 14:20:51 +00:00
s . Backend . TxQueueManager ( ) . SetTransactionQueueHandler ( func ( queuedTx * common . QueuedTx ) {
2017-05-25 13:14:52 +00:00
log . Info ( "tx enqueued" , "i" , i + 1 , "queue size" , txQueue . Count ( ) , "id" , queuedTx . ID )
2017-05-16 12:09:52 +00:00
txIDs [ i ] = queuedTx . ID
i ++
} )
s . Zero ( txQueue . Count ( ) , "transaction count should be zero" )
2017-10-20 09:06:22 +00:00
for j := 0 ; j < 10 ; j ++ {
go s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs { } ) // nolint: errcheck
2017-05-16 12:09:52 +00:00
}
2017-09-08 12:32:02 +00:00
time . Sleep ( 2 * time . Second ) // FIXME(tiabc): more reliable synchronization to ensure all transactions are enqueued
2017-05-16 12:09:52 +00:00
log . Info ( fmt . Sprintf ( "Number of transactions queued: %d. Queue size (shouldn't be more than %d): %d" ,
2017-09-27 00:50:41 +00:00
i , txqueue . DefaultTxQueueCap , txQueue . Count ( ) ) )
2017-05-16 12:09:52 +00:00
s . Equal ( 10 , txQueue . Count ( ) , "transaction count should be 10" )
2017-09-27 00:50:41 +00:00
for i := 0 ; i < txqueue . DefaultTxQueueCap + 5 ; i ++ { // stress test by hitting with lots of goroutines
2017-10-20 09:06:22 +00:00
go s . Backend . SendTransaction ( context . TODO ( ) , common . SendTxArgs { } ) // nolint: errcheck
2017-05-16 12:09:52 +00:00
}
2017-11-07 17:46:11 +00:00
time . Sleep ( 5 * time . Second )
2017-05-16 12:09:52 +00:00
2017-10-11 14:20:51 +00:00
s . True ( txQueue . Count ( ) <= txqueue . DefaultTxQueueCap , "transaction count should be %d (or %d): got %d" , txqueue . DefaultTxQueueCap , txqueue . DefaultTxQueueCap - 1 , txQueue . Count ( ) )
2017-05-16 12:09:52 +00:00
for _ , txID := range txIDs {
txQueue . Remove ( txID )
}
2017-10-11 14:20:51 +00:00
s . Zero ( txQueue . Count ( ) , "transaction count should be zero: %d" , txQueue . Count ( ) )
2017-05-16 12:09:52 +00:00
}