geth,cmd/status: pause/resume node fixes #96
This commit is contained in:
parent
a77c2362d5
commit
fd36dcfdb4
|
@ -205,6 +205,18 @@ func StartNode(datadir *C.char) *C.char {
|
||||||
return makeJSONErrorResponse(err)
|
return makeJSONErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export StopNode
|
||||||
|
func StopNode() *C.char {
|
||||||
|
err := geth.NodeManagerInstance().StopNode()
|
||||||
|
return makeJSONErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export ResumeNode
|
||||||
|
func ResumeNode() *C.char {
|
||||||
|
err := geth.NodeManagerInstance().ResumeNode()
|
||||||
|
return makeJSONErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
//export ResetChainData
|
//export ResetChainData
|
||||||
func ResetChainData() *C.char {
|
func ResetChainData() *C.char {
|
||||||
err := geth.NodeManagerInstance().ResetChainData()
|
err := geth.NodeManagerInstance().ResetChainData()
|
||||||
|
|
|
@ -79,6 +79,14 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
|
||||||
"test jailed calls",
|
"test jailed calls",
|
||||||
testJailFunctionCall,
|
testJailFunctionCall,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"reset blockchain data",
|
||||||
|
testResetChainData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pause node",
|
||||||
|
testStopResumeNode,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -90,6 +98,119 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
|
||||||
done <- struct{}{}
|
done <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testResetChainData(t *testing.T) bool {
|
||||||
|
resetChainDataResponse := geth.JSONError{}
|
||||||
|
rawResponse := ResetChainData()
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &resetChainDataResponse); err != nil {
|
||||||
|
t.Errorf("cannot decode ResetChainData reponse (%s): %v", C.GoString(rawResponse), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if resetChainDataResponse.Error != "" {
|
||||||
|
t.Errorf("unexpected error: %s", resetChainDataResponse.Error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(15 * time.Second) // allow to re-sync blockchain
|
||||||
|
|
||||||
|
testCompleteTransaction(t)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStopResumeNode(t *testing.T) bool {
|
||||||
|
geth.Logout() // to make sure that we start with empty account (which might get populated during previous tests)
|
||||||
|
|
||||||
|
whisperService, err := geth.NodeManagerInstance().WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("whisper service not running: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an account
|
||||||
|
address1, pubKey1, _, err := geth.CreateAccount(newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not create account: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.Logf("account created: {address: %s, key: %s}", address1, pubKey1)
|
||||||
|
|
||||||
|
// make sure that identity is not (yet injected)
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Error("identity already present in whisper")
|
||||||
|
}
|
||||||
|
|
||||||
|
// select account
|
||||||
|
loginResponse := geth.JSONError{}
|
||||||
|
rawResponse := Login(C.CString(address1), C.CString(newAccountPassword))
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil {
|
||||||
|
t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginResponse.Error != "" {
|
||||||
|
t.Errorf("could not select account: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Errorf("identity not injected into whisper: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop and resume node, then make sure that selected account is still selected
|
||||||
|
stopNodeFn := func() bool {
|
||||||
|
response := geth.JSONError{}
|
||||||
|
rawResponse = StopNode()
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &response); err != nil {
|
||||||
|
t.Errorf("cannot decode StopNode reponse (%s): %v", C.GoString(rawResponse), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
t.Errorf("unexpected error: %s", response.Error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
resumeNodeFn := func() bool {
|
||||||
|
response := geth.JSONError{}
|
||||||
|
rawResponse = ResumeNode()
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &response); err != nil {
|
||||||
|
t.Errorf("cannot decode ResumeNode reponse (%s): %v", C.GoString(rawResponse), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
t.Errorf("unexpected error: %s", response.Error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stopNodeFn() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !resumeNodeFn() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// now, verify that we still have account logged in
|
||||||
|
whisperService, err = geth.NodeManagerInstance().WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("whisper service not running: %v", err)
|
||||||
|
}
|
||||||
|
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Errorf("identity evicted from whisper on node restart: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// additionally, let's complete transaction (just to make sure that node lives through pause/resume w/o issues)
|
||||||
|
testCompleteTransaction(t)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func testRestartNodeRPC(t *testing.T) bool {
|
func testRestartNodeRPC(t *testing.T) bool {
|
||||||
// stop RPC
|
// stop RPC
|
||||||
stopNodeRPCServerResponse := geth.JSONError{}
|
stopNodeRPCServerResponse := geth.JSONError{}
|
||||||
|
|
|
@ -167,6 +167,27 @@ func SelectAccount(address, password string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSelectAccount selects previously selected account, often, after node restart.
|
||||||
|
func ReSelectAccount() error {
|
||||||
|
nodeManager := NodeManagerInstance()
|
||||||
|
|
||||||
|
selectedAccount := nodeManager.SelectedAccount
|
||||||
|
if selectedAccount == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
whisperService, err := nodeManager.WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := whisperService.InjectIdentity(selectedAccount.AccountKey.PrivateKey); err != nil {
|
||||||
|
return ErrWhisperIdentityInjectionFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Logout clears whisper identities
|
// Logout clears whisper identities
|
||||||
func Logout() error {
|
func Logout() error {
|
||||||
nodeManager := NodeManagerInstance()
|
nodeManager := NodeManagerInstance()
|
||||||
|
|
|
@ -413,3 +413,185 @@ func TestAccountLogout(t *testing.T) {
|
||||||
t.Error("identity not cleared from whisper")
|
t.Error("identity not cleared from whisper")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelectedAccountOnNodeRestart(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to make sure that selected account is injected as identity into Whisper
|
||||||
|
whisperService, err := geth.NodeManagerInstance().WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("whisper service not running: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create test accounts
|
||||||
|
address1, pubKey1, _, err := geth.CreateAccount(newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not create account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("account1 created: {address: %s, key: %s}", address1, pubKey1)
|
||||||
|
address2, pubKey2, _, err := geth.CreateAccount(newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not create account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("account2 created: {address: %s, key: %s}", address2, pubKey2)
|
||||||
|
|
||||||
|
// make sure that identity is not (yet injected)
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Error("identity already present in whisper")
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that no account is selected by default
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount != nil {
|
||||||
|
t.Error("account selected, but should not be")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// select account
|
||||||
|
err = geth.SelectAccount(address1, "wrongPassword")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("select account is expected to throw error: wrong password used")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = geth.SelectAccount(address1, newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not select account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Errorf("identity not injected into whisper: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||||
|
t.Error("identity already present in whisper")
|
||||||
|
}
|
||||||
|
err = geth.SelectAccount(address2, newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: could not select account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||||
|
t.Errorf("identity not injected into whisper: %v", err)
|
||||||
|
}
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Error("identity should be removed, but it is still present in whisper")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop node (and all of its sub-protocols)
|
||||||
|
if err := geth.NodeManagerInstance().StopNode(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that account is still selected
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount == nil {
|
||||||
|
t.Error("no selected account")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount.Address.Hex() != "0x"+address2 {
|
||||||
|
t.Error("incorrect address selected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume node
|
||||||
|
if err := geth.NodeManagerInstance().ResumeNode(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-check selected account (account2 MUST be selected)
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount == nil {
|
||||||
|
t.Error("no selected account")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount.Address.Hex() != "0x"+address2 {
|
||||||
|
t.Error("incorrect address selected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that Whisper gets identity re-injected
|
||||||
|
whisperService, err = geth.NodeManagerInstance().WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("whisper service not running: %v", err)
|
||||||
|
}
|
||||||
|
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||||
|
t.Errorf("identity not injected into whisper: %v", err)
|
||||||
|
}
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Error("identity should not be present, but it is still present in whisper")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeRestartWithNoSelectedAccount(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
geth.Logout()
|
||||||
|
|
||||||
|
// we need to make sure that selected account is injected as identity into Whisper
|
||||||
|
whisperService, err := geth.NodeManagerInstance().WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("whisper service not running: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create test accounts
|
||||||
|
address1, pubKey1, _, err := geth.CreateAccount(newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not create account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("account1 created: {address: %s, key: %s}", address1, pubKey1)
|
||||||
|
|
||||||
|
// make sure that identity is not present
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Error("identity already present in whisper")
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that no account is selected
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount != nil {
|
||||||
|
t.Error("account selected, but should not be")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop node (and all of its sub-protocols)
|
||||||
|
if err := geth.NodeManagerInstance().StopNode(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that no account is selected
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount != nil {
|
||||||
|
t.Error("account selected, but should not be")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume node
|
||||||
|
if err := geth.NodeManagerInstance().ResumeNode(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that no account is selected
|
||||||
|
if geth.NodeManagerInstance().SelectedAccount != nil {
|
||||||
|
t.Error("account selected, but should not be")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that Whisper gets identity re-injected
|
||||||
|
whisperService, err = geth.NodeManagerInstance().WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("whisper service not running: %v", err)
|
||||||
|
}
|
||||||
|
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||||
|
t.Error("identity should not be present, but it is present in whisper")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -208,6 +208,23 @@ func (m *NodeManager) RestartNode() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResumeNode resumes previously stopped P2P node
|
||||||
|
func (m *NodeManager) ResumeNode() error {
|
||||||
|
if m == nil || !m.NodeInited() {
|
||||||
|
return ErrInvalidGethNode
|
||||||
|
}
|
||||||
|
|
||||||
|
m.RunNode()
|
||||||
|
m.WaitNodeStarted()
|
||||||
|
|
||||||
|
// re-select the previously selected account
|
||||||
|
if err := ReSelectAccount(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ResetChainData purges chain data (by removing data directory). Safe to apply on running P2P node.
|
// ResetChainData purges chain data (by removing data directory). Safe to apply on running P2P node.
|
||||||
func (m *NodeManager) ResetChainData() error {
|
func (m *NodeManager) ResetChainData() error {
|
||||||
if m == nil || !m.NodeInited() {
|
if m == nil || !m.NodeInited() {
|
||||||
|
@ -227,8 +244,9 @@ func (m *NodeManager) ResetChainData() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.RunNode()
|
if err := m.ResumeNode(); err != nil {
|
||||||
m.WaitNodeStarted()
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue