geth,cmd/status: pause/resume node fixes #96

This commit is contained in:
Victor Farazdagi 2017-01-24 21:42:55 +03:00
parent a77c2362d5
commit fd36dcfdb4
5 changed files with 356 additions and 2 deletions

View File

@ -205,6 +205,18 @@ func StartNode(datadir *C.char) *C.char {
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
func ResetChainData() *C.char {
err := geth.NodeManagerInstance().ResetChainData()

View File

@ -79,6 +79,14 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
"test jailed calls",
testJailFunctionCall,
},
{
"reset blockchain data",
testResetChainData,
},
{
"pause node",
testStopResumeNode,
},
}
for _, test := range tests {
@ -90,6 +98,119 @@ func testExportedAPI(t *testing.T, done chan 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 {
// stop RPC
stopNodeRPCServerResponse := geth.JSONError{}

View File

@ -167,6 +167,27 @@ func SelectAccount(address, password string) error {
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
func Logout() error {
nodeManager := NodeManagerInstance()

View File

@ -413,3 +413,185 @@ func TestAccountLogout(t *testing.T) {
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")
}
}

View File

@ -208,6 +208,23 @@ func (m *NodeManager) RestartNode() error {
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.
func (m *NodeManager) ResetChainData() error {
if m == nil || !m.NodeInited() {
@ -227,8 +244,9 @@ func (m *NodeManager) ResetChainData() error {
return err
}
m.RunNode()
m.WaitNodeStarted()
if err := m.ResumeNode(); err != nil {
return err
}
return nil
}