Merge pull request #175 from farazdagi/feature/expose-in-proc-rpc-server
Feature: expose in proc RPC server
This commit is contained in:
commit
d1d62686d9
|
@ -32,13 +32,26 @@ func StartNode(configJSON *C.char) *C.char {
|
||||||
return makeJSONResponse(err)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = statusAPI.StartNodeNonBlocking(config)
|
_, err = statusAPI.StartNodeAsync(config)
|
||||||
return makeJSONResponse(err)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export StopNode
|
//export StopNode
|
||||||
func StopNode() *C.char {
|
func StopNode() *C.char {
|
||||||
return makeJSONResponse(statusAPI.StopNode())
|
_, err := statusAPI.StopNodeAsync()
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export ResetChainData
|
||||||
|
func ResetChainData() *C.char {
|
||||||
|
_, err := statusAPI.ResetChainDataAsync()
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export CallRPC
|
||||||
|
func CallRPC(inputJSON *C.char) *C.char {
|
||||||
|
outputJSON := statusAPI.CallRPC(C.GoString(inputJSON))
|
||||||
|
return C.CString(outputJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export ResumeNode
|
//export ResumeNode
|
||||||
|
@ -47,11 +60,6 @@ func ResumeNode() *C.char {
|
||||||
return makeJSONResponse(err)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export ResetChainData
|
|
||||||
func ResetChainData() *C.char {
|
|
||||||
return makeJSONResponse(statusAPI.ResetChainData())
|
|
||||||
}
|
|
||||||
|
|
||||||
//export StopNodeRPCServer
|
//export StopNodeRPCServer
|
||||||
func StopNodeRPCServer() *C.char {
|
func StopNodeRPCServer() *C.char {
|
||||||
err := fmt.Errorf("%v: %v", common.ErrDeprecatedMethod.Error(), "StopNodeRPCServer")
|
err := fmt.Errorf("%v: %v", common.ErrDeprecatedMethod.Error(), "StopNodeRPCServer")
|
||||||
|
|
|
@ -55,6 +55,10 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
|
||||||
"stop/resume node",
|
"stop/resume node",
|
||||||
testStopResumeNode,
|
testStopResumeNode,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"call RPC on in-proc handler",
|
||||||
|
testCallRPC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"create main and child accounts",
|
"create main and child accounts",
|
||||||
testCreateChildAccount,
|
testCreateChildAccount,
|
||||||
|
@ -335,7 +339,6 @@ func testStopResumeNode(t *testing.T) bool {
|
||||||
response := common.APIResponse{}
|
response := common.APIResponse{}
|
||||||
rawResponse = StartNode(C.CString(nodeConfigJSON))
|
rawResponse = StartNode(C.CString(nodeConfigJSON))
|
||||||
|
|
||||||
|
|
||||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &response); err != nil {
|
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &response); err != nil {
|
||||||
t.Errorf("cannot decode StartNode response (%s): %v", C.GoString(rawResponse), err)
|
t.Errorf("cannot decode StartNode response (%s): %v", C.GoString(rawResponse), err)
|
||||||
return false
|
return false
|
||||||
|
@ -372,6 +375,18 @@ func testStopResumeNode(t *testing.T) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testCallRPC(t *testing.T) bool {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}` + "\n"
|
||||||
|
rawResponse := CallRPC(C.CString(`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`))
|
||||||
|
received := C.GoString(rawResponse)
|
||||||
|
if expected != received {
|
||||||
|
t.Errorf("unexpected reponse: expected: %v, got: %v", expected, received)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func testCreateChildAccount(t *testing.T) bool {
|
func testCreateChildAccount(t *testing.T) bool {
|
||||||
// to make sure that we start with empty account (which might get populated during previous tests)
|
// to make sure that we start with empty account (which might get populated during previous tests)
|
||||||
if err := statusAPI.Logout(); err != nil {
|
if err := statusAPI.Logout(); err != nil {
|
||||||
|
|
|
@ -40,25 +40,29 @@ func (api *StatusAPI) StartNode(config *params.NodeConfig) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
<-nodeStarted
|
||||||
<-nodeStarted // do not return up until backend is ready
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartNodeNonBlocking start Status node, fails if node is already started
|
// StartNodeAsync start Status node, fails if node is already started
|
||||||
// Returns immediately w/o waiting for node to start (relies on listening
|
// Returns immediately w/o waiting for node to start (see node.ready)
|
||||||
// for node.started signal)
|
func (api *StatusAPI) StartNodeAsync(config *params.NodeConfig) (<-chan struct{}, error) {
|
||||||
func (api *StatusAPI) StartNodeNonBlocking(config *params.NodeConfig) error {
|
return api.b.StartNode(config)
|
||||||
_, err := api.b.StartNode(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopNode stop Status node. Stopped node cannot be resumed.
|
// StopNode stop Status node. Stopped node cannot be resumed.
|
||||||
func (api *StatusAPI) StopNode() error {
|
func (api *StatusAPI) StopNode() error {
|
||||||
|
nodeStopped, err := api.b.StopNode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
<-nodeStopped
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopNodeAsync stop Status node. Stopped node cannot be resumed.
|
||||||
|
// Returns immediately, w/o waiting for node to stop (see node.stopped)
|
||||||
|
func (api *StatusAPI) StopNodeAsync() (<-chan struct{}, error) {
|
||||||
return api.b.StopNode()
|
return api.b.StopNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +76,11 @@ func (api *StatusAPI) RestartNode() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestartNodeAsync restart running Status node, in async manner
|
||||||
|
func (api *StatusAPI) RestartNodeAsync() (<-chan struct{}, error) {
|
||||||
|
return api.b.RestartNode()
|
||||||
|
}
|
||||||
|
|
||||||
// ResetChainData remove chain data from data directory.
|
// ResetChainData remove chain data from data directory.
|
||||||
// Node is stopped, and new node is started, with clean data directory.
|
// Node is stopped, and new node is started, with clean data directory.
|
||||||
func (api *StatusAPI) ResetChainData() error {
|
func (api *StatusAPI) ResetChainData() error {
|
||||||
|
@ -83,14 +92,14 @@ func (api *StatusAPI) ResetChainData() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopulateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
// ResetChainDataAsync remove chain data from data directory, in async manner
|
||||||
func (api *StatusAPI) PopulateStaticPeers() error {
|
func (api *StatusAPI) ResetChainDataAsync() (<-chan struct{}, error) {
|
||||||
return api.b.nodeManager.PopulateStaticPeers()
|
return api.b.ResetChainData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer adds new static peer node
|
// CallRPC executes RPC request on node's in-proc RPC server
|
||||||
func (api *StatusAPI) AddPeer(url string) error {
|
func (api *StatusAPI) CallRPC(inputJSON string) string {
|
||||||
return api.b.nodeManager.AddPeer(url)
|
return api.b.CallRPC(inputJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccount creates an internal geth account
|
// CreateAccount creates an internal geth account
|
||||||
|
@ -98,38 +107,38 @@ func (api *StatusAPI) AddPeer(url string) error {
|
||||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
||||||
// sub-account derivations)
|
// sub-account derivations)
|
||||||
func (api *StatusAPI) CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
|
func (api *StatusAPI) CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
|
||||||
return api.b.CreateAccount(password)
|
return api.b.AccountManager().CreateAccount(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateChildAccount creates sub-account for an account identified by parent address.
|
// CreateChildAccount creates sub-account for an account identified by parent address.
|
||||||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
||||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||||
func (api *StatusAPI) CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
func (api *StatusAPI) CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||||
return api.b.CreateChildAccount(parentAddress, password)
|
return api.b.AccountManager().CreateChildAccount(parentAddress, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecoverAccount re-creates master key using given details.
|
// RecoverAccount re-creates master key using given details.
|
||||||
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
||||||
func (api *StatusAPI) RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
func (api *StatusAPI) RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
||||||
return api.b.RecoverAccount(password, mnemonic)
|
return api.b.AccountManager().RecoverAccount(password, mnemonic)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
||||||
// If no error is returned, then account is considered verified.
|
// If no error is returned, then account is considered verified.
|
||||||
func (api *StatusAPI) VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
func (api *StatusAPI) VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
||||||
return api.b.VerifyAccountPassword(keyStoreDir, address, password)
|
return api.b.AccountManager().VerifyAccountPassword(keyStoreDir, address, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
||||||
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
||||||
// all previous identities are removed).
|
// all previous identities are removed).
|
||||||
func (api *StatusAPI) SelectAccount(address, password string) error {
|
func (api *StatusAPI) SelectAccount(address, password string) error {
|
||||||
return api.b.SelectAccount(address, password)
|
return api.b.AccountManager().SelectAccount(address, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logout clears whisper identities
|
// Logout clears whisper identities
|
||||||
func (api *StatusAPI) Logout() error {
|
func (api *StatusAPI) Logout() error {
|
||||||
return api.b.Logout()
|
return api.b.AccountManager().Logout()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompleteTransaction instructs backend to complete sending of a given transaction
|
// CompleteTransaction instructs backend to complete sending of a given transaction
|
||||||
|
@ -152,20 +161,19 @@ func (api *StatusAPI) DiscardTransactions(ids string) map[string]common.RawDisca
|
||||||
return api.b.DiscardTransactions(ids)
|
return api.b.DiscardTransactions(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse creates a new jail cell context, with the given chatID as identifier.
|
// JailParse creates a new jail cell context, with the given chatID as identifier.
|
||||||
// New context executes provided JavaScript code, right after the initialization.
|
// New context executes provided JavaScript code, right after the initialization.
|
||||||
func (api *StatusAPI) JailParse(chatID string, js string) string {
|
func (api *StatusAPI) JailParse(chatID string, js string) string {
|
||||||
return api.b.jailManager.Parse(chatID, js)
|
return api.b.jailManager.Parse(chatID, js)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call executes given JavaScript function w/i a jail cell context identified by the chatID.
|
// JailCall executes given JavaScript function w/i a jail cell context identified by the chatID.
|
||||||
// Jail cell is clonned before call is executed i.e. all calls execute w/i their own contexts.
|
// Jail cell is clonned before call is executed i.e. all calls execute w/i their own contexts.
|
||||||
func (api *StatusAPI) JailCall(chatID string, path string, args string) string {
|
func (api *StatusAPI) JailCall(chatID string, path string, args string) string {
|
||||||
return api.b.jailManager.Call(chatID, path, args)
|
return api.b.jailManager.Call(chatID, path, args)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseJS allows to setup initial JavaScript to be loaded on each jail.Parse()
|
// JailBaseJS allows to setup initial JavaScript to be loaded on each jail.Parse()
|
||||||
func (api *StatusAPI) JailBaseJS(js string) {
|
func (api *StatusAPI) JailBaseJS(js string) {
|
||||||
api.b.jailManager.BaseJS(js)
|
api.b.jailManager.BaseJS(js)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
package api_test
|
package api_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/status-im/status-go/geth/api"
|
"github.com/status-im/status-go/geth/api"
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
. "github.com/status-im/status-go/geth/testing"
|
. "github.com/status-im/status-go/geth/testing"
|
||||||
|
@ -26,33 +32,99 @@ func (s *APITestSuite) SetupTest() {
|
||||||
s.api = statusAPI
|
s.api = statusAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *APITestSuite) TestStartStopRaces() {
|
func (s *APITestSuite) TestCHTUpdate() {
|
||||||
require := s.Require()
|
require := s.Require()
|
||||||
|
require.NotNil(s.api)
|
||||||
|
|
||||||
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "cht-updates")
|
||||||
|
require.NoError(err)
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
url := "https://gist.githubusercontent.com/farazdagi/3d05d1d3bfa36db7b650c955e23fd7ae/raw/?u=" + strconv.Itoa(int(time.Now().Unix()))
|
||||||
|
configJSON := `{
|
||||||
|
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
|
||||||
|
"DataDir": "` + tmpDir + `",
|
||||||
|
"LogEnabled": true,
|
||||||
|
"LogLevel": "INFO",
|
||||||
|
"LightEthConfig": {
|
||||||
|
"CHTRootConfigURL": "` + url + `"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
nodeConfig, err := params.LoadNodeConfig(configJSON)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
progress := make(chan struct{}, 100)
|
// start node
|
||||||
|
nodeConfig.DevMode = true
|
||||||
|
s.api.StartNode(nodeConfig)
|
||||||
|
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||||
|
s.api.StopNode()
|
||||||
|
}
|
||||||
|
|
||||||
start := func() {
|
func (s *APITestSuite) TestRaceConditions() {
|
||||||
s.api.StartNode(nodeConfig)
|
require := s.Require()
|
||||||
progress <- struct{}{}
|
require.NotNil(s.api)
|
||||||
}
|
|
||||||
stop := func() {
|
cnt := 25
|
||||||
s.api.StopNode()
|
progress := make(chan struct{}, cnt)
|
||||||
progress <- struct{}{}
|
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
nodeConfig1, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig2, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||||
|
|
||||||
|
var funcsToTest = []func(*params.NodeConfig){
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("StartNodeAsync()")
|
||||||
|
_, err := s.api.StartNodeAsync(config)
|
||||||
|
s.T().Logf("StartNodeAsync() for network: %d, error: %v", config.NetworkID, err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("StopNodeAsync()")
|
||||||
|
_, err := s.api.StopNodeAsync()
|
||||||
|
s.T().Logf("StopNodeAsync(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("RestartNodeAsync()")
|
||||||
|
_, err := s.api.RestartNodeAsync()
|
||||||
|
s.T().Logf("RestartNodeAsync(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("ResetChainDataAsync()")
|
||||||
|
_, err := s.api.ResetChainDataAsync()
|
||||||
|
s.T().Logf("ResetChainDataAsync(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 50; i++ {
|
// increase StartNode()/StopNode() population
|
||||||
go start()
|
for i := 0; i < 5; i++ {
|
||||||
go stop()
|
funcsToTest = append(funcsToTest, funcsToTest[0], funcsToTest[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < cnt; i++ {
|
||||||
|
randConfig := nodeConfigs[rnd.Intn(len(nodeConfigs))]
|
||||||
|
randFunc := funcsToTest[rnd.Intn(len(funcsToTest))]
|
||||||
|
|
||||||
|
if rnd.Intn(100) > 75 { // introduce random delays
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
go randFunc(randConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt := 0
|
|
||||||
for range progress {
|
for range progress {
|
||||||
cnt += 1
|
cnt -= 1
|
||||||
if cnt >= 100 {
|
if cnt <= 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second) // so that we see some logs
|
||||||
|
s.api.StopNode() // just in case we have a node running
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"sync"
|
||||||
|
|
||||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
@ -13,10 +14,13 @@ import (
|
||||||
|
|
||||||
// StatusBackend implements Status.im service
|
// StatusBackend implements Status.im service
|
||||||
type StatusBackend struct {
|
type StatusBackend struct {
|
||||||
|
sync.Mutex
|
||||||
|
nodeReady chan struct{} // channel to wait for when node is fully ready
|
||||||
nodeManager common.NodeManager
|
nodeManager common.NodeManager
|
||||||
accountManager common.AccountManager
|
accountManager common.AccountManager
|
||||||
txQueueManager common.TxQueueManager
|
txQueueManager common.TxQueueManager
|
||||||
jailManager common.JailManager
|
jailManager common.JailManager
|
||||||
|
rpcManager common.RPCManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStatusBackend create a new NewStatusBackend instance
|
// NewStatusBackend create a new NewStatusBackend instance
|
||||||
|
@ -30,6 +34,7 @@ func NewStatusBackend() *StatusBackend {
|
||||||
accountManager: accountManager,
|
accountManager: accountManager,
|
||||||
txQueueManager: node.NewTxQueueManager(nodeManager, accountManager),
|
txQueueManager: node.NewTxQueueManager(nodeManager, accountManager),
|
||||||
jailManager: jail.New(nodeManager),
|
jailManager: jail.New(nodeManager),
|
||||||
|
rpcManager: node.NewRPCManager(nodeManager),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,102 +60,115 @@ func (m *StatusBackend) IsNodeRunning() bool {
|
||||||
|
|
||||||
// StartNode start Status node, fails if node is already started
|
// StartNode start Status node, fails if node is already started
|
||||||
func (m *StatusBackend) StartNode(config *params.NodeConfig) (<-chan struct{}, error) {
|
func (m *StatusBackend) StartNode(config *params.NodeConfig) (<-chan struct{}, error) {
|
||||||
backendReady := make(chan struct{})
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if m.nodeReady != nil {
|
||||||
|
return nil, node.ErrNodeExists
|
||||||
|
}
|
||||||
|
|
||||||
nodeStarted, err := m.nodeManager.StartNode(config)
|
nodeStarted, err := m.nodeManager.StartNode(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go m.onNodeStart(backendReady, nodeStarted)
|
m.nodeReady = make(chan struct{}, 1)
|
||||||
return backendReady, err
|
go m.onNodeStart(nodeStarted, m.nodeReady) // waits on nodeStarted, writes to backendReady
|
||||||
|
|
||||||
|
return m.nodeReady, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *StatusBackend) onNodeStart(backendReady chan struct{}, nodeStarted <-chan struct{}) {
|
// onNodeStart does everything required to prepare backend
|
||||||
defer close(backendReady)
|
func (m *StatusBackend) onNodeStart(nodeStarted <-chan struct{}, backendReady chan struct{}) {
|
||||||
<-nodeStarted
|
<-nodeStarted
|
||||||
|
|
||||||
if err := m.registerHandlers(); err != nil {
|
if err := m.registerHandlers(); err != nil {
|
||||||
log.Error("Handler registration failed", "err", err)
|
log.Error("Handler registration failed", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.accountManager.ReSelectAccount()
|
||||||
|
log.Info("Account reselected")
|
||||||
|
|
||||||
|
close(backendReady)
|
||||||
|
node.SendSignal(node.SignalEnvelope{
|
||||||
|
Type: node.EventNodeReady,
|
||||||
|
Event: struct{}{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopNode stop Status node. Stopped node cannot be resumed.
|
||||||
|
func (m *StatusBackend) StopNode() (<-chan struct{}, error) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if m.nodeReady == nil {
|
||||||
|
return nil, node.ErrNoRunningNode
|
||||||
|
}
|
||||||
|
<-m.nodeReady
|
||||||
|
|
||||||
|
nodeStopped, err := m.nodeManager.StopNode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backendStopped := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
<-nodeStopped
|
||||||
|
m.Lock()
|
||||||
|
m.nodeReady = nil
|
||||||
|
m.Unlock()
|
||||||
|
close(backendStopped)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return backendStopped, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestartNode restart running Status node, fails if node is not running
|
// RestartNode restart running Status node, fails if node is not running
|
||||||
func (m *StatusBackend) RestartNode() (<-chan struct{}, error) {
|
func (m *StatusBackend) RestartNode() (<-chan struct{}, error) {
|
||||||
backendReady := make(chan struct{})
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if m.nodeReady == nil {
|
||||||
|
return nil, node.ErrNoRunningNode
|
||||||
|
}
|
||||||
|
<-m.nodeReady
|
||||||
|
|
||||||
nodeRestarted, err := m.nodeManager.RestartNode()
|
nodeRestarted, err := m.nodeManager.RestartNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go m.onNodeStart(backendReady, nodeRestarted)
|
m.nodeReady = make(chan struct{}, 1)
|
||||||
return backendReady, err
|
go m.onNodeStart(nodeRestarted, m.nodeReady) // waits on nodeRestarted, writes to backendReady
|
||||||
}
|
|
||||||
|
|
||||||
// StopNode stop Status node. Stopped node cannot be resumed.
|
return m.nodeReady, err
|
||||||
func (m *StatusBackend) StopNode() error {
|
|
||||||
return m.nodeManager.StopNode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetChainData remove chain data from data directory.
|
// ResetChainData remove chain data from data directory.
|
||||||
// Node is stopped, and new node is started, with clean data directory.
|
// Node is stopped, and new node is started, with clean data directory.
|
||||||
func (m *StatusBackend) ResetChainData() (<-chan struct{}, error) {
|
func (m *StatusBackend) ResetChainData() (<-chan struct{}, error) {
|
||||||
backendReady := make(chan struct{})
|
m.Lock()
|
||||||
nodeRestarted, err := m.nodeManager.ResetChainData()
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if m.nodeReady == nil {
|
||||||
|
return nil, node.ErrNoRunningNode
|
||||||
|
}
|
||||||
|
<-m.nodeReady
|
||||||
|
|
||||||
|
nodeReset, err := m.nodeManager.ResetChainData()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go m.onNodeStart(backendReady, nodeRestarted)
|
m.nodeReady = make(chan struct{}, 1)
|
||||||
return backendReady, err
|
go m.onNodeStart(nodeReset, m.nodeReady) // waits on nodeReset, writes to backendReady
|
||||||
|
|
||||||
|
return m.nodeReady, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccount creates an internal geth account
|
// CallRPC executes RPC request on node's in-proc RPC server
|
||||||
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
func (m *StatusBackend) CallRPC(inputJSON string) string {
|
||||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
return m.rpcManager.Call(inputJSON)
|
||||||
// sub-account derivations)
|
|
||||||
func (m *StatusBackend) CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
|
|
||||||
return m.accountManager.CreateAccount(password)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateChildAccount creates sub-account for an account identified by parent address.
|
|
||||||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
|
||||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
|
||||||
func (m *StatusBackend) CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
|
||||||
return m.accountManager.CreateChildAccount(parentAddress, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecoverAccount re-creates master key using given details.
|
|
||||||
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
|
||||||
func (m *StatusBackend) RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
|
||||||
return m.accountManager.RecoverAccount(password, mnemonic)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
|
||||||
// If no error is returned, then account is considered verified.
|
|
||||||
func (m *StatusBackend) VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
|
||||||
return m.accountManager.VerifyAccountPassword(keyStoreDir, address, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
|
||||||
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
|
||||||
// all previous identities are removed).
|
|
||||||
func (m *StatusBackend) SelectAccount(address, password string) error {
|
|
||||||
return m.accountManager.SelectAccount(address, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSelectAccount selects previously selected account, often, after node restart.
|
|
||||||
func (m *StatusBackend) ReSelectAccount() error {
|
|
||||||
return m.accountManager.ReSelectAccount()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logout clears whisper identities
|
|
||||||
func (m *StatusBackend) Logout() error {
|
|
||||||
return m.accountManager.Logout()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectedAccount returns currently selected account
|
|
||||||
func (m *StatusBackend) SelectedAccount() (*common.SelectedExtKey, error) {
|
|
||||||
return m.accountManager.SelectedAccount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompleteTransaction instructs backend to complete sending of a given transaction
|
// CompleteTransaction instructs backend to complete sending of a given transaction
|
||||||
|
@ -194,13 +212,5 @@ func (m *StatusBackend) registerHandlers() error {
|
||||||
lightEthereum.StatusBackend.SetTransactionReturnHandler(m.txQueueManager.TransactionReturnHandler())
|
lightEthereum.StatusBackend.SetTransactionReturnHandler(m.txQueueManager.TransactionReturnHandler())
|
||||||
log.Info("Registered handler", "fn", "TransactionReturnHandler")
|
log.Info("Registered handler", "fn", "TransactionReturnHandler")
|
||||||
|
|
||||||
m.ReSelectAccount()
|
|
||||||
log.Info("Account reselected")
|
|
||||||
|
|
||||||
node.SendSignal(node.SignalEnvelope{
|
|
||||||
Type: node.EventNodeReady,
|
|
||||||
Event: struct{}{},
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (s *BackendTestSuite) TestAccountsList() {
|
||||||
require.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
require.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
address, _, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address, _, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// ensure that there is still no accounts returned
|
// ensure that there is still no accounts returned
|
||||||
|
@ -43,7 +43,7 @@ func (s *BackendTestSuite) TestAccountsList() {
|
||||||
require.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
require.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
||||||
|
|
||||||
// select account (sub-accounts will be created for this key)
|
// select account (sub-accounts will be created for this key)
|
||||||
err = s.backend.SelectAccount(address, TestConfig.Account1.Password)
|
err = s.backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password)
|
||||||
require.NoError(err, "account selection failed")
|
require.NoError(err, "account selection failed")
|
||||||
|
|
||||||
// at this point main account should show up
|
// at this point main account should show up
|
||||||
|
@ -53,7 +53,7 @@ func (s *BackendTestSuite) TestAccountsList() {
|
||||||
fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), "0x"+address))
|
fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), "0x"+address))
|
||||||
|
|
||||||
// create sub-account 1
|
// create sub-account 1
|
||||||
subAccount1, subPubKey1, err := s.backend.CreateChildAccount("", TestConfig.Account1.Password)
|
subAccount1, subPubKey1, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||||
require.NoError(err, "cannot create sub-account")
|
require.NoError(err, "cannot create sub-account")
|
||||||
|
|
||||||
// now we expect to see both main account and sub-account 1
|
// now we expect to see both main account and sub-account 1
|
||||||
|
@ -63,7 +63,7 @@ func (s *BackendTestSuite) TestAccountsList() {
|
||||||
require.Equal(string(accounts[1].Hex()), "0x"+subAccount1, "subAcount1 not returned")
|
require.Equal(string(accounts[1].Hex()), "0x"+subAccount1, "subAcount1 not returned")
|
||||||
|
|
||||||
// create sub-account 2, index automatically progresses
|
// create sub-account 2, index automatically progresses
|
||||||
subAccount2, subPubKey2, err := s.backend.CreateChildAccount("", TestConfig.Account1.Password)
|
subAccount2, subPubKey2, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||||
require.NoError(err, "cannot create sub-account")
|
require.NoError(err, "cannot create sub-account")
|
||||||
require.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
require.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ func (s *BackendTestSuite) TestCreateChildAccount() {
|
||||||
require.NotNil(keyStore)
|
require.NotNil(keyStore)
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
address, pubKey, mnemonic, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address, pubKey, mnemonic, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||||
|
|
||||||
|
@ -106,28 +106,28 @@ func (s *BackendTestSuite) TestCreateChildAccount() {
|
||||||
require.NotNil(key.ExtendedKey, "CKD#2 has not been generated for new account")
|
require.NotNil(key.ExtendedKey, "CKD#2 has not been generated for new account")
|
||||||
|
|
||||||
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
||||||
_, _, err = s.backend.CreateChildAccount("", TestConfig.Account1.Password)
|
_, _, err = s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||||
require.EqualError(node.ErrNoAccountSelected, err.Error(), "expected error is not returned (tried to create sub-account w/o login)")
|
require.EqualError(node.ErrNoAccountSelected, err.Error(), "expected error is not returned (tried to create sub-account w/o login)")
|
||||||
|
|
||||||
err = s.backend.SelectAccount(address, TestConfig.Account1.Password)
|
err = s.backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password)
|
||||||
require.NoError(err, "cannot select account")
|
require.NoError(err, "cannot select account")
|
||||||
|
|
||||||
// try to create sub-account with wrong password
|
// try to create sub-account with wrong password
|
||||||
_, _, err = s.backend.CreateChildAccount("", "wrong password")
|
_, _, err = s.backend.AccountManager().CreateChildAccount("", "wrong password")
|
||||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||||
require.EqualError(expectedErr, err.Error(), "create sub-account with wrong password")
|
require.EqualError(expectedErr, err.Error(), "create sub-account with wrong password")
|
||||||
|
|
||||||
// create sub-account (from implicit parent)
|
// create sub-account (from implicit parent)
|
||||||
subAccount1, subPubKey1, err := s.backend.CreateChildAccount("", TestConfig.Account1.Password)
|
subAccount1, subPubKey1, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||||
require.NoError(err, "cannot create sub-account")
|
require.NoError(err, "cannot create sub-account")
|
||||||
|
|
||||||
// make sure that sub-account index automatically progresses
|
// make sure that sub-account index automatically progresses
|
||||||
subAccount2, subPubKey2, err := s.backend.CreateChildAccount("", TestConfig.Account1.Password)
|
subAccount2, subPubKey2, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
require.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
||||||
|
|
||||||
// create sub-account (from explicit parent)
|
// create sub-account (from explicit parent)
|
||||||
subAccount3, subPubKey3, err := s.backend.CreateChildAccount(subAccount2, TestConfig.Account1.Password)
|
subAccount3, subPubKey3, err := s.backend.AccountManager().CreateChildAccount(subAccount2, TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.False(subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3)
|
require.False(subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3)
|
||||||
}
|
}
|
||||||
|
@ -144,12 +144,12 @@ func (s *BackendTestSuite) TestRecoverAccount() {
|
||||||
require.NotNil(keyStore)
|
require.NotNil(keyStore)
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
address, pubKey, mnemonic, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address, pubKey, mnemonic, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||||
|
|
||||||
// try recovering using password + mnemonic
|
// try recovering using password + mnemonic
|
||||||
addressCheck, pubKeyCheck, err := s.backend.RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
addressCheck, pubKeyCheck, err := s.backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||||
require.NoError(err, "recover account failed")
|
require.NoError(err, "recover account failed")
|
||||||
require.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect accound details recovered")
|
require.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect accound details recovered")
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ func (s *BackendTestSuite) TestRecoverAccount() {
|
||||||
|
|
||||||
require.NoError(keyStore.Delete(account, TestConfig.Account1.Password), "cannot remove account")
|
require.NoError(keyStore.Delete(account, TestConfig.Account1.Password), "cannot remove account")
|
||||||
|
|
||||||
addressCheck, pubKeyCheck, err = s.backend.RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
addressCheck, pubKeyCheck, err = s.backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||||
require.NoError(err, "recover account failed (for non-cached account)")
|
require.NoError(err, "recover account failed (for non-cached account)")
|
||||||
require.False(address != addressCheck || pubKey != pubKeyCheck,
|
require.False(address != addressCheck || pubKey != pubKeyCheck,
|
||||||
"incorrect account details recovered (for non-cached account)")
|
"incorrect account details recovered (for non-cached account)")
|
||||||
|
@ -174,7 +174,7 @@ func (s *BackendTestSuite) TestRecoverAccount() {
|
||||||
require.Equal(extChild2String, key.ExtendedKey.String(), "CKD#2 key mismatch")
|
require.Equal(extChild2String, key.ExtendedKey.String(), "CKD#2 key mismatch")
|
||||||
|
|
||||||
// make sure that calling import several times, just returns from cache (no error is expected)
|
// make sure that calling import several times, just returns from cache (no error is expected)
|
||||||
addressCheck, pubKeyCheck, err = s.backend.RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
addressCheck, pubKeyCheck, err = s.backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||||
require.NoError(err, "recover account failed (for non-cached account)")
|
require.NoError(err, "recover account failed (for non-cached account)")
|
||||||
require.False(address != addressCheck || pubKey != pubKeyCheck,
|
require.False(address != addressCheck || pubKey != pubKeyCheck,
|
||||||
"incorrect account details recovered (for non-cached account)")
|
"incorrect account details recovered (for non-cached account)")
|
||||||
|
@ -184,7 +184,7 @@ func (s *BackendTestSuite) TestRecoverAccount() {
|
||||||
|
|
||||||
// make sure that identity is not (yet injected)
|
// make sure that identity is not (yet injected)
|
||||||
require.False(whisperService.HasKeyPair(pubKeyCheck), "identity already present in whisper")
|
require.False(whisperService.HasKeyPair(pubKeyCheck), "identity already present in whisper")
|
||||||
require.NoError(s.backend.SelectAccount(addressCheck, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(addressCheck, TestConfig.Account1.Password))
|
||||||
require.True(whisperService.HasKeyPair(pubKeyCheck), "identity not injected into whisper")
|
require.True(whisperService.HasKeyPair(pubKeyCheck), "identity not injected into whisper")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,11 +199,11 @@ func (s *BackendTestSuite) TestSelectAccount() {
|
||||||
whisperService := s.WhisperService()
|
whisperService := s.WhisperService()
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
address1, pubKey1, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address1, pubKey1, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
s.T().Logf("Account created: {address: %s, key: %s}", address1, pubKey1)
|
s.T().Logf("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||||
|
|
||||||
address2, pubKey2, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address2, pubKey2, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
s.T().Logf("Account created: {address: %s, key: %s}", address2, pubKey2)
|
s.T().Logf("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||||
|
|
||||||
|
@ -211,17 +211,17 @@ func (s *BackendTestSuite) TestSelectAccount() {
|
||||||
require.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
||||||
|
|
||||||
// try selecting with wrong password
|
// try selecting with wrong password
|
||||||
err = s.backend.SelectAccount(address1, "wrongPassword")
|
err = s.backend.AccountManager().SelectAccount(address1, "wrongPassword")
|
||||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||||
require.EqualError(expectedErr, err.Error(), "select account is expected to throw error: wrong password used")
|
require.EqualError(expectedErr, err.Error(), "select account is expected to throw error: wrong password used")
|
||||||
|
|
||||||
err = s.backend.SelectAccount(address1, TestConfig.Account1.Password)
|
err = s.backend.AccountManager().SelectAccount(address1, TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
require.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
||||||
|
|
||||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||||
require.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
||||||
require.NoError(s.backend.SelectAccount(address2, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(address2, TestConfig.Account1.Password))
|
||||||
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
||||||
}
|
}
|
||||||
|
@ -236,15 +236,15 @@ func (s *BackendTestSuite) TestLogout() {
|
||||||
whisperService := s.WhisperService()
|
whisperService := s.WhisperService()
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
address, pubKey, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address, pubKey, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// make sure that identity doesn't exist (yet) in Whisper
|
// make sure that identity doesn't exist (yet) in Whisper
|
||||||
require.False(whisperService.HasKeyPair(pubKey), "identity already present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey), "identity already present in whisper")
|
||||||
require.NoError(s.backend.SelectAccount(address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password))
|
||||||
require.True(whisperService.HasKeyPair(pubKey), "identity not injected into whisper")
|
require.True(whisperService.HasKeyPair(pubKey), "identity not injected into whisper")
|
||||||
|
|
||||||
require.NoError(s.backend.Logout())
|
require.NoError(s.backend.AccountManager().Logout())
|
||||||
require.False(whisperService.HasKeyPair(pubKey), "identity not cleared from whisper")
|
require.False(whisperService.HasKeyPair(pubKey), "identity not cleared from whisper")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,42 +258,44 @@ func (s *BackendTestSuite) TestSelectedAccountOnRestart() {
|
||||||
whisperService := s.WhisperService()
|
whisperService := s.WhisperService()
|
||||||
|
|
||||||
// create test accounts
|
// create test accounts
|
||||||
address1, pubKey1, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address1, pubKey1, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
address2, pubKey2, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
address2, pubKey2, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// make sure that identity is not (yet injected)
|
// make sure that identity is not (yet injected)
|
||||||
require.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
||||||
|
|
||||||
// make sure that no account is selected by default
|
// make sure that no account is selected by default
|
||||||
selectedAccount, err := s.backend.SelectedAccount()
|
selectedAccount, err := s.backend.AccountManager().SelectedAccount()
|
||||||
require.EqualError(node.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
require.EqualError(node.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
||||||
require.Nil(selectedAccount)
|
require.Nil(selectedAccount)
|
||||||
|
|
||||||
// select account
|
// select account
|
||||||
err = s.backend.SelectAccount(address1, "wrongPassword")
|
err = s.backend.AccountManager().SelectAccount(address1, "wrongPassword")
|
||||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||||
require.EqualError(expectedErr, err.Error())
|
require.EqualError(expectedErr, err.Error())
|
||||||
|
|
||||||
require.NoError(s.backend.SelectAccount(address1, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(address1, TestConfig.Account1.Password))
|
||||||
require.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
require.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
||||||
|
|
||||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||||
require.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
||||||
require.NoError(s.backend.SelectAccount(address2, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(address2, TestConfig.Account1.Password))
|
||||||
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
||||||
|
|
||||||
// stop node (and all of its sub-protocols)
|
// stop node (and all of its sub-protocols)
|
||||||
nodeConfig, err := s.NodeManager.NodeConfig()
|
nodeConfig, err := s.backend.NodeManager().NodeConfig()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(nodeConfig)
|
require.NotNil(nodeConfig)
|
||||||
preservedNodeConfig := *nodeConfig
|
preservedNodeConfig := *nodeConfig
|
||||||
require.NoError(s.NodeManager.StopNode())
|
nodeStoped, err := s.backend.StopNode()
|
||||||
|
require.NoError(err)
|
||||||
|
<-nodeStoped
|
||||||
|
|
||||||
// make sure that account is still selected
|
// make sure that account is still selected
|
||||||
selectedAccount, err = s.backend.SelectedAccount()
|
selectedAccount, err = s.backend.AccountManager().SelectedAccount()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(selectedAccount)
|
require.NotNil(selectedAccount)
|
||||||
require.Equal(selectedAccount.Address.Hex(), "0x"+address2, "incorrect address selected")
|
require.Equal(selectedAccount.Address.Hex(), "0x"+address2, "incorrect address selected")
|
||||||
|
@ -304,7 +306,7 @@ func (s *BackendTestSuite) TestSelectedAccountOnRestart() {
|
||||||
<-nodeStarted
|
<-nodeStarted
|
||||||
|
|
||||||
// re-check selected account (account2 MUST be selected)
|
// re-check selected account (account2 MUST be selected)
|
||||||
selectedAccount, err = s.backend.SelectedAccount()
|
selectedAccount, err = s.backend.AccountManager().SelectedAccount()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(selectedAccount)
|
require.NotNil(selectedAccount)
|
||||||
require.Equal(selectedAccount.Address.Hex(), "0x"+address2, "incorrect address selected")
|
require.Equal(selectedAccount.Address.Hex(), "0x"+address2, "incorrect address selected")
|
||||||
|
@ -323,13 +325,13 @@ func (s *BackendTestSuite) TestSelectedAccountOnRestart() {
|
||||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||||
|
|
||||||
// now logout, and make sure that on restart no account is selected (i.e. logout works properly)
|
// now logout, and make sure that on restart no account is selected (i.e. logout works properly)
|
||||||
require.NoError(s.backend.Logout())
|
require.NoError(s.backend.AccountManager().Logout())
|
||||||
s.RestartTestNode()
|
s.RestartTestNode()
|
||||||
whisperService = s.WhisperService()
|
whisperService = s.WhisperService()
|
||||||
require.False(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
require.False(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||||
|
|
||||||
selectedAccount, err = s.backend.SelectedAccount()
|
selectedAccount, err = s.backend.AccountManager().SelectedAccount()
|
||||||
require.EqualError(node.ErrNoAccountSelected, err.Error())
|
require.EqualError(node.ErrNoAccountSelected, err.Error())
|
||||||
require.Nil(selectedAccount)
|
require.Nil(selectedAccount)
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (s *BackendTestSuite) TestJailContractDeployment() {
|
||||||
|
|
||||||
//t.Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
//t.Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
||||||
|
|
||||||
s.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
||||||
|
@ -117,7 +117,7 @@ func (s *BackendTestSuite) TestJailSendQueuedTransaction() {
|
||||||
require.NotNil(jailInstance)
|
require.NotNil(jailInstance)
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
txParams := `{
|
txParams := `{
|
||||||
"from": "` + TestConfig.Account1.Address + `",
|
"from": "` + TestConfig.Account1.Address + `",
|
||||||
|
@ -310,7 +310,7 @@ func (s *BackendTestSuite) TestGasEstimation() {
|
||||||
|
|
||||||
//t.Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
//t.Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
||||||
|
|
||||||
s.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package api_test
|
package api_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||||
"github.com/status-im/status-go/geth/api"
|
"github.com/status-im/status-go/geth/api"
|
||||||
"github.com/status-im/status-go/geth/common"
|
"github.com/status-im/status-go/geth/common"
|
||||||
|
"github.com/status-im/status-go/geth/jail"
|
||||||
"github.com/status-im/status-go/geth/node"
|
"github.com/status-im/status-go/geth/node"
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
. "github.com/status-im/status-go/geth/testing"
|
. "github.com/status-im/status-go/geth/testing"
|
||||||
|
@ -19,7 +22,7 @@ func TestBackendTestSuite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendTestSuite struct {
|
type BackendTestSuite struct {
|
||||||
BaseTestSuite
|
suite.Suite
|
||||||
backend *api.StatusBackend
|
backend *api.StatusBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +32,6 @@ func (s *BackendTestSuite) SetupTest() {
|
||||||
require.NotNil(backend)
|
require.NotNil(backend)
|
||||||
require.IsType(&api.StatusBackend{}, backend)
|
require.IsType(&api.StatusBackend{}, backend)
|
||||||
s.backend = backend
|
s.backend = backend
|
||||||
s.NodeManager = backend.NodeManager()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BackendTestSuite) StartTestBackend(networkID int) {
|
func (s *BackendTestSuite) StartTestBackend(networkID int) {
|
||||||
|
@ -54,7 +56,9 @@ func (s *BackendTestSuite) StopTestBackend() {
|
||||||
require := s.Require()
|
require := s.Require()
|
||||||
require.NotNil(s.backend)
|
require.NotNil(s.backend)
|
||||||
require.True(s.backend.IsNodeRunning())
|
require.True(s.backend.IsNodeRunning())
|
||||||
require.NoError(s.backend.StopNode())
|
backendStopped, err := s.backend.StopNode()
|
||||||
|
require.NoError(err)
|
||||||
|
<-backendStopped
|
||||||
require.False(s.backend.IsNodeRunning())
|
require.False(s.backend.IsNodeRunning())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,38 +114,274 @@ func (s *BackendTestSuite) TestNodeStartStop() {
|
||||||
|
|
||||||
// try stopping non-started node
|
// try stopping non-started node
|
||||||
require.False(s.backend.IsNodeRunning())
|
require.False(s.backend.IsNodeRunning())
|
||||||
err = s.backend.StopNode()
|
nodeStopped, err := s.backend.StopNode()
|
||||||
if s.Error(err) {
|
require.EqualError(err, node.ErrNoRunningNode.Error())
|
||||||
require.IsType(node.ErrNoRunningNode, err)
|
require.Nil(nodeStopped)
|
||||||
}
|
|
||||||
|
|
||||||
require.False(s.backend.IsNodeRunning())
|
require.False(s.backend.IsNodeRunning())
|
||||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
require.NotNil(nodeStarted)
|
||||||
|
|
||||||
<-nodeStarted // wait till node is started
|
<-nodeStarted // wait till node is started
|
||||||
require.True(s.backend.IsNodeRunning())
|
require.True(s.backend.IsNodeRunning())
|
||||||
|
|
||||||
// try starting another node (w/o stopping the previously started node)
|
// try starting another node (w/o stopping the previously started node)
|
||||||
_, err = s.backend.StartNode(nodeConfig)
|
nodeStarted, err = s.backend.StartNode(nodeConfig)
|
||||||
if s.Error(err) {
|
require.EqualError(err, node.ErrNodeExists.Error())
|
||||||
require.IsType(node.ErrNodeAlreadyExists, err)
|
require.Nil(nodeStarted)
|
||||||
}
|
|
||||||
|
|
||||||
// now stop node, and make sure that a new node, on different network can be started
|
// now stop node, and make sure that a new node, on different network can be started
|
||||||
err = s.backend.StopNode()
|
nodeStopped, err = s.backend.StopNode()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
require.NotNil(nodeStopped)
|
||||||
|
<-nodeStopped
|
||||||
|
|
||||||
// start new node with exactly the same config
|
// start new node with exactly the same config
|
||||||
require.False(s.backend.IsNodeRunning())
|
require.False(s.backend.IsNodeRunning())
|
||||||
nodeStarted, err = s.backend.StartNode(nodeConfig)
|
nodeStarted, err = s.backend.StartNode(nodeConfig)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
defer s.StopTestNode()
|
require.NotNil(nodeStarted)
|
||||||
|
defer s.backend.StopNode()
|
||||||
|
|
||||||
<-nodeStarted
|
<-nodeStarted
|
||||||
require.True(s.backend.IsNodeRunning())
|
require.True(s.backend.IsNodeRunning())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BackendTestSuite) TestCallRPC() {
|
||||||
|
require := s.Require()
|
||||||
|
require.NotNil(s.backend)
|
||||||
|
|
||||||
|
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||||
|
require.NoError(err)
|
||||||
|
require.NotNil(nodeStarted)
|
||||||
|
defer s.backend.StopNode()
|
||||||
|
<-nodeStarted
|
||||||
|
|
||||||
|
progress := make(chan struct{}, 25)
|
||||||
|
type rpcCall struct {
|
||||||
|
inputJSON string
|
||||||
|
validator func(resultJSON string)
|
||||||
|
}
|
||||||
|
var rpcCalls = []rpcCall{
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{
|
||||||
|
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
|
||||||
|
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||||
|
"gas": "0x76c0",
|
||||||
|
"gasPrice": "0x9184e72a000",
|
||||||
|
"value": "0x9184e72a",
|
||||||
|
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
log.Info("eth_sendTransaction")
|
||||||
|
s.T().Log("GOT: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"shh_version","params":[],"id":67}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":67,"result":"0x5"}` + "\n"
|
||||||
|
s.Equal(expected, resultJSON)
|
||||||
|
s.T().Log("shh_version: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}` + "\n"
|
||||||
|
s.Equal(expected, resultJSON)
|
||||||
|
s.T().Log("web3_sha3: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"net_version","params":[],"id":67}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":67,"result":"4"}` + "\n"
|
||||||
|
s.Equal(expected, resultJSON)
|
||||||
|
s.T().Log("net_version: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt := len(rpcCalls) - 1 // send transaction blocks up until complete/discarded/times out
|
||||||
|
for _, r := range rpcCalls {
|
||||||
|
go func(r rpcCall) {
|
||||||
|
s.T().Logf("Run test: %v", r.inputJSON)
|
||||||
|
resultJSON := s.backend.CallRPC(r.inputJSON)
|
||||||
|
r.validator(resultJSON)
|
||||||
|
}(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
for range progress {
|
||||||
|
cnt -= 1
|
||||||
|
if cnt <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BackendTestSuite) TestRaceConditions() {
|
||||||
|
require := s.Require()
|
||||||
|
require.NotNil(s.backend)
|
||||||
|
|
||||||
|
cnt := 25
|
||||||
|
progress := make(chan struct{}, cnt)
|
||||||
|
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
nodeConfig1, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig2, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||||
|
|
||||||
|
var funcsToTest = []func(*params.NodeConfig){
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("StartNode()")
|
||||||
|
_, err := s.backend.StartNode(config)
|
||||||
|
s.T().Logf("StartNode() for network: %d, error: %v", config.NetworkID, err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("StopNode()")
|
||||||
|
_, err := s.backend.StopNode()
|
||||||
|
s.T().Logf("StopNode() for network: %d, error: %v", config.NetworkID, err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("ResetChainData()")
|
||||||
|
_, err := s.backend.ResetChainData()
|
||||||
|
s.T().Logf("ResetChainData(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("RestartNode()")
|
||||||
|
_, err := s.backend.RestartNode()
|
||||||
|
s.T().Logf("RestartNode(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("NodeManager()")
|
||||||
|
instance := s.backend.NodeManager()
|
||||||
|
s.NotNil(instance)
|
||||||
|
s.IsType(&node.NodeManager{}, instance)
|
||||||
|
s.T().Logf("NodeManager(), result: %v", instance)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("AccountManager()")
|
||||||
|
instance := s.backend.AccountManager()
|
||||||
|
s.NotNil(instance)
|
||||||
|
s.IsType(&node.AccountManager{}, instance)
|
||||||
|
s.T().Logf("AccountManager(), result: %v", instance)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("JailManager()")
|
||||||
|
instance := s.backend.JailManager()
|
||||||
|
s.NotNil(instance)
|
||||||
|
s.IsType(&jail.Jail{}, instance)
|
||||||
|
s.T().Logf("JailManager(), result: %v", instance)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("CreateAccount()")
|
||||||
|
address, pubKey, mnemonic, err := s.backend.AccountManager().CreateAccount("password")
|
||||||
|
s.T().Logf("CreateAccount(), error: %v (address: %v, pubKey: %v, mnemonic: %v)", err, address, pubKey, mnemonic)
|
||||||
|
if err == nil {
|
||||||
|
// SelectAccount
|
||||||
|
log.Info("CreateAccount()")
|
||||||
|
err = s.backend.AccountManager().SelectAccount(address, "password")
|
||||||
|
s.T().Logf("SelectAccount(%v, %v), error: %v", address, "password", err)
|
||||||
|
|
||||||
|
// CreateChildAccount
|
||||||
|
log.Info("CreateChildAccount()")
|
||||||
|
address, pubKey, err := s.backend.AccountManager().CreateChildAccount(address, "password")
|
||||||
|
s.T().Logf("CreateAccount(), error: %v (address: %v, pubKey: %v)", err, address, pubKey)
|
||||||
|
|
||||||
|
// RecoverAccount
|
||||||
|
log.Info("RecoverAccount()")
|
||||||
|
address, pubKey, err = s.backend.AccountManager().RecoverAccount("password", mnemonic)
|
||||||
|
s.T().Logf("RecoverAccount(), error: %v (address: %v, pubKey: %v)", err, address, pubKey)
|
||||||
|
}
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("VerifyAccountPassword()")
|
||||||
|
_, err := s.backend.AccountManager().VerifyAccountPassword(config.KeyStoreDir, "0x0", "bar")
|
||||||
|
s.T().Logf("VerifyAccountPassword(), err: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("Logout()")
|
||||||
|
s.T().Logf("Logout(), result: %v", s.backend.AccountManager().Logout())
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("IsNodeRunning()")
|
||||||
|
s.T().Logf("IsNodeRunning(), result: %v", s.backend.IsNodeRunning())
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("CompleteTransaction()")
|
||||||
|
_, err := s.backend.CompleteTransaction("id", "password")
|
||||||
|
s.T().Logf("CompleteTransaction(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("DiscardTransaction()")
|
||||||
|
s.T().Logf("DiscardTransaction(), error: %v", s.backend.DiscardTransaction("id"))
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("CompleteTransactions()")
|
||||||
|
s.T().Logf("CompleteTransactions(), result: %v", s.backend.CompleteTransactions(`["id1","id2"]`, "password"))
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("DiscardTransactions()")
|
||||||
|
s.T().Logf("DiscardTransactions(), result: %v", s.backend.DiscardTransactions(`["id1","id2"]`))
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// increase StartNode()/StopNode() population
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
funcsToTest = append(funcsToTest, funcsToTest[0], funcsToTest[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < cnt; i++ {
|
||||||
|
randConfig := nodeConfigs[rnd.Intn(len(nodeConfigs))]
|
||||||
|
randFunc := funcsToTest[rnd.Intn(len(funcsToTest))]
|
||||||
|
|
||||||
|
if rnd.Intn(100) > 75 { // introduce random delays
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
go randFunc(randConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
for range progress {
|
||||||
|
cnt -= 1
|
||||||
|
if cnt <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second) // so that we see some logs
|
||||||
|
nodeStopped, _ := s.backend.StopNode() // just in case we have a node running
|
||||||
|
if nodeStopped != nil {
|
||||||
|
<-nodeStopped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *BackendTestSuite) TestNetworkSwitching() {
|
func (s *BackendTestSuite) TestNetworkSwitching() {
|
||||||
require := s.Require()
|
require := s.Require()
|
||||||
require.NotNil(s.backend)
|
require.NotNil(s.backend)
|
||||||
|
@ -157,11 +397,12 @@ func (s *BackendTestSuite) TestNetworkSwitching() {
|
||||||
<-nodeStarted // wait till node is started
|
<-nodeStarted // wait till node is started
|
||||||
require.True(s.backend.IsNodeRunning())
|
require.True(s.backend.IsNodeRunning())
|
||||||
|
|
||||||
s.FirstBlockHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
FirstBlockHash(require, s.backend.NodeManager(), "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||||
|
|
||||||
// now stop node, and make sure that a new node, on different network can be started
|
// now stop node, and make sure that a new node, on different network can be started
|
||||||
err = s.backend.StopNode()
|
nodeStopped, err := s.backend.StopNode()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
<-nodeStopped
|
||||||
|
|
||||||
// start new node with completely different config
|
// start new node with completely different config
|
||||||
nodeConfig, err = MakeTestNodeConfig(params.RinkebyNetworkID)
|
nodeConfig, err = MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
@ -175,9 +416,11 @@ func (s *BackendTestSuite) TestNetworkSwitching() {
|
||||||
require.True(s.backend.IsNodeRunning())
|
require.True(s.backend.IsNodeRunning())
|
||||||
|
|
||||||
// make sure we are on another network indeed
|
// make sure we are on another network indeed
|
||||||
s.FirstBlockHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
|
|
||||||
require.NoError(s.backend.StopNode())
|
nodeStopped, err = s.backend.StopNode()
|
||||||
|
require.NoError(err)
|
||||||
|
<-nodeStopped
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BackendTestSuite) TestResetChainData() {
|
func (s *BackendTestSuite) TestResetChainData() {
|
||||||
|
@ -196,7 +439,7 @@ func (s *BackendTestSuite) TestResetChainData() {
|
||||||
s.True(s.backend.IsNodeRunning()) // new node, with previous config should be running
|
s.True(s.backend.IsNodeRunning()) // new node, with previous config should be running
|
||||||
|
|
||||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||||
s.FirstBlockHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BackendTestSuite) TestRestartNode() {
|
func (s *BackendTestSuite) TestRestartNode() {
|
||||||
|
@ -206,7 +449,7 @@ func (s *BackendTestSuite) TestRestartNode() {
|
||||||
s.StartTestBackend(params.RinkebyNetworkID)
|
s.StartTestBackend(params.RinkebyNetworkID)
|
||||||
defer s.StopTestBackend()
|
defer s.StopTestBackend()
|
||||||
|
|
||||||
s.FirstBlockHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
|
|
||||||
s.True(s.backend.IsNodeRunning())
|
s.True(s.backend.IsNodeRunning())
|
||||||
nodeRestarted, err := s.backend.RestartNode()
|
nodeRestarted, err := s.backend.RestartNode()
|
||||||
|
@ -215,5 +458,5 @@ func (s *BackendTestSuite) TestRestartNode() {
|
||||||
s.True(s.backend.IsNodeRunning()) // new node, with previous config should be running
|
s.True(s.backend.IsNodeRunning()) // new node, with previous config should be running
|
||||||
|
|
||||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||||
s.FirstBlockHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
||||||
require.NotNil(backend)
|
require.NotNil(backend)
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
sampleAddress, _, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
sampleAddress, _, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// make sure you panic if transaction complete doesn't return
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
@ -58,7 +58,7 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
||||||
|
|
||||||
// the second call will also fail (we are logged in as different user)
|
// the second call will also fail (we are logged in as different user)
|
||||||
log.Info("trying to complete with invalid user")
|
log.Info("trying to complete with invalid user")
|
||||||
err = s.backend.SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
err = s.backend.AccountManager().SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
||||||
s.EqualError(err, status.ErrInvalidCompleteTxSender.Error(),
|
s.EqualError(err, status.ErrInvalidCompleteTxSender.Error(),
|
||||||
|
@ -66,7 +66,7 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
||||||
|
|
||||||
// the third call will work as expected (as we are logged in with correct credentials)
|
// 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 suceed")
|
log.Info("trying to complete with correct user, this should suceed")
|
||||||
s.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
||||||
s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"]))
|
s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"]))
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
||||||
require.NotNil(backend)
|
require.NotNil(backend)
|
||||||
|
|
||||||
// create an account
|
// create an account
|
||||||
sampleAddress, _, _, err := s.backend.CreateAccount(TestConfig.Account1.Password)
|
sampleAddress, _, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// make sure you panic if transaction complete doesn't return
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
@ -136,7 +136,7 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
||||||
|
|
||||||
// the second call will also fail (we are logged in as different user)
|
// the second call will also fail (we are logged in as different user)
|
||||||
log.Info("trying to complete with invalid user")
|
log.Info("trying to complete with invalid user")
|
||||||
err = s.backend.SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
err = s.backend.AccountManager().SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
||||||
s.EqualError(err, status.ErrInvalidCompleteTxSender.Error(),
|
s.EqualError(err, status.ErrInvalidCompleteTxSender.Error(),
|
||||||
|
@ -144,7 +144,7 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
||||||
|
|
||||||
// the third call will work as expected (as we are logged in with correct credentials)
|
// 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 suceed")
|
log.Info("trying to complete with correct user, this should suceed")
|
||||||
s.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
txHash, err = s.backend.CompleteTransaction(event["id"].(string), TestConfig.Account1.Password)
|
||||||
s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"]))
|
s.NoError(err, fmt.Sprintf("cannot complete queued transaction[%v]", event["id"]))
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ func (s *BackendTestSuite) TestDoubleCompleteQueuedTransactions() {
|
||||||
require.NotNil(backend)
|
require.NotNil(backend)
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
// make sure you panic if transaction complete doesn't return
|
// make sure you panic if transaction complete doesn't return
|
||||||
completeQueuedTransaction := make(chan struct{}, 1)
|
completeQueuedTransaction := make(chan struct{}, 1)
|
||||||
|
@ -265,7 +265,7 @@ func (s *BackendTestSuite) TestDiscardQueuedTransaction() {
|
||||||
backend.TransactionQueue().Reset()
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
// make sure you panic if transaction complete doesn't return
|
// make sure you panic if transaction complete doesn't return
|
||||||
completeQueuedTransaction := make(chan struct{}, 1)
|
completeQueuedTransaction := make(chan struct{}, 1)
|
||||||
|
@ -346,7 +346,7 @@ func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
||||||
backend.TransactionQueue().Reset()
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
// make sure you panic if transaction complete doesn't return
|
// make sure you panic if transaction complete doesn't return
|
||||||
testTxCount := 3
|
testTxCount := 3
|
||||||
|
@ -461,7 +461,7 @@ func (s *BackendTestSuite) TestDiscardMultipleQueuedTransactions() {
|
||||||
backend.TransactionQueue().Reset()
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
// make sure you panic if transaction complete doesn't return
|
// make sure you panic if transaction complete doesn't return
|
||||||
testTxCount := 3
|
testTxCount := 3
|
||||||
|
@ -594,7 +594,7 @@ func (s *BackendTestSuite) TestNonExistentQueuedTransactions() {
|
||||||
require.NotNil(backend)
|
require.NotNil(backend)
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
// replace transaction notification handler
|
// replace transaction notification handler
|
||||||
node.SetDefaultNodeNotificationHandler(func(string) {})
|
node.SetDefaultNodeNotificationHandler(func(string) {})
|
||||||
|
@ -619,13 +619,13 @@ func (s *BackendTestSuite) TestEvictionOfQueuedTransactions() {
|
||||||
backend.TransactionQueue().Reset()
|
backend.TransactionQueue().Reset()
|
||||||
|
|
||||||
// log into account from which transactions will be sent
|
// log into account from which transactions will be sent
|
||||||
require.NoError(s.backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||||
|
|
||||||
txQueue := backend.TransactionQueue()
|
txQueue := backend.TransactionQueue()
|
||||||
var i = 0
|
var i = 0
|
||||||
txIDs := [status.DefaultTxQueueCap + 5 + 10]status.QueuedTxID{}
|
txIDs := [status.DefaultTxQueueCap + 5 + 10]status.QueuedTxID{}
|
||||||
backend.SetTransactionQueueHandler(func(queuedTx status.QueuedTx) {
|
backend.SetTransactionQueueHandler(func(queuedTx status.QueuedTx) {
|
||||||
log.Info("tx enqueued", "i", i + 1, "queue size", txQueue.Count(), "id", queuedTx.ID)
|
log.Info("tx enqueued", "i", i+1, "queue size", txQueue.Count(), "id", queuedTx.ID)
|
||||||
txIDs[i] = queuedTx.ID
|
txIDs[i] = queuedTx.ID
|
||||||
i++
|
i++
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/status-im/status-go/geth/params"
|
|
||||||
"github.com/status-im/status-go/geth/common"
|
"github.com/status-im/status-go/geth/common"
|
||||||
|
"github.com/status-im/status-go/geth/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLogger(t *testing.T) {
|
func TestLogger(t *testing.T) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/status-im/status-go/static"
|
"github.com/status-im/status-go/static"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// errors
|
||||||
var (
|
var (
|
||||||
ErrDeprecatedMethod = errors.New("Method is depricated and will be removed in future release")
|
ErrDeprecatedMethod = errors.New("Method is depricated and will be removed in future release")
|
||||||
)
|
)
|
||||||
|
@ -45,7 +46,7 @@ type NodeManager interface {
|
||||||
|
|
||||||
// StopNode stop the running Status node.
|
// StopNode stop the running Status node.
|
||||||
// Stopped node cannot be resumed, one starts a new node instead.
|
// Stopped node cannot be resumed, one starts a new node instead.
|
||||||
StopNode() error
|
StopNode() (<-chan struct{}, error)
|
||||||
|
|
||||||
// RestartNode restart running Status node, fails if node is not running
|
// RestartNode restart running Status node, fails if node is not running
|
||||||
RestartNode() (<-chan struct{}, error)
|
RestartNode() (<-chan struct{}, error)
|
||||||
|
@ -83,6 +84,9 @@ type NodeManager interface {
|
||||||
|
|
||||||
// RPCClient exposes reference to RPC client connected to the running node
|
// RPCClient exposes reference to RPC client connected to the running node
|
||||||
RPCClient() (*rpc.Client, error)
|
RPCClient() (*rpc.Client, error)
|
||||||
|
|
||||||
|
// RPCServer exposes reference to running node's in-proc RPC server/handler
|
||||||
|
RPCServer() (*rpc.Server, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountManager defines expected methods for managing Status accounts
|
// AccountManager defines expected methods for managing Status accounts
|
||||||
|
@ -129,6 +133,12 @@ type AccountManager interface {
|
||||||
AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error)
|
AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RPCManager defines expected methods for managing RPC client/server
|
||||||
|
type RPCManager interface {
|
||||||
|
// Call executes RPC request on node's in-proc RPC server
|
||||||
|
Call(inputJSON string) string
|
||||||
|
}
|
||||||
|
|
||||||
// RawCompleteTransactionResult is a JSON returned from transaction complete function (used internally)
|
// RawCompleteTransactionResult is a JSON returned from transaction complete function (used internally)
|
||||||
type RawCompleteTransactionResult struct {
|
type RawCompleteTransactionResult struct {
|
||||||
Hash common.Hash
|
Hash common.Hash
|
||||||
|
|
|
@ -4,10 +4,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
@ -17,22 +15,20 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||||
"github.com/status-im/status-go/geth/common"
|
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
var (
|
var (
|
||||||
ErrNodeAlreadyExists = errors.New("there is a running node already, stop it before starting another one")
|
ErrNodeExists = errors.New("node is already running")
|
||||||
ErrNoRunningNode = errors.New("there is no running node")
|
ErrNoRunningNode = errors.New("there is no running node")
|
||||||
ErrNodeOpTimedOut = errors.New("operation takes too long, timed out")
|
|
||||||
ErrInvalidRunningNode = errors.New("running node is not correctly initialized")
|
|
||||||
ErrInvalidNodeManager = errors.New("node manager is not properly initialized")
|
ErrInvalidNodeManager = errors.New("node manager is not properly initialized")
|
||||||
ErrInvalidWhisperService = errors.New("whisper service is unavailable")
|
ErrInvalidWhisperService = errors.New("whisper service is unavailable")
|
||||||
ErrInvalidLightEthereumService = errors.New("LES service is unavailable")
|
ErrInvalidLightEthereumService = errors.New("LES service is unavailable")
|
||||||
ErrInvalidAccountManager = errors.New("could not retrieve account manager")
|
ErrInvalidAccountManager = errors.New("could not retrieve account manager")
|
||||||
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
||||||
ErrInvalidRPCClient = errors.New("RPC service is unavailable")
|
ErrInvalidRPCClient = errors.New("RPC client is unavailable")
|
||||||
|
ErrInvalidRPCServer = errors.New("RPC server is unavailable")
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeManager manages Status node (which abstracts contained geth node)
|
// NodeManager manages Status node (which abstracts contained geth node)
|
||||||
|
@ -40,35 +36,18 @@ type NodeManager struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
config *params.NodeConfig // Status node configuration
|
config *params.NodeConfig // Status node configuration
|
||||||
node *node.Node // reference to Geth P2P stack/node
|
node *node.Node // reference to Geth P2P stack/node
|
||||||
|
nodeStarted chan struct{} // channel to wait for start up notifications
|
||||||
nodeStopped chan struct{} // channel to wait for termination notifications
|
nodeStopped chan struct{} // channel to wait for termination notifications
|
||||||
whisperService *whisper.Whisper // reference to Whisper service
|
whisperService *whisper.Whisper // reference to Whisper service
|
||||||
lesService *les.LightEthereum // reference to LES service
|
lesService *les.LightEthereum // reference to LES service
|
||||||
rpcClient *rpc.Client // reference to RPC client
|
rpcClient *rpc.Client // reference to RPC client
|
||||||
|
rpcServer *rpc.Server // reference to RPC server
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNodeManager makes new instance of node manager
|
// NewNodeManager makes new instance of node manager
|
||||||
func NewNodeManager() *NodeManager {
|
func NewNodeManager() *NodeManager {
|
||||||
m := &NodeManager{}
|
m := &NodeManager{}
|
||||||
|
go HaltOnInterruptSignal(m) // allow interrupting running nodes
|
||||||
// allow interrupting running nodes
|
|
||||||
go func() {
|
|
||||||
sigc := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigc, os.Interrupt)
|
|
||||||
defer signal.Stop(sigc)
|
|
||||||
<-sigc
|
|
||||||
if m.node == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("Got interrupt, shutting down...")
|
|
||||||
go m.node.Stop() // nolint: errcheck
|
|
||||||
for i := 3; i > 0; i-- {
|
|
||||||
<-sigc
|
|
||||||
if i > 1 {
|
|
||||||
log.Info(fmt.Sprintf("Already shutting down, interrupt %d more times for panic.", i-1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("interrupted!")
|
|
||||||
}()
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -87,30 +66,23 @@ func (m *NodeManager) StartNode(config *params.NodeConfig) (<-chan struct{}, err
|
||||||
|
|
||||||
// startNode start Status node, fails if node is already started
|
// startNode start Status node, fails if node is already started
|
||||||
func (m *NodeManager) startNode(config *params.NodeConfig) (<-chan struct{}, error) {
|
func (m *NodeManager) startNode(config *params.NodeConfig) (<-chan struct{}, error) {
|
||||||
if m.node != nil || m.nodeStopped != nil {
|
if m.node != nil || m.nodeStarted != nil {
|
||||||
return nil, ErrNodeAlreadyExists
|
return nil, ErrNodeExists
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
ethNode, err := MakeNode(config)
|
||||||
m.node, err = MakeNode(config)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.config = config // preserve config of successfully created node
|
m.nodeStarted = make(chan struct{}, 1)
|
||||||
|
|
||||||
nodeStarted := make(chan struct{})
|
|
||||||
m.nodeStopped = make(chan struct{})
|
|
||||||
go func() {
|
go func() {
|
||||||
defer HaltOnPanic()
|
defer HaltOnPanic()
|
||||||
|
|
||||||
if err := m.node.Start(); err != nil {
|
// start underlying node
|
||||||
m.Lock() // TODO potential deadlock (add test case to prove otherwise)
|
if err := ethNode.Start(); err != nil {
|
||||||
m.config = nil
|
close(m.nodeStarted)
|
||||||
m.lesService = nil
|
m.Lock()
|
||||||
m.whisperService = nil
|
m.nodeStarted = nil
|
||||||
m.rpcClient = nil
|
|
||||||
m.nodeStopped = nil
|
|
||||||
m.node = nil
|
|
||||||
m.Unlock()
|
m.Unlock()
|
||||||
SendSignal(SignalEnvelope{
|
SendSignal(SignalEnvelope{
|
||||||
Type: EventNodeCrashed,
|
Type: EventNodeCrashed,
|
||||||
|
@ -118,48 +90,92 @@ func (m *NodeManager) startNode(config *params.NodeConfig) (<-chan struct{}, err
|
||||||
Error: fmt.Errorf("%v: %v", ErrNodeStartFailure, err).Error(),
|
Error: fmt.Errorf("%v: %v", ErrNodeStartFailure, err).Error(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
close(nodeStarted)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// node is ready, use it
|
m.Lock()
|
||||||
m.onNodeStarted(nodeStarted)
|
m.node = ethNode
|
||||||
|
m.nodeStopped = make(chan struct{}, 1)
|
||||||
|
m.config = config
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
// underlying node is started, every method can use it, we use it immediately
|
||||||
|
go func() {
|
||||||
|
if err := m.PopulateStaticPeers(); err != nil {
|
||||||
|
log.Error("Static peers population", "error", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// notify all subscribers that Status node is started
|
||||||
|
close(m.nodeStarted)
|
||||||
|
SendSignal(SignalEnvelope{
|
||||||
|
Type: EventNodeStarted,
|
||||||
|
Event: struct{}{},
|
||||||
|
})
|
||||||
|
|
||||||
|
// wait up until underlying node is stopped
|
||||||
|
m.node.Wait()
|
||||||
|
|
||||||
|
// notify m.Stop() that node has been stopped
|
||||||
|
close(m.nodeStopped)
|
||||||
|
log.Info("Node is stopped")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nodeStarted, nil
|
return m.nodeStarted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// onNodeStarted extra processing once we have running node
|
// StopNode stop Status node. Stopped node cannot be resumed.
|
||||||
func (m *NodeManager) onNodeStarted(nodeStarted chan struct{}) {
|
func (m *NodeManager) StopNode() (<-chan struct{}, error) {
|
||||||
// post-start processing
|
if m == nil {
|
||||||
if err := m.populateStaticPeers(); err != nil {
|
return nil, ErrInvalidNodeManager
|
||||||
log.Error("Static peers population", "error", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// obtain node info
|
m.Lock()
|
||||||
enode := "none"
|
defer m.Unlock()
|
||||||
if server := m.node.Server(); server != nil {
|
|
||||||
if nodeInfo := server.NodeInfo(); nodeInfo != nil {
|
return m.stopNode()
|
||||||
enode = nodeInfo.Enode
|
}
|
||||||
log.Info("Node is ready", "enode", enode)
|
|
||||||
}
|
// stopNode stop Status node. Stopped node cannot be resumed.
|
||||||
|
func (m *NodeManager) stopNode() (<-chan struct{}, error) {
|
||||||
|
if m.node == nil || m.nodeStarted == nil || m.nodeStopped == nil {
|
||||||
|
return nil, ErrNoRunningNode
|
||||||
|
}
|
||||||
|
<-m.nodeStarted // make sure you operate on fully started node
|
||||||
|
|
||||||
|
// now attempt to stop
|
||||||
|
if err := m.node.Stop(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify all subscribers that node is started
|
nodeStopped := make(chan struct{}, 1)
|
||||||
SendSignal(SignalEnvelope{
|
go func() {
|
||||||
Type: EventNodeStarted,
|
<-m.nodeStopped // Status node is stopped (code after Wait() is executed)
|
||||||
Event: struct{}{},
|
log.Info("Ready to reset node")
|
||||||
})
|
|
||||||
close(nodeStarted)
|
|
||||||
|
|
||||||
// wait up until node is stopped
|
// reset node params
|
||||||
m.node.Wait()
|
m.Lock()
|
||||||
SendSignal(SignalEnvelope{
|
m.config = nil
|
||||||
Type: EventNodeStopped,
|
m.lesService = nil
|
||||||
Event: struct{}{},
|
m.whisperService = nil
|
||||||
})
|
m.rpcClient = nil
|
||||||
close(m.nodeStopped)
|
m.rpcServer = nil
|
||||||
log.Info("Node is stopped", "enode", enode)
|
m.nodeStarted = nil
|
||||||
|
m.node = nil
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
close(nodeStopped) // Status node is stopped, and we can create another
|
||||||
|
log.Info("Node manager resets node params")
|
||||||
|
|
||||||
|
// notify application that it can send more requests now
|
||||||
|
SendSignal(SignalEnvelope{
|
||||||
|
Type: EventNodeStopped,
|
||||||
|
Event: struct{}{},
|
||||||
|
})
|
||||||
|
log.Info("Node manager notifed app, that node has stopped")
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nodeStopped, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeRunning confirm that node is running
|
// IsNodeRunning confirm that node is running
|
||||||
|
@ -171,50 +187,13 @@ func (m *NodeManager) IsNodeRunning() bool {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
return m.node != nil && m.nodeStopped != nil
|
// make sure that node is fully started
|
||||||
}
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
|
return false
|
||||||
// StopNode stop Status node. Stopped node cannot be resumed.
|
|
||||||
func (m *NodeManager) StopNode() error {
|
|
||||||
if m == nil {
|
|
||||||
return ErrInvalidNodeManager
|
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
m.Lock()
|
return true
|
||||||
defer m.Unlock()
|
|
||||||
|
|
||||||
return m.stopNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopNode stop Status node. Stopped node cannot be resumed.
|
|
||||||
func (m *NodeManager) stopNode() error {
|
|
||||||
if m.node == nil {
|
|
||||||
return ErrNoRunningNode
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.nodeStopped == nil { // node may be running, but required channel not set
|
|
||||||
return ErrInvalidRunningNode
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.node.Stop(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait till the previous node is fully stopped
|
|
||||||
select {
|
|
||||||
case <-m.nodeStopped:
|
|
||||||
// pass
|
|
||||||
case <-time.After(30 * time.Second):
|
|
||||||
return fmt.Errorf("%v: %s", ErrNodeOpTimedOut, common.NameOf(m.StopNode))
|
|
||||||
}
|
|
||||||
|
|
||||||
m.config = nil
|
|
||||||
m.lesService = nil
|
|
||||||
m.whisperService = nil
|
|
||||||
m.rpcClient = nil
|
|
||||||
m.nodeStopped = nil
|
|
||||||
m.node = nil
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node returns underlying Status node
|
// Node returns underlying Status node
|
||||||
|
@ -226,9 +205,11 @@ func (m *NodeManager) Node() (*node.Node, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
return m.node, nil
|
return m.node, nil
|
||||||
}
|
}
|
||||||
|
@ -247,9 +228,11 @@ func (m *NodeManager) PopulateStaticPeers() error {
|
||||||
|
|
||||||
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
||||||
func (m *NodeManager) populateStaticPeers() error {
|
func (m *NodeManager) populateStaticPeers() error {
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return ErrNoRunningNode
|
return ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
if !m.config.BootClusterConfig.Enabled {
|
if !m.config.BootClusterConfig.Enabled {
|
||||||
log.Info("Boot cluster is disabled")
|
log.Info("Boot cluster is disabled")
|
||||||
|
@ -286,9 +269,11 @@ func (m *NodeManager) AddPeer(url string) error {
|
||||||
|
|
||||||
// addPeer adds new static peer node
|
// addPeer adds new static peer node
|
||||||
func (m *NodeManager) addPeer(url string) error {
|
func (m *NodeManager) addPeer(url string) error {
|
||||||
if m == nil || m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return ErrNoRunningNode
|
return ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
server := m.node.Server()
|
server := m.node.Server()
|
||||||
if server == nil {
|
if server == nil {
|
||||||
|
@ -315,15 +300,28 @@ func (m *NodeManager) ResetChainData() (<-chan struct{}, error) {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if m.node == nil {
|
return m.resetChainData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetChainData remove chain data from data directory.
|
||||||
|
// Node is stopped, and new node is started, with clean data directory.
|
||||||
|
func (m *NodeManager) resetChainData() (<-chan struct{}, error) {
|
||||||
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
prevConfig := *m.config
|
prevConfig := *m.config
|
||||||
if err := m.stopNode(); err != nil {
|
nodeStopped, err := m.stopNode()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.Unlock()
|
||||||
|
<-nodeStopped
|
||||||
|
m.Lock()
|
||||||
|
|
||||||
chainDataDir := filepath.Join(prevConfig.DataDir, prevConfig.Name, "lightchaindata")
|
chainDataDir := filepath.Join(prevConfig.DataDir, prevConfig.Name, "lightchaindata")
|
||||||
if _, err := os.Stat(chainDataDir); os.IsNotExist(err) {
|
if _, err := os.Stat(chainDataDir); os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -336,7 +334,7 @@ func (m *NodeManager) ResetChainData() (<-chan struct{}, error) {
|
||||||
Type: EventChainDataRemoved,
|
Type: EventChainDataRemoved,
|
||||||
Event: struct{}{},
|
Event: struct{}{},
|
||||||
})
|
})
|
||||||
log.Info("chaindata removed", "dir", chainDataDir)
|
log.Info("Chain data has been removed", "dir", chainDataDir)
|
||||||
|
|
||||||
return m.startNode(&prevConfig)
|
return m.startNode(&prevConfig)
|
||||||
}
|
}
|
||||||
|
@ -350,15 +348,27 @@ func (m *NodeManager) RestartNode() (<-chan struct{}, error) {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if m.node == nil {
|
return m.restartNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// restartNode restart running Status node, fails if node is not running
|
||||||
|
func (m *NodeManager) restartNode() (<-chan struct{}, error) {
|
||||||
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
prevConfig := *m.config
|
prevConfig := *m.config
|
||||||
if err := m.stopNode(); err != nil {
|
nodeStopped, err := m.stopNode()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.Unlock()
|
||||||
|
<-nodeStopped
|
||||||
|
m.Lock()
|
||||||
|
|
||||||
return m.startNode(&prevConfig)
|
return m.startNode(&prevConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,9 +381,11 @@ func (m *NodeManager) NodeConfig() (*params.NodeConfig, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
return m.config, nil
|
return m.config, nil
|
||||||
}
|
}
|
||||||
|
@ -387,9 +399,11 @@ func (m *NodeManager) LightEthereumService() (*les.LightEthereum, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
if m.lesService == nil {
|
if m.lesService == nil {
|
||||||
if err := m.node.Service(&m.lesService); err != nil {
|
if err := m.node.Service(&m.lesService); err != nil {
|
||||||
|
@ -414,9 +428,11 @@ func (m *NodeManager) WhisperService() (*whisper.Whisper, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
if m.whisperService == nil {
|
if m.whisperService == nil {
|
||||||
if err := m.node.Service(&m.whisperService); err != nil {
|
if err := m.node.Service(&m.whisperService); err != nil {
|
||||||
|
@ -441,9 +457,11 @@ func (m *NodeManager) AccountManager() (*accounts.Manager, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
accountManager := m.node.AccountManager()
|
accountManager := m.node.AccountManager()
|
||||||
if accountManager == nil {
|
if accountManager == nil {
|
||||||
|
@ -462,9 +480,11 @@ func (m *NodeManager) AccountKeyStore() (*keystore.KeyStore, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
accountManager := m.node.AccountManager()
|
accountManager := m.node.AccountManager()
|
||||||
if accountManager == nil {
|
if accountManager == nil {
|
||||||
|
@ -493,9 +513,11 @@ func (m *NodeManager) RPCClient() (*rpc.Client, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
if m.node == nil {
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
return nil, ErrNoRunningNode
|
return nil, ErrNoRunningNode
|
||||||
}
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
if m.rpcClient == nil {
|
if m.rpcClient == nil {
|
||||||
var err error
|
var err error
|
||||||
|
@ -512,3 +534,34 @@ func (m *NodeManager) RPCClient() (*rpc.Client, error) {
|
||||||
|
|
||||||
return m.rpcClient, nil
|
return m.rpcClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RPCServer exposes reference to running node's in-proc RPC server/handler
|
||||||
|
func (m *NodeManager) RPCServer() (*rpc.Server, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, ErrInvalidNodeManager
|
||||||
|
}
|
||||||
|
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
|
||||||
|
// make sure that node is fully started
|
||||||
|
if m.node == nil || m.nodeStarted == nil {
|
||||||
|
return nil, ErrNoRunningNode
|
||||||
|
}
|
||||||
|
<-m.nodeStarted
|
||||||
|
|
||||||
|
if m.rpcServer == nil {
|
||||||
|
var err error
|
||||||
|
m.rpcServer, err = m.node.InProcRPC()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Cannot expose on-proc RPC server", "error", err)
|
||||||
|
return nil, ErrInvalidRPCServer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.rpcServer == nil {
|
||||||
|
return nil, ErrInvalidRPCServer
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.rpcServer, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package node_test
|
package node_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
gethnode "github.com/ethereum/go-ethereum/node"
|
gethnode "github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||||
|
@ -30,7 +34,7 @@ func (s *ManagerTestSuite) SetupTest() {
|
||||||
s.Require().IsType(&node.NodeManager{}, s.NodeManager)
|
s.Require().IsType(&node.NodeManager{}, s.NodeManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ManagerTestSuite) TestGettingReferencedServices() {
|
func (s *ManagerTestSuite) TestReferences() {
|
||||||
s.Require().NotNil(s.NodeManager)
|
s.Require().NotNil(s.NodeManager)
|
||||||
|
|
||||||
var nilNodeManager *node.NodeManager
|
var nilNodeManager *node.NodeManager
|
||||||
|
@ -41,6 +45,60 @@ func (s *ManagerTestSuite) TestGettingReferencedServices() {
|
||||||
initFn func() (interface{}, error)
|
initFn func() (interface{}, error)
|
||||||
expectedErr error
|
expectedErr error
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
"null manager, StartNode()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nilNodeManager.StartNode(nil)
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null manager, StopNode()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nilNodeManager.StopNode()
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null manager, RestartNode()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nilNodeManager.RestartNode()
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null manager, ResetChainData()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nilNodeManager.ResetChainData()
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null manager, IsNodeRunning()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
result := nilNodeManager.IsNodeRunning()
|
||||||
|
var err error
|
||||||
|
if !result {
|
||||||
|
err = node.ErrInvalidNodeManager
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null manager, PopulateStaticPeers()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nil, nilNodeManager.PopulateStaticPeers()
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null manager, AddPeer()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nil, nilNodeManager.AddPeer("enode://da3bf389a031f33fb55c9f5f54fde8473912402d27fffaa50efd74c0d0515f3a61daf6d52151f2876b19c15828e6f670352bff432b5ec457652e74755e8c864f@51.15.62.116:30303")
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"null manager, get NodeConfig",
|
"null manager, get NodeConfig",
|
||||||
func() (interface{}, error) {
|
func() (interface{}, error) {
|
||||||
|
@ -90,6 +148,41 @@ func (s *ManagerTestSuite) TestGettingReferencedServices() {
|
||||||
},
|
},
|
||||||
node.ErrInvalidNodeManager,
|
node.ErrInvalidNodeManager,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"null manager, get RPC Server",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nilNodeManager.RPCServer()
|
||||||
|
},
|
||||||
|
node.ErrInvalidNodeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"non-null manager, no running node, RestartNode()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return s.NodeManager.RestartNode()
|
||||||
|
},
|
||||||
|
node.ErrNoRunningNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"non-null manager, no running node, ResetChainData()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return s.NodeManager.ResetChainData()
|
||||||
|
},
|
||||||
|
node.ErrNoRunningNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"non-null manager, no running node, PopulateStaticPeers()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nil, s.NodeManager.PopulateStaticPeers()
|
||||||
|
},
|
||||||
|
node.ErrNoRunningNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"non-null manager, no running node, AddPeer()",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return nil, s.NodeManager.AddPeer("enode://da3bf389a031f33fb55c9f5f54fde8473912402d27fffaa50efd74c0d0515f3a61daf6d52151f2876b19c15828e6f670352bff432b5ec457652e74755e8c864f@51.15.62.116:30303")
|
||||||
|
},
|
||||||
|
node.ErrNoRunningNode,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"non-null manager, no running node, get NodeConfig",
|
"non-null manager, no running node, get NodeConfig",
|
||||||
func() (interface{}, error) {
|
func() (interface{}, error) {
|
||||||
|
@ -139,6 +232,13 @@ func (s *ManagerTestSuite) TestGettingReferencedServices() {
|
||||||
},
|
},
|
||||||
node.ErrNoRunningNode,
|
node.ErrNoRunningNode,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"non-null manager, no running node, get RPC Server",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return s.NodeManager.RPCServer()
|
||||||
|
},
|
||||||
|
node.ErrNoRunningNode,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range noNodeTests {
|
for _, testCase := range noNodeTests {
|
||||||
s.T().Log(testCase.name)
|
s.T().Log(testCase.name)
|
||||||
|
@ -204,6 +304,13 @@ func (s *ManagerTestSuite) TestGettingReferencedServices() {
|
||||||
},
|
},
|
||||||
&rpc.Client{},
|
&rpc.Client{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"node is running, get RPC Server",
|
||||||
|
func() (interface{}, error) {
|
||||||
|
return s.NodeManager.RPCServer()
|
||||||
|
},
|
||||||
|
&rpc.Server{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range nodeReadyTestCases {
|
for _, testCase := range nodeReadyTestCases {
|
||||||
obj, err := testCase.initFn()
|
obj, err := testCase.initFn()
|
||||||
|
@ -223,10 +330,8 @@ func (s *ManagerTestSuite) TestNodeStartStop() {
|
||||||
|
|
||||||
// try stopping non-started node
|
// try stopping non-started node
|
||||||
require.False(s.NodeManager.IsNodeRunning())
|
require.False(s.NodeManager.IsNodeRunning())
|
||||||
err = s.NodeManager.StopNode()
|
_, err = s.NodeManager.StopNode()
|
||||||
if s.Error(err) {
|
require.EqualError(err, node.ErrNoRunningNode.Error())
|
||||||
require.IsType(node.ErrNoRunningNode, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
require.False(s.NodeManager.IsNodeRunning())
|
require.False(s.NodeManager.IsNodeRunning())
|
||||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||||
|
@ -237,13 +342,12 @@ func (s *ManagerTestSuite) TestNodeStartStop() {
|
||||||
|
|
||||||
// try starting another node (w/o stopping the previously started node)
|
// try starting another node (w/o stopping the previously started node)
|
||||||
_, err = s.NodeManager.StartNode(nodeConfig)
|
_, err = s.NodeManager.StartNode(nodeConfig)
|
||||||
if s.Error(err) {
|
require.EqualError(err, node.ErrNodeExists.Error())
|
||||||
require.IsType(node.ErrNodeAlreadyExists, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// now stop node, and make sure that a new node, on different network can be started
|
// now stop node, and make sure that a new node, on different network can be started
|
||||||
err = s.NodeManager.StopNode()
|
nodeStopped, err := s.NodeManager.StopNode()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
<-nodeStopped
|
||||||
|
|
||||||
// start new node with exactly the same config
|
// start new node with exactly the same config
|
||||||
require.False(s.NodeManager.IsNodeRunning())
|
require.False(s.NodeManager.IsNodeRunning())
|
||||||
|
@ -271,11 +375,12 @@ func (s *ManagerTestSuite) TestNetworkSwitching() {
|
||||||
<-nodeStarted // wait till node is started
|
<-nodeStarted // wait till node is started
|
||||||
require.True(s.NodeManager.IsNodeRunning())
|
require.True(s.NodeManager.IsNodeRunning())
|
||||||
|
|
||||||
s.FirstBlockHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
FirstBlockHash(require, s.NodeManager, "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||||
|
|
||||||
// now stop node, and make sure that a new node, on different network can be started
|
// now stop node, and make sure that a new node, on different network can be started
|
||||||
err = s.NodeManager.StopNode()
|
nodeStopped, err := s.NodeManager.StopNode()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
<-nodeStopped
|
||||||
|
|
||||||
// start new node with completely different config
|
// start new node with completely different config
|
||||||
nodeConfig, err = MakeTestNodeConfig(params.RinkebyNetworkID)
|
nodeConfig, err = MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
@ -289,7 +394,7 @@ func (s *ManagerTestSuite) TestNetworkSwitching() {
|
||||||
require.True(s.NodeManager.IsNodeRunning())
|
require.True(s.NodeManager.IsNodeRunning())
|
||||||
|
|
||||||
// make sure we are on another network indeed
|
// make sure we are on another network indeed
|
||||||
s.FirstBlockHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
FirstBlockHash(require, s.NodeManager, "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
|
|
||||||
s.StopTestNode()
|
s.StopTestNode()
|
||||||
}
|
}
|
||||||
|
@ -310,5 +415,201 @@ func (s *ManagerTestSuite) TestResetChainData() {
|
||||||
s.True(s.NodeManager.IsNodeRunning()) // new node, with previous config should be running
|
s.True(s.NodeManager.IsNodeRunning()) // new node, with previous config should be running
|
||||||
|
|
||||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||||
s.FirstBlockHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
FirstBlockHash(require, s.NodeManager, "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) TestRestartNode() {
|
||||||
|
require := s.Require()
|
||||||
|
require.NotNil(s.NodeManager)
|
||||||
|
|
||||||
|
s.StartTestNode(params.RinkebyNetworkID)
|
||||||
|
defer s.StopTestNode()
|
||||||
|
|
||||||
|
s.True(s.NodeManager.IsNodeRunning())
|
||||||
|
nodeReady, err := s.NodeManager.RestartNode()
|
||||||
|
require.NoError(err)
|
||||||
|
<-nodeReady
|
||||||
|
s.True(s.NodeManager.IsNodeRunning()) // new node, with previous config should be running
|
||||||
|
|
||||||
|
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||||
|
FirstBlockHash(require, s.NodeManager, "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) TestRaceConditions() {
|
||||||
|
require := s.Require()
|
||||||
|
require.NotNil(s.NodeManager)
|
||||||
|
|
||||||
|
cnt := 25
|
||||||
|
progress := make(chan struct{}, cnt)
|
||||||
|
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
nodeConfig1, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig2, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||||
|
|
||||||
|
var funcsToTest = []func(*params.NodeConfig){
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("StartNode()")
|
||||||
|
_, err := s.NodeManager.StartNode(config)
|
||||||
|
s.T().Logf("StartNode() for network: %d, error: %v", config.NetworkID, err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("StopNode()")
|
||||||
|
_, err := s.NodeManager.StopNode()
|
||||||
|
s.T().Logf("StopNode() for network: %d, error: %v", config.NetworkID, err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("Node()")
|
||||||
|
_, err := s.NodeManager.Node()
|
||||||
|
s.T().Logf("Node(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("IsNodeRunning()")
|
||||||
|
s.T().Logf("IsNodeRunning(), result: %v", s.NodeManager.IsNodeRunning())
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("PopulateStaticPeers()")
|
||||||
|
s.T().Logf("PopulateStaticPeers(), error: %v", s.NodeManager.PopulateStaticPeers())
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("ResetChainData()")
|
||||||
|
_, err := s.NodeManager.ResetChainData()
|
||||||
|
s.T().Logf("ResetChainData(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("RestartNode()")
|
||||||
|
_, err := s.NodeManager.RestartNode()
|
||||||
|
s.T().Logf("RestartNode(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("NodeConfig()")
|
||||||
|
_, err := s.NodeManager.NodeConfig()
|
||||||
|
s.T().Logf("NodeConfig(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("LightEthereumService()")
|
||||||
|
_, err := s.NodeManager.LightEthereumService()
|
||||||
|
s.T().Logf("LightEthereumService(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("WhisperService()")
|
||||||
|
_, err := s.NodeManager.WhisperService()
|
||||||
|
s.T().Logf("WhisperService(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("AccountManager()")
|
||||||
|
_, err := s.NodeManager.AccountManager()
|
||||||
|
s.T().Logf("AccountManager(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("AccountKeyStore()")
|
||||||
|
_, err := s.NodeManager.AccountKeyStore()
|
||||||
|
s.T().Logf("AccountKeyStore(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("RPCClient()")
|
||||||
|
_, err := s.NodeManager.RPCClient()
|
||||||
|
s.T().Logf("RPCClient(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
func(config *params.NodeConfig) {
|
||||||
|
log.Info("RPCServer()")
|
||||||
|
_, err := s.NodeManager.RPCServer()
|
||||||
|
s.T().Logf("RPCServer(), error: %v", err)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// increase StartNode()/StopNode() population
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
funcsToTest = append(funcsToTest, funcsToTest[0], funcsToTest[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < cnt; i++ {
|
||||||
|
randConfig := nodeConfigs[rnd.Intn(len(nodeConfigs))]
|
||||||
|
randFunc := funcsToTest[rnd.Intn(len(funcsToTest))]
|
||||||
|
|
||||||
|
if rnd.Intn(100) > 75 { // introduce random delays
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
go randFunc(randConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
for range progress {
|
||||||
|
cnt -= 1
|
||||||
|
if cnt <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second) // so that we see some logs
|
||||||
|
nodeStopped, _ := s.NodeManager.StopNode() // just in case we have a node running
|
||||||
|
if nodeStopped != nil {
|
||||||
|
<-nodeStopped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) TestNodeStartCrash() {
|
||||||
|
require := s.Require()
|
||||||
|
require.NotNil(s.NodeManager)
|
||||||
|
|
||||||
|
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// start node outside the manager (on the same port), so that manager node.Start() method fails
|
||||||
|
outsideNode, err := node.MakeNode(nodeConfig)
|
||||||
|
require.NoError(outsideNode.Start())
|
||||||
|
|
||||||
|
// let's listen for node.crashed signal
|
||||||
|
signalReceived := false
|
||||||
|
node.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
log.Info("Notification Received", "event", jsonEvent)
|
||||||
|
var envelope node.SignalEnvelope
|
||||||
|
err := json.Unmarshal([]byte(jsonEvent), &envelope)
|
||||||
|
s.NoError(err, fmt.Sprintf("cannot unmarshal JSON: %s", jsonEvent))
|
||||||
|
|
||||||
|
if envelope.Type == node.EventNodeCrashed {
|
||||||
|
signalReceived = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// now try starting using node manager
|
||||||
|
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||||
|
require.NoError(err) // no error is thrown, as node is started in separate routine
|
||||||
|
<-nodeStarted // no deadlock either, as manager should close the channel on error
|
||||||
|
require.False(s.NodeManager.IsNodeRunning())
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second) // allow signal to propagate
|
||||||
|
require.True(signalReceived, "node crash signal is expected")
|
||||||
|
|
||||||
|
// stop outside node, and re-try
|
||||||
|
require.NoError(outsideNode.Stop())
|
||||||
|
signalReceived = false
|
||||||
|
nodeStarted, err = s.NodeManager.StartNode(nodeConfig)
|
||||||
|
require.NoError(err) // again, no error
|
||||||
|
<-nodeStarted // no deadlock, and no signal this time, manager should be able to start node
|
||||||
|
require.True(s.NodeManager.IsNodeRunning())
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second) // allow signal to propagate
|
||||||
|
require.False(signalReceived, "node should start w/o crash signal")
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
s.NodeManager.StopNode()
|
||||||
|
node.ResetDefaultNodeNotificationHandler()
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ func updateCHT(eth *les.LightEthereum, config *params.NodeConfig) {
|
||||||
Dev string `json:"dev"`
|
Dev string `json:"dev"`
|
||||||
}
|
}
|
||||||
loadCHTLists := func() ([]MsgCHTRoot, error) {
|
loadCHTLists := func() ([]MsgCHTRoot, error) {
|
||||||
url := "https://gist.githubusercontent.com/farazdagi/a8d36e2818b3b2b6074d691da63a0c36/raw/?u=" + strconv.Itoa(int(time.Now().Unix()))
|
url := config.LightEthConfig.CHTRootConfigURL + "?u=" + strconv.Itoa(int(time.Now().Unix()))
|
||||||
client := &http.Client{Timeout: 5 * time.Second}
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
r, err := client.Get(url)
|
r, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -181,7 +181,7 @@ func updateCHT(eth *les.LightEthereum, config *params.NodeConfig) {
|
||||||
Number: root.Number,
|
Number: root.Number,
|
||||||
Root: gethcommon.HexToHash(chtRoot),
|
Root: gethcommon.HexToHash(chtRoot),
|
||||||
})
|
})
|
||||||
log.Info("Loaded CHT from net", "CHT", chtRoot, "number", root.Number)
|
log.Info("Loaded CHT from net", "CHT", chtRoot, "number", root.Number, "dev", config.DevMode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/les/status"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/status-im/status-go/geth/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
jsonrpcVersion = "2.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jsonRequest struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Payload json.RawMessage `json:"params,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonError struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data interface{} `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonErrResponse struct {
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
ID interface{} `json:"id,omitempty"`
|
||||||
|
Error jsonError `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCManager abstract RPC management API (for both client and server)
|
||||||
|
type RPCManager struct {
|
||||||
|
sync.Mutex
|
||||||
|
requestID int
|
||||||
|
nodeManager common.NodeManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// errors
|
||||||
|
var (
|
||||||
|
ErrInvalidMethod = errors.New("method does not exist")
|
||||||
|
ErrRPCServerTimeout = errors.New("RPC server cancelled call due to timeout")
|
||||||
|
ErrRPCServerCallFailed = errors.New("RPC server cannot complete request")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRPCManager returns new instance of RPC client
|
||||||
|
func NewRPCManager(nodeManager common.NodeManager) *RPCManager {
|
||||||
|
return &RPCManager{
|
||||||
|
nodeManager: nodeManager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call executes RPC request on node's in-proc RPC server
|
||||||
|
func (c *RPCManager) Call(inputJSON string) string {
|
||||||
|
server, err := c.nodeManager.RPCServer()
|
||||||
|
if err != nil {
|
||||||
|
return c.makeJSONErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow HTTP requests to block w/o
|
||||||
|
outputJSON := make(chan string, 1)
|
||||||
|
go func() {
|
||||||
|
httpReq := httptest.NewRequest("POST", "/", strings.NewReader(inputJSON))
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
server.ServeHTTP(rr, httpReq)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if respStatus := rr.Code; respStatus != http.StatusOK {
|
||||||
|
log.Error("handler returned wrong status code", "got", respStatus, "want", http.StatusOK)
|
||||||
|
outputJSON <- c.makeJSONErrorResponse(ErrRPCServerCallFailed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything is ok, return
|
||||||
|
outputJSON <- rr.Body.String()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait till call is complete
|
||||||
|
select {
|
||||||
|
case out := <-outputJSON:
|
||||||
|
return out
|
||||||
|
case <-time.After((status.DefaultTxSendCompletionTimeout + 10) * time.Minute): // give up eventually
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.makeJSONErrorResponse(ErrRPCServerTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeJSONErrorResponse returns error as JSON response
|
||||||
|
func (c *RPCManager) makeJSONErrorResponse(err error) string {
|
||||||
|
response := jsonErrResponse{
|
||||||
|
Version: jsonrpcVersion,
|
||||||
|
Error: jsonError{
|
||||||
|
Message: err.Error(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
outBytes, _ := json.Marshal(&response)
|
||||||
|
return string(outBytes)
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package node_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/status-im/status-go/geth/node"
|
||||||
|
"github.com/status-im/status-go/geth/params"
|
||||||
|
. "github.com/status-im/status-go/geth/testing"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRPCTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(RPCTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCTestSuite struct {
|
||||||
|
BaseTestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCTestSuite) SetupTest() {
|
||||||
|
require := s.Require()
|
||||||
|
s.NodeManager = node.NewNodeManager()
|
||||||
|
require.NotNil(s.NodeManager)
|
||||||
|
require.IsType(&node.NodeManager{}, s.NodeManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCTestSuite) TestCallRPC() {
|
||||||
|
require := s.Require()
|
||||||
|
require.NotNil(s.NodeManager)
|
||||||
|
|
||||||
|
rpcClient := node.NewRPCManager(s.NodeManager)
|
||||||
|
require.NotNil(rpcClient)
|
||||||
|
|
||||||
|
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig.IPCEnabled = false
|
||||||
|
nodeConfig.WSEnabled = false
|
||||||
|
nodeConfig.HTTPHost = "" // to make sure that no HTTP interface is started
|
||||||
|
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||||
|
require.NoError(err)
|
||||||
|
require.NotNil(nodeConfig)
|
||||||
|
defer s.NodeManager.StopNode()
|
||||||
|
<-nodeStarted
|
||||||
|
|
||||||
|
progress := make(chan struct{}, 25)
|
||||||
|
type rpcCall struct {
|
||||||
|
inputJSON string
|
||||||
|
validator func(resultJSON string)
|
||||||
|
}
|
||||||
|
var rpcCalls = []rpcCall{
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{
|
||||||
|
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
|
||||||
|
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||||
|
"gas": "0x76c0",
|
||||||
|
"gasPrice": "0x9184e72a000",
|
||||||
|
"value": "0x9184e72a",
|
||||||
|
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
log.Info("eth_sendTransaction")
|
||||||
|
s.T().Log("GOT: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"shh_version","params":[],"id":67}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":67,"result":"0x5"}` + "\n"
|
||||||
|
s.Equal(expected, resultJSON)
|
||||||
|
s.T().Log("shh_version: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}` + "\n"
|
||||||
|
s.Equal(expected, resultJSON)
|
||||||
|
s.T().Log("web3_sha3: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"jsonrpc":"2.0","method":"net_version","params":[],"id":67}`,
|
||||||
|
func(resultJSON string) {
|
||||||
|
expected := `{"jsonrpc":"2.0","id":67,"result":"4"}` + "\n"
|
||||||
|
s.Equal(expected, resultJSON)
|
||||||
|
s.T().Log("net_version: ", resultJSON)
|
||||||
|
progress <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt := len(rpcCalls) - 1 // send transaction blocks up until complete/discarded/times out
|
||||||
|
for _, r := range rpcCalls {
|
||||||
|
go func(r rpcCall) {
|
||||||
|
s.T().Logf("Run test: %v", r.inputJSON)
|
||||||
|
resultJSON := rpcClient.Call(r.inputJSON)
|
||||||
|
r.validator(resultJSON)
|
||||||
|
}(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
for range progress {
|
||||||
|
cnt -= 1
|
||||||
|
if cnt <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,6 +51,11 @@ func SetDefaultNodeNotificationHandler(fn NodeNotificationHandler) {
|
||||||
notificationHandler = fn
|
notificationHandler = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReetDefaultNodeNotificationHandler sets notification handler to default one
|
||||||
|
func ResetDefaultNodeNotificationHandler() {
|
||||||
|
notificationHandler = TriggerDefaultNodeNotificationHandler
|
||||||
|
}
|
||||||
|
|
||||||
// TriggerDefaultNodeNotificationHandler triggers default notification handler (helpful in tests)
|
// TriggerDefaultNodeNotificationHandler triggers default notification handler (helpful in tests)
|
||||||
func TriggerDefaultNodeNotificationHandler(jsonEvent string) {
|
func TriggerDefaultNodeNotificationHandler(jsonEvent string) {
|
||||||
log.Info("Notification received", "event", jsonEvent)
|
log.Info("Notification received", "event", jsonEvent)
|
||||||
|
|
|
@ -2,7 +2,10 @@ package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/status-im/status-go/geth/common"
|
"github.com/status-im/status-go/geth/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,3 +25,23 @@ func HaltOnPanic() {
|
||||||
common.Fatalf(err) // os.exit(1) is called internally
|
common.Fatalf(err) // os.exit(1) is called internally
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HaltOnInterruptSignal stops node and panics if you press Ctrl-C enough times
|
||||||
|
func HaltOnInterruptSignal(nodeManager *NodeManager) {
|
||||||
|
sigc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigc, os.Interrupt)
|
||||||
|
defer signal.Stop(sigc)
|
||||||
|
<-sigc
|
||||||
|
if nodeManager.node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("Got interrupt, shutting down...")
|
||||||
|
go nodeManager.node.Stop() // nolint: errcheck
|
||||||
|
for i := 3; i > 0; i-- {
|
||||||
|
<-sigc
|
||||||
|
if i > 1 {
|
||||||
|
log.Info(fmt.Sprintf("Already shutting down, interrupt %d more times for panic.", i-1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("interrupted!")
|
||||||
|
}
|
||||||
|
|
|
@ -49,6 +49,10 @@ type LightEthConfig struct {
|
||||||
|
|
||||||
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
|
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
|
|
||||||
|
// CHTRootConfigURL defines URL to file containing hard-coded CHT roots
|
||||||
|
// TODO remove this hack, once CHT sync is implemented on LES side
|
||||||
|
CHTRootConfigURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// FirebaseConfig holds FCM-related configuration
|
// FirebaseConfig holds FCM-related configuration
|
||||||
|
@ -233,8 +237,9 @@ func NewNodeConfig(dataDir string, networkID uint64, devMode bool) (*NodeConfig,
|
||||||
LogLevel: LogLevel,
|
LogLevel: LogLevel,
|
||||||
LogToStderr: LogToStderr,
|
LogToStderr: LogToStderr,
|
||||||
LightEthConfig: &LightEthConfig{
|
LightEthConfig: &LightEthConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
DatabaseCache: DatabaseCache,
|
DatabaseCache: DatabaseCache,
|
||||||
|
CHTRootConfigURL: CHTRootConfigURL,
|
||||||
},
|
},
|
||||||
BootClusterConfig: &BootClusterConfig{
|
BootClusterConfig: &BootClusterConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
|
|
@ -51,6 +51,10 @@ const (
|
||||||
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
|
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
|
||||||
DatabaseCache = 16
|
DatabaseCache = 16
|
||||||
|
|
||||||
|
// CHTRootConfigURL defines URL to file containing hard-coded CHT roots
|
||||||
|
// TODO remove this hack, once CHT sync is implemented on LES side
|
||||||
|
CHTRootConfigURL = "https://gist.githubusercontent.com/farazdagi/a8d36e2818b3b2b6074d691da63a0c36/raw/"
|
||||||
|
|
||||||
// LogFile defines where to write logs to
|
// LogFile defines where to write logs to
|
||||||
LogFile = "geth.log"
|
LogFile = "geth.log"
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -12,6 +12,7 @@ import (
|
||||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/status-im/status-go/geth/common"
|
"github.com/status-im/status-go/geth/common"
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
|
assertions "github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,20 +82,21 @@ func (s *BaseTestSuite) StopTestNode() {
|
||||||
require := s.Require()
|
require := s.Require()
|
||||||
require.NotNil(s.NodeManager)
|
require.NotNil(s.NodeManager)
|
||||||
require.True(s.NodeManager.IsNodeRunning())
|
require.True(s.NodeManager.IsNodeRunning())
|
||||||
require.NoError(s.NodeManager.StopNode())
|
nodeStopped, err := s.NodeManager.StopNode()
|
||||||
|
require.NoError(err)
|
||||||
|
<-nodeStopped
|
||||||
require.False(s.NodeManager.IsNodeRunning())
|
require.False(s.NodeManager.IsNodeRunning())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseTestSuite) FirstBlockHash(expectedHash string) {
|
func FirstBlockHash(require *assertions.Assertions, nodeManager common.NodeManager, expectedHash string) {
|
||||||
require := s.Require()
|
require.NotNil(nodeManager)
|
||||||
require.NotNil(s.NodeManager)
|
|
||||||
|
|
||||||
var firstBlock struct {
|
var firstBlock struct {
|
||||||
Hash gethcommon.Hash `json:"hash"`
|
Hash gethcommon.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// obtain RPC client for running node
|
// obtain RPC client for running node
|
||||||
runningNode, err := s.NodeManager.Node()
|
runningNode, err := nodeManager.Node()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(runningNode)
|
require.NotNil(runningNode)
|
||||||
|
|
||||||
|
@ -105,7 +107,7 @@ func (s *BaseTestSuite) FirstBlockHash(expectedHash string) {
|
||||||
err = rpcClient.CallContext(context.Background(), &firstBlock, "eth_getBlockByNumber", "0x0", true)
|
err = rpcClient.CallContext(context.Background(), &firstBlock, "eth_getBlockByNumber", "0x0", true)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
s.Equal(expectedHash, firstBlock.Hash.Hex())
|
require.Equal(expectedHash, firstBlock.Hash.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
|
func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
|
||||||
|
|
|
@ -124,7 +124,7 @@ func scriptsWeb3Js() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "scripts/web3.js", size: 496217, mode: os.FileMode(420), modTime: time.Unix(1495573395, 0)}
|
info := bindataFileInfo{name: "scripts/web3.js", size: 496217, mode: os.FileMode(420), modTime: time.Unix(1495916191, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ func configLinter_exclude_listTxt() (*asset, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _configTestDataJson = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x8d\x31\x6b\xc3\x40\x0c\x46\x77\xff\x0a\xa1\xb9\xc3\xc9\xae\xe4\xb3\xb7\x6c\x9d\x8a\xc1\x81\xce\x3a\x9d\x3c\xfa\xc0\x97\xd2\x96\x92\xff\x5e\x8e\x06\x02\x85\x92\x41\x20\x1e\x7c\xef\x7d\x77\x00\xf8\x5a\xb2\xe3\x0c\xed\x07\xc0\xf5\x6b\xb7\xd5\xad\xec\xb9\xe2\x0c\x14\x9e\x7e\xf1\xcb\xf9\xbc\x2c\xe5\xb8\xe0\x0c\x51\x9e\xf9\x46\xdf\xd6\x3b\x93\x0e\xe0\xda\x38\x9e\xcc\xca\xfb\x7e\xa1\xbb\xf4\x94\xf3\xe1\xb5\x09\x31\x7c\x6a\xd6\x8d\x38\xa4\x29\xb0\x6d\xec\xa2\xe3\x18\x9d\x79\x70\x62\xa5\x61\x4a\x22\x14\x53\x4a\x23\xde\x22\x8b\xd6\xfa\x51\x8e\xdc\xd6\x5a\xf3\xd6\x0e\xff\xc6\xfa\x7f\x62\xc2\x16\x88\xa3\xa8\x06\x73\xe2\x3e\x0e\x6c\x63\x8c\x6a\x2e\xc2\x3e\x91\xa6\x81\xfb\x31\xc5\x87\xb1\xee\xfa\x13\x00\x00\xff\xff\x93\xde\xa0\x86\x2d\x01\x00\x00")
|
var _configTestDataJson = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x8d\x31\x6b\xc4\x30\x0c\x46\xf7\xfc\x0a\xa1\xb9\x83\x1d\x57\xb2\x93\xed\xb6\x4e\x25\x90\x83\xce\xb2\xac\x8c\x31\xc4\x57\xda\x52\xee\xbf\x17\xd3\x83\x83\x42\xb9\x41\x20\x1e\x7c\xef\x7d\x0f\x00\xf8\x5a\x8b\xe1\x0c\xfd\x07\xc0\xf5\x6b\xd7\xd5\xb4\xee\xa5\xe1\x0c\xf1\xe9\x97\xbe\x9c\xcf\xcb\x52\x8f\x0b\xce\x90\xf8\x99\x6e\xf4\x6d\xbd\x33\x1e\x00\xae\x9d\xe3\x49\xb5\xbe\xef\x17\x7f\x77\x9e\x4a\x39\xac\x75\x1f\xba\x4f\x29\xb2\x79\x72\x79\x72\xa4\x1b\x19\x4b\x8c\xc9\x88\x82\x79\x12\x1f\xa6\xcc\xec\x53\xce\x39\xe2\x2d\xb2\x48\x6b\x1f\xf5\x28\x7d\x2d\xad\x6c\xfd\xf0\x6f\x6c\xfc\x27\xc6\xa4\xce\x53\x62\x11\xa7\xe6\x69\x4c\x81\x34\xa6\x24\x6a\xcc\x64\x93\x97\x1c\x68\x8c\x39\x3d\x8c\x0d\xd7\x9f\x00\x00\x00\xff\xff\xb4\xe1\xf5\x0c\x2c\x01\x00\x00")
|
||||||
|
|
||||||
func configTestDataJsonBytes() ([]byte, error) {
|
func configTestDataJsonBytes() ([]byte, error) {
|
||||||
return bindataRead(
|
return bindataRead(
|
||||||
|
@ -284,7 +284,7 @@ func configTestDataJson() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "config/test-data.json", size: 301, mode: os.FileMode(420), modTime: time.Unix(1495573389, 0)}
|
info := bindataFileInfo{name: "config/test-data.json", size: 300, mode: os.FileMode(420), modTime: time.Unix(1495916186, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ func testdataJailCommandsJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/jail/commands.js", size: 7677, mode: os.FileMode(420), modTime: time.Unix(1492580435, 0)}
|
info := bindataFileInfo{name: "testdata/jail/commands.js", size: 7677, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,7 @@ func testdataJailStatusJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/jail/status.js", size: 3402, mode: os.FileMode(420), modTime: time.Unix(1492580435, 0)}
|
info := bindataFileInfo{name: "testdata/jail/status.js", size: 3402, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,7 @@ func testdataJailTxSendContextNoMessageIdJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/jail/tx-send/context-no-message-id.js", size: 1793, mode: os.FileMode(420), modTime: time.Unix(1492580435, 0)}
|
info := bindataFileInfo{name: "testdata/jail/tx-send/context-no-message-id.js", size: 1793, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -464,7 +464,7 @@ func testdataJailTxSendMessageIdNoContextJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/jail/tx-send/message-id-no-context.js", size: 1875, mode: os.FileMode(420), modTime: time.Unix(1492580435, 0)}
|
info := bindataFileInfo{name: "testdata/jail/tx-send/message-id-no-context.js", size: 1875, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -484,7 +484,7 @@ func testdataJailTxSendNoMessageIdOrContextJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/jail/tx-send/no-message-id-or-context.js", size: 1354, mode: os.FileMode(420), modTime: time.Unix(1492580435, 0)}
|
info := bindataFileInfo{name: "testdata/jail/tx-send/no-message-id-or-context.js", size: 1354, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -504,7 +504,7 @@ func testdataJailTxSendTxSendJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/jail/tx-send/tx-send.js", size: 2987, mode: os.FileMode(420), modTime: time.Unix(1492580435, 0)}
|
info := bindataFileInfo{name: "testdata/jail/tx-send/tx-send.js", size: 2987, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -524,7 +524,7 @@ func testdataNodeTestSol() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "testdata/node/test.sol", size: 119, mode: os.FileMode(420), modTime: time.Unix(1488290438, 0)}
|
info := bindataFileInfo{name: "testdata/node/test.sol", size: 119, mode: os.FileMode(420), modTime: time.Unix(1495640010, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"Node": {
|
"Node": {
|
||||||
"SyncSeconds": 10,
|
"SyncSeconds": 7,
|
||||||
"HTTPPort": 8645,
|
"HTTPPort": 8645,
|
||||||
"WSPort": 8646
|
"WSPort": 8646
|
||||||
},
|
},
|
||||||
|
|
|
@ -568,6 +568,18 @@ func (n *Node) Attach() (*rpc.Client, error) {
|
||||||
return rpc.DialInProc(n.inprocHandler), nil
|
return rpc.DialInProc(n.inprocHandler), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InProcRPC exposes in-proc RPC server
|
||||||
|
func (n *Node) InProcRPC() (*rpc.Server, error) {
|
||||||
|
n.lock.RLock()
|
||||||
|
defer n.lock.RUnlock()
|
||||||
|
|
||||||
|
if n.server == nil || n.inprocHandler == nil {
|
||||||
|
return nil, ErrNodeStopped
|
||||||
|
}
|
||||||
|
|
||||||
|
return n.inprocHandler, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Server retrieves the currently running P2P network layer. This method is meant
|
// Server retrieves the currently running P2P network layer. This method is meant
|
||||||
// only to inspect fields of the currently running server, life cycle management
|
// only to inspect fields of the currently running server, life cycle management
|
||||||
// should be left to this Node entity.
|
// should be left to this Node entity.
|
||||||
|
|
Loading…
Reference in New Issue