2017-05-16 12:09:52 +00:00
package api_test
import (
"encoding/json"
"fmt"
"math/big"
"reflect"
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/geth/common"
2017-08-10 13:35:58 +00:00
"github.com/status-im/status-go/geth/log"
2017-05-16 12:09:52 +00:00
"github.com/status-im/status-go/geth/node"
"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-05-16 12:09:52 +00:00
. "github.com/status-im/status-go/geth/testing"
)
2017-08-04 16:14:17 +00:00
// FIXME(tiabc): Sometimes it fails due to "no suitable peers found".
2017-05-16 12:09:52 +00:00
func ( s * BackendTestSuite ) TestSendContractTx ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second ) // allow to sync
2017-05-25 13:14:52 +00:00
sampleAddress , _ , _ , err := s . backend . AccountManager ( ) . CreateAccount ( TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
require . NoError ( err )
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-05-16 12:09:52 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
if envelope . Type == node . 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" )
2017-09-04 12:56:58 +00:00
txHash , err = s . backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . EqualError (
err ,
node . ErrNoAccountSelected . Error ( ) ,
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-05-25 13:14:52 +00:00
err = s . backend . AccountManager ( ) . SelectAccount ( sampleAddress , TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
s . NoError ( err )
2017-09-04 12:56:58 +00:00
txHash , err = s . backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . EqualError (
err ,
node . ErrInvalidCompleteTxSender . Error ( ) ,
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-05-25 13:14:52 +00:00
s . NoError ( s . backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-09-04 12:56:58 +00:00
txHash , err = s . backend . CompleteTransaction (
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 ` )
require . NoError ( err )
2017-09-04 12:56:58 +00:00
txHashCheck , err := s . backend . SendTransaction ( nil , 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
}
func ( s * BackendTestSuite ) TestSendEtherTx ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second ) // allow to sync
backend := s . LightEthereumService ( ) . StatusBackend
require . NotNil ( backend )
// create an account
2017-05-25 13:14:52 +00:00
sampleAddress , _ , _ , err := s . backend . AccountManager ( ) . CreateAccount ( TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
require . NoError ( err )
2017-09-15 10:35:31 +00:00
completeQueuedTransaction := make ( chan struct { } )
2017-05-16 12:09:52 +00:00
// 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-05-16 12:09:52 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err , fmt . Sprintf ( "cannot unmarshal JSON: %s" , jsonEvent ) )
if envelope . Type == node . 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" )
2017-09-04 12:56:58 +00:00
txHash , err = s . backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) ,
TestConfig . Account1 . Password ,
)
s . EqualError (
err ,
node . ErrNoAccountSelected . Error ( ) ,
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-05-25 13:14:52 +00:00
err = s . backend . AccountManager ( ) . SelectAccount ( sampleAddress , TestConfig . Account1 . Password )
2017-05-16 12:09:52 +00:00
s . NoError ( err )
2017-09-04 12:56:58 +00:00
txHash , err = s . backend . CompleteTransaction (
common . QueuedTxID ( event [ "id" ] . ( string ) ) , TestConfig . Account1 . Password )
s . EqualError (
err ,
node . ErrInvalidCompleteTxSender . Error ( ) ,
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-05-25 13:14:52 +00:00
s . NoError ( s . backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-09-04 12:56:58 +00:00
txHash , err = s . backend . CompleteTransaction (
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
}
} )
2017-09-04 12:56:58 +00:00
// this call blocks, up until Complete Transaction is called
txHashCheck , err := s . backend . SendTransaction ( nil , 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 ( 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 . backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
}
2017-09-19 11:19:18 +00:00
func ( s * BackendTestSuite ) TestSendEtherTxUpstream ( ) {
s . StartTestBackend ( params . RopstenNetworkID , WithUpstream ( "https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4" ) )
defer s . StopTestBackend ( )
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second ) // allow to sync
err := s . backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password )
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-09-19 11:19:18 +00:00
err := json . Unmarshal ( [ ] byte ( jsonEvent ) , & envelope )
s . NoError ( err , "cannot unmarshal JSON: %s" , jsonEvent )
if envelope . Type == node . EventTransactionQueued {
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction queued (will be completed shortly)" , "id" , event [ "id" ] . ( string ) )
txHash , err = s . backend . CompleteTransaction (
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.
txHashCheck , err := s . backend . SendTransaction ( nil , common . SendTxArgs {
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" )
s . Zero ( s . backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
}
2017-05-16 12:09:52 +00:00
func ( s * BackendTestSuite ) TestDoubleCompleteQueuedTransactions ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second ) // allow to sync
backend := s . LightEthereumService ( ) . StatusBackend
require . NotNil ( backend )
// log into account from which transactions will be sent
2017-05-25 13:14:52 +00:00
require . 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 ) )
if envelope . Type == node . EventTransactionQueued {
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)
_ , err = s . backend . CompleteTransaction ( txID , TestConfig . Account1 . Password + "wrong" )
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-09-04 12:56:58 +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 )
}
if envelope . Type == node . EventTransactionFailed {
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)
txHashCheck , err := s . backend . SendTransaction ( nil , 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-09-04 12:56:58 +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" )
}
func ( s * BackendTestSuite ) TestDiscardQueuedTransaction ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second ) // allow to sync
backend := s . LightEthereumService ( ) . StatusBackend
require . NotNil ( backend )
// reset queue
2017-09-04 12:56:58 +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-05-25 13:14:52 +00:00
require . 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 ) )
if envelope . Type == node . EventTransactionQueued {
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-09-04 12:56:58 +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
err := s . backend . DiscardTransaction ( txID )
s . NoError ( err , "cannot discard tx" )
// try completing discarded transaction
_ , err = s . backend . CompleteTransaction ( txID , TestConfig . Account1 . Password )
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-09-04 12:56:58 +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 )
}
if envelope . Type == node . EventTransactionFailed {
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction return event received" , "id" , event [ "id" ] . ( string ) )
receivedErrMessage := event [ "error_message" ] . ( string )
2017-09-04 12:56:58 +00:00
expectedErrMessage := node . 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
txHashCheck , err := s . backend . SendTransaction ( nil , 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-04 12:56:58 +00:00
s . EqualError ( err , node . 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-09-04 12:56:58 +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" )
}
func ( s * BackendTestSuite ) TestCompleteMultipleQueuedTransactions ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
2017-09-04 12:56:58 +00:00
// allow to sync
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second )
2017-05-16 12:09:52 +00:00
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-09-04 12:56:58 +00:00
err := s . backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password )
require . 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-05-16 12:09:52 +00:00
if envelope . Type == node . EventTransactionQueued {
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-09-04 12:56:58 +00:00
txHashCheck , err := s . backend . SendTransaction ( nil , 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" )
results := s . backend . CompleteTransactions ( txIDs , TestConfig . Account1 . Password )
2017-09-02 17:04:23 +00:00
require . Len ( results , testTxCount + 1 )
require . 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 {
require . False (
txResult . Error != nil && txID != "invalid-tx-id" ,
"invalid error for %s" , txID ,
)
require . 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-09-02 17:04:23 +00:00
require . False (
2017-09-04 12:56:58 +00:00
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-09-04 12:56:58 +00:00
require . Zero ( s . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "queue should be empty" )
2017-05-16 12:09:52 +00:00
}
func ( s * BackendTestSuite ) TestDiscardMultipleQueuedTransactions ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
time . Sleep ( TestConfig . Node . SyncSeconds * time . Second ) // allow to sync
backend := s . LightEthereumService ( ) . StatusBackend
require . NotNil ( backend )
// reset queue
2017-09-04 12:56:58 +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-05-25 13:14:52 +00:00
require . 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 )
if envelope . Type == node . EventTransactionQueued {
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-09-04 12:56:58 +00:00
s . True ( s . backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) ,
"txqueue should still have test tx" )
2017-05-16 12:09:52 +00:00
txIDs <- txID
}
if envelope . Type == node . EventTransactionFailed {
event := envelope . Event . ( map [ string ] interface { } )
log . Info ( "transaction return event received" , "id" , event [ "id" ] . ( string ) )
receivedErrMessage := event [ "error_message" ] . ( string )
2017-09-04 12:56:58 +00:00
expectedErrMessage := node . 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-09-04 12:56:58 +00:00
txHashCheck , err := s . backend . SendTransaction ( nil , 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-04 12:56:58 +00:00
s . EqualError ( err , node . 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-09-04 12:56:58 +00:00
discardResults := s . backend . DiscardTransactions ( txIDs )
2017-09-02 17:04:23 +00:00
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 )
2017-05-16 12:09:52 +00:00
// try completing discarded transaction
2017-09-04 12:56:58 +00:00
completeResults := s . backend . CompleteTransactions ( txIDs , TestConfig . Account1 . Password )
2017-09-02 17:04:23 +00:00
require . 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-09-02 17:04:23 +00:00
require . Error ( txResult . Error , "transaction hash not found" , "invalid error for %s" , txResult . Hash . Hex ( ) )
require . 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 {
require . False (
s . backend . TxQueueManager ( ) . TransactionQueue ( ) . Has ( txID ) ,
"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-09-15 10:35:31 +00:00
require . FailNow ( "test timed out" )
2017-05-16 12:09:52 +00:00
}
2017-09-04 12:56:58 +00:00
require . Zero ( s . backend . TxQueueManager ( ) . TransactionQueue ( ) . Count ( ) , "tx queue must be empty at this point" )
2017-05-16 12:09:52 +00:00
}
func ( s * BackendTestSuite ) TestNonExistentQueuedTransactions ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
backend := s . LightEthereumService ( ) . StatusBackend
require . NotNil ( backend )
// log into account from which transactions will be sent
2017-05-25 13:14:52 +00:00
require . 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
_ , err := s . backend . CompleteTransaction ( "some-bad-transaction-id" , TestConfig . Account1 . Password )
s . Error ( err , "error expected and not received" )
2017-09-04 12:56:58 +00:00
s . EqualError ( err , node . ErrQueuedTxIDNotFound . Error ( ) )
2017-05-16 12:09:52 +00:00
}
func ( s * BackendTestSuite ) TestEvictionOfQueuedTransactions ( ) {
require := s . Require ( )
require . NotNil ( s . backend )
s . StartTestBackend ( params . RopstenNetworkID )
defer s . StopTestBackend ( )
backend := s . LightEthereumService ( ) . StatusBackend
require . NotNil ( backend )
// reset queue
2017-09-04 12:56:58 +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-05-25 13:14:52 +00:00
require . NoError ( s . backend . AccountManager ( ) . SelectAccount ( TestConfig . Account1 . Address , TestConfig . Account1 . Password ) )
2017-05-16 12:09:52 +00:00
2017-09-04 12:56:58 +00:00
txQueue := s . backend . TxQueueManager ( ) . TransactionQueue ( )
2017-05-16 12:09:52 +00:00
var i = 0
2017-09-04 12:56:58 +00:00
txIDs := [ node . DefaultTxQueueCap + 5 + 10 ] common . QueuedTxID { }
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" )
for i := 0 ; i < 10 ; i ++ {
2017-09-04 12:56:58 +00:00
go s . backend . SendTransaction ( nil , 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-04 12:56:58 +00:00
i , node . DefaultTxQueueCap , txQueue . Count ( ) ) )
2017-05-16 12:09:52 +00:00
s . Equal ( 10 , txQueue . Count ( ) , "transaction count should be 10" )
2017-09-04 12:56:58 +00:00
for i := 0 ; i < node . DefaultTxQueueCap + 5 ; i ++ { // stress test by hitting with lots of goroutines
go s . backend . SendTransaction ( nil , common . SendTxArgs { } ) // nolint: errcheck
2017-05-16 12:09:52 +00:00
}
time . Sleep ( 3 * time . Second )
2017-09-04 12:56:58 +00:00
require . True ( txQueue . Count ( ) <= node . DefaultTxQueueCap , "transaction count should be %d (or %d): got %d" , node . DefaultTxQueueCap , node . DefaultTxQueueCap - 1 , txQueue . Count ( ) )
2017-05-16 12:09:52 +00:00
for _ , txID := range txIDs {
txQueue . Remove ( txID )
}
2017-09-04 12:56:58 +00:00
require . Zero ( txQueue . Count ( ) , "transaction count should be zero: %d" , txQueue . Count ( ) )
2017-05-16 12:09:52 +00:00
}