Merge pull request #60 from farazdagi/feature/tx-multi-actions
DiscardTransactions() + CompleteTransactions()
This commit is contained in:
commit
bc8ec4500e
2
Makefile
2
Makefile
|
@ -59,6 +59,8 @@ test-all:
|
||||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./extkeys
|
build/env.sh go test -coverprofile=coverage.out -covermode=set ./extkeys
|
||||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||||
|
build/env.sh go test -coverprofile=coverage.out -covermode=set ./cmd/status
|
||||||
|
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||||
@build/env.sh go tool cover -html=coverage-all.out -o coverage.html
|
@build/env.sh go tool cover -html=coverage-all.out -o coverage.html
|
||||||
@build/env.sh go tool cover -func=coverage-all.out
|
@build/env.sh go tool cover -func=coverage-all.out
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,7 @@ func CompleteTransaction(id, password *C.char) *C.char {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := geth.CompleteTransactionResult{
|
out := geth.CompleteTransactionResult{
|
||||||
|
Id: C.GoString(id),
|
||||||
Hash: txHash.Hex(),
|
Hash: txHash.Hex(),
|
||||||
Error: errString,
|
Error: errString,
|
||||||
}
|
}
|
||||||
|
@ -158,6 +159,27 @@ func CompleteTransaction(id, password *C.char) *C.char {
|
||||||
return C.CString(string(outBytes))
|
return C.CString(string(outBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export CompleteTransactions
|
||||||
|
func CompleteTransactions(ids, password *C.char) *C.char {
|
||||||
|
out := geth.CompleteTransactionsResult{}
|
||||||
|
out.Results = make(map[string]geth.CompleteTransactionResult)
|
||||||
|
|
||||||
|
results := geth.CompleteTransactions(C.GoString(ids), C.GoString(password))
|
||||||
|
for txId, result := range results {
|
||||||
|
txResult := geth.CompleteTransactionResult{
|
||||||
|
Id: txId,
|
||||||
|
Hash: result.Hash.Hex(),
|
||||||
|
}
|
||||||
|
if result.Error != nil {
|
||||||
|
txResult.Error = result.Error.Error()
|
||||||
|
}
|
||||||
|
out.Results[txId] = txResult
|
||||||
|
}
|
||||||
|
outBytes, _ := json.Marshal(&out)
|
||||||
|
|
||||||
|
return C.CString(string(outBytes))
|
||||||
|
}
|
||||||
|
|
||||||
//export DiscardTransaction
|
//export DiscardTransaction
|
||||||
func DiscardTransaction(id *C.char) *C.char {
|
func DiscardTransaction(id *C.char) *C.char {
|
||||||
err := geth.DiscardTransaction(C.GoString(id))
|
err := geth.DiscardTransaction(C.GoString(id))
|
||||||
|
@ -177,6 +199,26 @@ func DiscardTransaction(id *C.char) *C.char {
|
||||||
return C.CString(string(outBytes))
|
return C.CString(string(outBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export DiscardTransactions
|
||||||
|
func DiscardTransactions(ids *C.char) *C.char {
|
||||||
|
out := geth.DiscardTransactionsResult{}
|
||||||
|
out.Results = make(map[string]geth.DiscardTransactionResult)
|
||||||
|
|
||||||
|
results := geth.DiscardTransactions(C.GoString(ids))
|
||||||
|
for txId, result := range results {
|
||||||
|
txResult := geth.DiscardTransactionResult{
|
||||||
|
Id: txId,
|
||||||
|
}
|
||||||
|
if result.Error != nil {
|
||||||
|
txResult.Error = result.Error.Error()
|
||||||
|
}
|
||||||
|
out.Results[txId] = txResult
|
||||||
|
}
|
||||||
|
outBytes, _ := json.Marshal(&out)
|
||||||
|
|
||||||
|
return C.CString(string(outBytes))
|
||||||
|
}
|
||||||
|
|
||||||
//export StartNode
|
//export StartNode
|
||||||
func StartNode(datadir *C.char) *C.char {
|
func StartNode(datadir *C.char) *C.char {
|
||||||
// This starts a geth node with the given datadir
|
// This starts a geth node with the given datadir
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// the actual test functions are in non-_test.go files (so that they can use cgo i.e. import "C")
|
||||||
|
// the only intent of these wrappers is for gotest can find what tests are exposed.
|
||||||
|
func TestExportedAPI(t *testing.T) {
|
||||||
|
allTestsDone := make(chan struct{}, 1)
|
||||||
|
go testExportedAPI(t, allTestsDone)
|
||||||
|
|
||||||
|
<- allTestsDone
|
||||||
|
}
|
|
@ -0,0 +1,384 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/les/status"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/status-im/status-go/geth"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testDataDir = "../../.ethereumtest"
|
||||||
|
testNodeSyncSeconds = 120
|
||||||
|
testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4"
|
||||||
|
testAddressPassword = "asdf"
|
||||||
|
newAccountPassword = "badpassword"
|
||||||
|
testAddress1 = "0xf82da7547534045b4e00442bc89e16186cf8c272"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testExportedAPI(t *testing.T, done chan struct{}) {
|
||||||
|
<-startTestNode(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fn func(t *testing.T) bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"test complete multiple queued transactions",
|
||||||
|
testCompleteMultipleQueuedTransactions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"test discard multiple queued transactions",
|
||||||
|
testDiscardMultipleQueuedTransactions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
if ok := test.fn(t); !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCompleteMultipleQueuedTransactions(t *testing.T) bool {
|
||||||
|
// obtain reference to status backend
|
||||||
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
backend := lightEthereum.StatusBackend
|
||||||
|
|
||||||
|
// reset queue
|
||||||
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
testTxCount := 3
|
||||||
|
txIds := make(chan string, testTxCount)
|
||||||
|
allTestTxCompleted := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
// replace transaction notification handler
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
var txId string
|
||||||
|
var envelope geth.GethEvent
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
txId = event["id"].(string)
|
||||||
|
t.Logf("transaction queued (will be completed in a single call, once aggregated): {id: %s}\n", txId)
|
||||||
|
|
||||||
|
txIds <- txId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
|
||||||
|
sendTx := func() {
|
||||||
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
|
From: geth.FromAddress(testAddress),
|
||||||
|
To: geth.ToAddress(testAddress1),
|
||||||
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error thrown: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||||
|
t.Error("transaction returned empty hash")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for transactions, and complete them in a single call
|
||||||
|
completeTxs := func(txIdStrings string) {
|
||||||
|
var parsedIds []string
|
||||||
|
json.Unmarshal([]byte(txIdStrings), &parsedIds)
|
||||||
|
|
||||||
|
parsedIds = append(parsedIds, "invalid-tx-id")
|
||||||
|
updatedTxIdStrings, _ := json.Marshal(parsedIds)
|
||||||
|
|
||||||
|
// complete
|
||||||
|
resultsString := CompleteTransactions(C.CString(string(updatedTxIdStrings)), C.CString(testAddressPassword))
|
||||||
|
resultsStruct := geth.CompleteTransactionsResult{}
|
||||||
|
json.Unmarshal([]byte(C.GoString(resultsString)), &resultsStruct)
|
||||||
|
results := resultsStruct.Results
|
||||||
|
|
||||||
|
if len(results) != (testTxCount+1) || results["invalid-tx-id"].Error != "transaction hash not found" {
|
||||||
|
t.Errorf("cannot complete txs: %v", results)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for txId, txResult := range results {
|
||||||
|
if txId != txResult.Id {
|
||||||
|
t.Errorf("tx id not set in result: expected id is %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txResult.Error != "" && txId != "invalid-tx-id" {
|
||||||
|
t.Errorf("invalid error for %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txResult.Hash == "0x0000000000000000000000000000000000000000000000000000000000000000" && txId != "invalid-tx-id" {
|
||||||
|
t.Errorf("invalid hash (expected non empty hash): %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if txResult.Hash != "0x0000000000000000000000000000000000000000000000000000000000000000" {
|
||||||
|
t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txResult.Hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||||
|
for _, txId := range parsedIds {
|
||||||
|
if backend.TransactionQueue().Has(status.QueuedTxId(txId)) {
|
||||||
|
t.Errorf("txqueue should not have test tx at this point (it should be completed): %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var txIdStrings []string
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
txIdStrings = append(txIdStrings, <-txIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
txIdJSON, _ := json.Marshal(txIdStrings)
|
||||||
|
completeTxs(string(txIdJSON))
|
||||||
|
allTestTxCompleted <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// send multiple transactions
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
go sendTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-allTestTxCompleted:
|
||||||
|
// pass
|
||||||
|
case <-time.After(20 * time.Second):
|
||||||
|
t.Error("test timed out")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend.TransactionQueue().Count() != 0 {
|
||||||
|
t.Error("tx queue must be empty at this point")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDiscardMultipleQueuedTransactions(t *testing.T) bool {
|
||||||
|
// obtain reference to status backend
|
||||||
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
backend := lightEthereum.StatusBackend
|
||||||
|
|
||||||
|
// reset queue
|
||||||
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
testTxCount := 3
|
||||||
|
txIds := make(chan string, testTxCount)
|
||||||
|
allTestTxDiscarded := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
// replace transaction notification handler
|
||||||
|
txFailedEventCallCount := 0
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
var txId string
|
||||||
|
var envelope geth.GethEvent
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
txId = event["id"].(string)
|
||||||
|
t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txId)
|
||||||
|
|
||||||
|
if !backend.TransactionQueue().Has(status.QueuedTxId(txId)) {
|
||||||
|
t.Errorf("txqueue should still have test tx: %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txIds <- txId
|
||||||
|
}
|
||||||
|
|
||||||
|
if envelope.Type == geth.EventTransactionFailed {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
t.Logf("transaction return event received: {id: %s}\n", event["id"].(string))
|
||||||
|
|
||||||
|
receivedErrMessage := event["error_message"].(string)
|
||||||
|
expectedErrMessage := status.ErrQueuedTxDiscarded.Error()
|
||||||
|
if receivedErrMessage != expectedErrMessage {
|
||||||
|
t.Errorf("unexpected error message received: got %v", receivedErrMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedErrCode := event["error_code"].(string)
|
||||||
|
if receivedErrCode != geth.SendTransactionDiscardedErrorCode {
|
||||||
|
t.Errorf("unexpected error code received: got %v", receivedErrCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txFailedEventCallCount++
|
||||||
|
if txFailedEventCallCount == testTxCount {
|
||||||
|
allTestTxDiscarded <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
|
||||||
|
sendTx := func() {
|
||||||
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
|
From: geth.FromAddress(testAddress),
|
||||||
|
To: geth.ToAddress(testAddress1),
|
||||||
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
|
})
|
||||||
|
if err != status.ErrQueuedTxDiscarded {
|
||||||
|
t.Errorf("expected error not thrown: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||||
|
t.Error("transaction returned hash, while it shouldn't")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for transactions, and discard immediately
|
||||||
|
discardTxs := func(txIdStrings string) {
|
||||||
|
var parsedIds []string
|
||||||
|
json.Unmarshal([]byte(txIdStrings), &parsedIds)
|
||||||
|
|
||||||
|
parsedIds = append(parsedIds, "invalid-tx-id")
|
||||||
|
updatedTxIdStrings, _ := json.Marshal(parsedIds)
|
||||||
|
|
||||||
|
// discard
|
||||||
|
discardResultsString := DiscardTransactions(C.CString(string(updatedTxIdStrings)))
|
||||||
|
discardResultsStruct := geth.DiscardTransactionsResult{}
|
||||||
|
json.Unmarshal([]byte(C.GoString(discardResultsString)), &discardResultsStruct)
|
||||||
|
discardResults := discardResultsStruct.Results
|
||||||
|
|
||||||
|
if len(discardResults) != 1 || discardResults["invalid-tx-id"].Error != "transaction hash not found" {
|
||||||
|
t.Errorf("cannot discard txs: %v", discardResults)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// try completing discarded transaction
|
||||||
|
completeResultsString := CompleteTransactions(C.CString(string(updatedTxIdStrings)), C.CString(testAddressPassword))
|
||||||
|
completeResultsStruct := geth.CompleteTransactionsResult{}
|
||||||
|
json.Unmarshal([]byte(C.GoString(completeResultsString)), &completeResultsStruct)
|
||||||
|
completeResults := completeResultsStruct.Results
|
||||||
|
|
||||||
|
if len(completeResults) != (testTxCount + 1) {
|
||||||
|
t.Error("unexpected number of errors (call to CompleteTransaction should not succeed)")
|
||||||
|
}
|
||||||
|
for txId, txResult := range completeResults {
|
||||||
|
if txId != txResult.Id {
|
||||||
|
t.Errorf("tx id not set in result: expected id is %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txResult.Error != "transaction hash not found" {
|
||||||
|
t.Errorf("invalid error for %s", txResult.Hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txResult.Hash != "0x0000000000000000000000000000000000000000000000000000000000000000" {
|
||||||
|
t.Errorf("invalid hash (expected zero): %s", txResult.Hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||||
|
for _, txId := range parsedIds {
|
||||||
|
if backend.TransactionQueue().Has(status.QueuedTxId(txId)) {
|
||||||
|
t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var txIdStrings []string
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
txIdStrings = append(txIdStrings, <-txIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
txIdJSON, _ := json.Marshal(txIdStrings)
|
||||||
|
discardTxs(string(txIdJSON))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// send multiple transactions
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
go sendTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-allTestTxDiscarded:
|
||||||
|
// pass
|
||||||
|
case <-time.After(20 * time.Second):
|
||||||
|
t.Error("test timed out")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend.TransactionQueue().Count() != 0 {
|
||||||
|
t.Error("tx queue must be empty at this point")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func startTestNode(t *testing.T) <-chan struct{} {
|
||||||
|
syncRequired := false
|
||||||
|
if _, err := os.Stat(filepath.Join(testDataDir, "testnet")); os.IsNotExist(err) {
|
||||||
|
syncRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForNodeStart := make(chan struct{}, 1)
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
t.Log(jsonEvent)
|
||||||
|
var envelope geth.GethEvent
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventNodeStarted {
|
||||||
|
if syncRequired {
|
||||||
|
t.Logf("Sync is required, it will take %d seconds", testNodeSyncSeconds)
|
||||||
|
time.Sleep(testNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done
|
||||||
|
} else {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we can proceed with tests
|
||||||
|
waitForNodeStart <- struct{}{}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
response := StartNode(C.CString(testDataDir))
|
||||||
|
err := geth.JSONError{}
|
||||||
|
|
||||||
|
json.Unmarshal([]byte(C.GoString(response)), &err)
|
||||||
|
if err.Error != "" {
|
||||||
|
t.Error("cannot start node")
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitForNodeStart
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ const (
|
||||||
testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4"
|
testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4"
|
||||||
testAddressPassword = "asdf"
|
testAddressPassword = "asdf"
|
||||||
newAccountPassword = "badpassword"
|
newAccountPassword = "badpassword"
|
||||||
|
testAddress1 = "0xf82da7547534045b4e00442bc89e16186cf8c272"
|
||||||
|
|
||||||
whisperMessage1 = "test message 1 (K1 -> K1)"
|
whisperMessage1 = "test message 1 (K1 -> K1)"
|
||||||
whisperMessage2 = "test message 2 (K1 -> '')"
|
whisperMessage2 = "test message 2 (K1 -> '')"
|
||||||
|
|
|
@ -102,6 +102,28 @@ func CompleteTransaction(id, password string) (common.Hash, error) {
|
||||||
return backend.CompleteQueuedTransaction(status.QueuedTxId(id), password)
|
return backend.CompleteQueuedTransaction(status.QueuedTxId(id), password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CompleteTransactions(ids, password string) map[string]RawCompleteTransactionResult {
|
||||||
|
results := make(map[string]RawCompleteTransactionResult)
|
||||||
|
|
||||||
|
parsedIds, err := parseJSONArray(ids)
|
||||||
|
if err != nil {
|
||||||
|
results["none"] = RawCompleteTransactionResult{
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, txId := range parsedIds {
|
||||||
|
txHash, txErr := CompleteTransaction(txId, password)
|
||||||
|
results[txId] = RawCompleteTransactionResult{
|
||||||
|
Hash: txHash,
|
||||||
|
Error: txErr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
func DiscardTransaction(id string) error {
|
func DiscardTransaction(id string) error {
|
||||||
lightEthereum, err := GetNodeManager().LightEthereumService()
|
lightEthereum, err := GetNodeManager().LightEthereumService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -113,6 +135,30 @@ func DiscardTransaction(id string) error {
|
||||||
return backend.DiscardQueuedTransaction(status.QueuedTxId(id))
|
return backend.DiscardQueuedTransaction(status.QueuedTxId(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DiscardTransactions(ids string) map[string]RawDiscardTransactionResult {
|
||||||
|
var parsedIds []string
|
||||||
|
results := make(map[string]RawDiscardTransactionResult)
|
||||||
|
|
||||||
|
parsedIds, err := parseJSONArray(ids)
|
||||||
|
if err != nil {
|
||||||
|
results["none"] = RawDiscardTransactionResult{
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, txId := range parsedIds {
|
||||||
|
err := DiscardTransaction(txId)
|
||||||
|
if err != nil {
|
||||||
|
results[txId] = RawDiscardTransactionResult{
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
func messageIdFromContext(ctx context.Context) string {
|
func messageIdFromContext(ctx context.Context) string {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return ""
|
return ""
|
||||||
|
@ -232,3 +278,13 @@ func sendTxArgsFromRPCCall(req RPCCall) status.SendTxArgs {
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseJSONArray(items string) ([]string, error) {
|
||||||
|
var parsedItems []string
|
||||||
|
err := json.Unmarshal([]byte(items), &parsedItems)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedItems, nil
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/les/status"
|
"github.com/ethereum/go-ethereum/les/status"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
@ -21,19 +20,6 @@ func TestQueuedTransactions(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an account
|
|
||||||
address, _, _, err := geth.CreateAccount(newAccountPassword)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not create account: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// obtain reference to status backend
|
// obtain reference to status backend
|
||||||
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -69,23 +55,10 @@ func TestQueuedTransactions(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// send from the same test account (which is guaranteed to have ether)
|
|
||||||
from, err := utils.MakeAddress(accountManager, testAddress)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not retrieve account from address: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
to, err := utils.MakeAddress(accountManager, address)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not retrieve account from address: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// this call blocks, up until Complete Transaction is called
|
// this call blocks, up until Complete Transaction is called
|
||||||
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
From: from.Address,
|
From: geth.FromAddress(testAddress),
|
||||||
To: &to.Address,
|
To: geth.ToAddress(testAddress1),
|
||||||
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,19 +90,6 @@ func TestDoubleCompleteQueuedTransactions(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an account
|
|
||||||
address, _, _, err := geth.CreateAccount(newAccountPassword)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not create account: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// obtain reference to status backend
|
// obtain reference to status backend
|
||||||
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -209,23 +169,10 @@ func TestDoubleCompleteQueuedTransactions(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// send from the same test account (which is guaranteed to have ether)
|
|
||||||
from, err := utils.MakeAddress(accountManager, testAddress)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not retrieve account from address: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
to, err := utils.MakeAddress(accountManager, address)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not retrieve account from address: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 := backend.SendTransaction(nil, status.SendTxArgs{
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
From: from.Address,
|
From: geth.FromAddress(testAddress),
|
||||||
To: &to.Address,
|
To: geth.ToAddress(testAddress1),
|
||||||
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -264,19 +211,6 @@ func TestDiscardQueuedTransactions(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an account
|
|
||||||
address, _, _, err := geth.CreateAccount(newAccountPassword)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not create account: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// obtain reference to status backend
|
// obtain reference to status backend
|
||||||
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -355,27 +289,14 @@ func TestDiscardQueuedTransactions(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// send from the same test account (which is guaranteed to have ether)
|
|
||||||
from, err := utils.MakeAddress(accountManager, testAddress)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not retrieve account from address: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
to, err := utils.MakeAddress(accountManager, address)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("could not retrieve account from address: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// this call blocks, and should return when DiscardQueuedTransaction() is called
|
// this call blocks, and should return when DiscardQueuedTransaction() is called
|
||||||
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
From: from.Address,
|
From: geth.FromAddress(testAddress),
|
||||||
To: &to.Address,
|
To: geth.ToAddress(testAddress1),
|
||||||
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
})
|
})
|
||||||
if err != status.ErrQueuedTxDiscarded {
|
if err != status.ErrQueuedTxDiscarded {
|
||||||
t.Errorf("expeced error not thrown: %v", err)
|
t.Errorf("expected error not thrown: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,6 +319,286 @@ func TestDiscardQueuedTransactions(t *testing.T) {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompleteMultipleQueuedTransactions(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain reference to status backend
|
||||||
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backend := lightEthereum.StatusBackend
|
||||||
|
|
||||||
|
// reset queue
|
||||||
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
testTxCount := 3
|
||||||
|
txIds := make(chan string, testTxCount)
|
||||||
|
allTestTxCompleted := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
// replace transaction notification handler
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
var txId string
|
||||||
|
var envelope geth.GethEvent
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
txId = event["id"].(string)
|
||||||
|
t.Logf("transaction queued (will be completed in a single call, once aggregated): {id: %s}\n", txId)
|
||||||
|
|
||||||
|
txIds <- txId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
|
||||||
|
sendTx := func() {
|
||||||
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
|
From: geth.FromAddress(testAddress),
|
||||||
|
To: geth.ToAddress(testAddress1),
|
||||||
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error thrown: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||||
|
t.Error("transaction returned empty hash")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for transactions, and complete them in a single call
|
||||||
|
completeTxs := func(txIdStrings string) {
|
||||||
|
var parsedIds []string
|
||||||
|
json.Unmarshal([]byte(txIdStrings), &parsedIds)
|
||||||
|
|
||||||
|
parsedIds = append(parsedIds, "invalid-tx-id")
|
||||||
|
updatedTxIdStrings, _ := json.Marshal(parsedIds)
|
||||||
|
|
||||||
|
// complete
|
||||||
|
results := geth.CompleteTransactions(string(updatedTxIdStrings), testAddressPassword)
|
||||||
|
if len(results) != (testTxCount+1) || results["invalid-tx-id"].Error.Error() != "transaction hash not found" {
|
||||||
|
t.Errorf("cannot complete txs: %v", results)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for txId, txResult := range results {
|
||||||
|
if txResult.Error != nil && txId != "invalid-tx-id" {
|
||||||
|
t.Errorf("invalid error for %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txResult.Hash.Hex() == "0x0000000000000000000000000000000000000000000000000000000000000000" && txId != "invalid-tx-id" {
|
||||||
|
t.Errorf("invalid hash (expected non empty hash): %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if txResult.Hash.Hex() != "0x0000000000000000000000000000000000000000000000000000000000000000" {
|
||||||
|
t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txResult.Hash.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||||
|
for _, txId := range parsedIds {
|
||||||
|
if backend.TransactionQueue().Has(status.QueuedTxId(txId)) {
|
||||||
|
t.Errorf("txqueue should not have test tx at this point (it should be completed): %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var txIdStrings []string
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
txIdStrings = append(txIdStrings, <-txIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
txIdJSON, _ := json.Marshal(txIdStrings)
|
||||||
|
completeTxs(string(txIdJSON))
|
||||||
|
allTestTxCompleted <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// send multiple transactions
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
go sendTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-allTestTxCompleted:
|
||||||
|
// pass
|
||||||
|
case <-time.After(20 * time.Second):
|
||||||
|
t.Error("test timed out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend.TransactionQueue().Count() != 0 {
|
||||||
|
t.Error("tx queue must be empty at this point")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiscardMultipleQueuedTransactions(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain reference to status backend
|
||||||
|
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backend := lightEthereum.StatusBackend
|
||||||
|
|
||||||
|
// reset queue
|
||||||
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
testTxCount := 3
|
||||||
|
txIds := make(chan string, testTxCount)
|
||||||
|
allTestTxDiscarded := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
// replace transaction notification handler
|
||||||
|
txFailedEventCallCount := 0
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
var txId string
|
||||||
|
var envelope geth.GethEvent
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
txId = event["id"].(string)
|
||||||
|
t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txId)
|
||||||
|
|
||||||
|
if !backend.TransactionQueue().Has(status.QueuedTxId(txId)) {
|
||||||
|
t.Errorf("txqueue should still have test tx: %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txIds <- txId
|
||||||
|
}
|
||||||
|
|
||||||
|
if envelope.Type == geth.EventTransactionFailed {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
t.Logf("transaction return event received: {id: %s}\n", event["id"].(string))
|
||||||
|
|
||||||
|
receivedErrMessage := event["error_message"].(string)
|
||||||
|
expectedErrMessage := status.ErrQueuedTxDiscarded.Error()
|
||||||
|
if receivedErrMessage != expectedErrMessage {
|
||||||
|
t.Errorf("unexpected error message received: got %v", receivedErrMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedErrCode := event["error_code"].(string)
|
||||||
|
if receivedErrCode != geth.SendTransactionDiscardedErrorCode {
|
||||||
|
t.Errorf("unexpected error code received: got %v", receivedErrCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txFailedEventCallCount++
|
||||||
|
if txFailedEventCallCount == testTxCount {
|
||||||
|
allTestTxDiscarded <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
|
||||||
|
sendTx := func() {
|
||||||
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
|
From: geth.FromAddress(testAddress),
|
||||||
|
To: geth.ToAddress(testAddress1),
|
||||||
|
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||||
|
})
|
||||||
|
if err != status.ErrQueuedTxDiscarded {
|
||||||
|
t.Errorf("expected error not thrown: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||||
|
t.Error("transaction returned hash, while it shouldn't")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for transactions, and discard immediately
|
||||||
|
discardTxs := func(txIdStrings string) {
|
||||||
|
var parsedIds []string
|
||||||
|
json.Unmarshal([]byte(txIdStrings), &parsedIds)
|
||||||
|
|
||||||
|
parsedIds = append(parsedIds, "invalid-tx-id")
|
||||||
|
updatedTxIdStrings, _ := json.Marshal(parsedIds)
|
||||||
|
|
||||||
|
// discard
|
||||||
|
discardResults := geth.DiscardTransactions(string(updatedTxIdStrings))
|
||||||
|
if len(discardResults) != 1 || discardResults["invalid-tx-id"].Error.Error() != "transaction hash not found" {
|
||||||
|
t.Errorf("cannot discard txs: %v", discardResults)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// try completing discarded transaction
|
||||||
|
completeResults := geth.CompleteTransactions(string(updatedTxIdStrings), testAddressPassword)
|
||||||
|
if len(completeResults) != (testTxCount + 1) {
|
||||||
|
t.Error("unexpected number of errors (call to CompleteTransaction should not succeed)")
|
||||||
|
}
|
||||||
|
for _, txResult := range completeResults {
|
||||||
|
if txResult.Error.Error() != "transaction hash not found" {
|
||||||
|
t.Errorf("invalid error for %s", txResult.Hash.Hex())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txResult.Hash.Hex() != "0x0000000000000000000000000000000000000000000000000000000000000000" {
|
||||||
|
t.Errorf("invalid hash (expected zero): %s", txResult.Hash.Hex())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||||
|
for _, txId := range parsedIds {
|
||||||
|
if backend.TransactionQueue().Has(status.QueuedTxId(txId)) {
|
||||||
|
t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var txIdStrings []string
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
txIdStrings = append(txIdStrings, <-txIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
txIdJSON, _ := json.Marshal(txIdStrings)
|
||||||
|
discardTxs(string(txIdJSON))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// send multiple transactions
|
||||||
|
for i := 0; i < testTxCount; i++ {
|
||||||
|
go sendTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-allTestTxDiscarded:
|
||||||
|
// pass
|
||||||
|
case <-time.After(20 * time.Second):
|
||||||
|
t.Error("test timed out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend.TransactionQueue().Count() != 0 {
|
||||||
|
t.Error("tx queue must be empty at this point")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNonExistentQueuedTransactions(t *testing.T) {
|
func TestNonExistentQueuedTransactions(t *testing.T) {
|
||||||
err := geth.PrepareTestNode()
|
err := geth.PrepareTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package geth
|
package geth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/les/status"
|
"github.com/ethereum/go-ethereum/les/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,15 +50,33 @@ type ReturnSendTransactionEvent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompleteTransactionResult struct {
|
type CompleteTransactionResult struct {
|
||||||
|
Id string `json:"id"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RawCompleteTransactionResult struct {
|
||||||
|
Hash common.Hash
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompleteTransactionsResult struct {
|
||||||
|
Results map[string]CompleteTransactionResult `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawDiscardTransactionResult struct {
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
type DiscardTransactionResult struct {
|
type DiscardTransactionResult struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DiscardTransactionsResult struct {
|
||||||
|
Results map[string]DiscardTransactionResult `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
type GethEvent struct {
|
type GethEvent struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Event interface{} `json:"event"`
|
Event interface{} `json:"event"`
|
||||||
|
|
|
@ -15,6 +15,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
)
|
)
|
||||||
|
@ -23,7 +25,7 @@ var muPrepareTestNode sync.Mutex
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TestDataDir = "../.ethereumtest"
|
TestDataDir = "../.ethereumtest"
|
||||||
TestNodeSyncSeconds = 60
|
TestNodeSyncSeconds = 120
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeNotificationHandler func(jsonEvent string)
|
type NodeNotificationHandler func(jsonEvent string)
|
||||||
|
@ -177,3 +179,31 @@ func PanicAfter(waitSeconds time.Duration, abort chan struct{}, desc string) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromAddress(accountAddress string) common.Address {
|
||||||
|
accountManager, err := GetNodeManager().AccountManager()
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
|
from, err := utils.MakeAddress(accountManager, accountAddress)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return from.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToAddress(accountAddress string) *common.Address {
|
||||||
|
accountManager, err := GetNodeManager().AccountManager()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
to, err := utils.MakeAddress(accountManager, accountAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &to.Address
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue