keystore_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // Copyright 2017 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package keystore
  17. import (
  18. "io/ioutil"
  19. "math/rand"
  20. "os"
  21. "runtime"
  22. "sort"
  23. "strings"
  24. "testing"
  25. "time"
  26. "github.com/ethereum/go-ethereum/accounts"
  27. "github.com/ethereum/go-ethereum/common"
  28. "github.com/ethereum/go-ethereum/event"
  29. )
  30. var testSigData = make([]byte, 32)
  31. func TestKeyStore(t *testing.T) {
  32. dir, ks := tmpKeyStore(t, true)
  33. defer os.RemoveAll(dir)
  34. a, err := ks.NewAccount("foo")
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. if !strings.HasPrefix(a.URL.Path, dir) {
  39. t.Errorf("account file %s doesn't have dir prefix", a.URL)
  40. }
  41. stat, err := os.Stat(a.URL.Path)
  42. if err != nil {
  43. t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
  44. }
  45. if runtime.GOOS != "windows" && stat.Mode() != 0600 {
  46. t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
  47. }
  48. if !ks.HasAddress(a.Address) {
  49. t.Errorf("HasAccount(%x) should've returned true", a.Address)
  50. }
  51. if err := ks.Update(a, "foo", "bar"); err != nil {
  52. t.Errorf("Update error: %v", err)
  53. }
  54. if err := ks.Delete(a, "bar"); err != nil {
  55. t.Errorf("Delete error: %v", err)
  56. }
  57. if common.FileExist(a.URL.Path) {
  58. t.Errorf("account file %s should be gone after Delete", a.URL)
  59. }
  60. if ks.HasAddress(a.Address) {
  61. t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
  62. }
  63. }
  64. func TestSign(t *testing.T) {
  65. dir, ks := tmpKeyStore(t, true)
  66. defer os.RemoveAll(dir)
  67. pass := "" // not used but required by API
  68. a1, err := ks.NewAccount(pass)
  69. if err != nil {
  70. t.Fatal(err)
  71. }
  72. if err := ks.Unlock(a1, ""); err != nil {
  73. t.Fatal(err)
  74. }
  75. if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
  76. t.Fatal(err)
  77. }
  78. }
  79. func TestSignWithPassphrase(t *testing.T) {
  80. dir, ks := tmpKeyStore(t, true)
  81. defer os.RemoveAll(dir)
  82. pass := "passwd"
  83. acc, err := ks.NewAccount(pass)
  84. if err != nil {
  85. t.Fatal(err)
  86. }
  87. if _, unlocked := ks.unlocked[acc.Address]; unlocked {
  88. t.Fatal("expected account to be locked")
  89. }
  90. _, err = ks.SignHashWithPassphrase(acc, pass, testSigData)
  91. if err != nil {
  92. t.Fatal(err)
  93. }
  94. if _, unlocked := ks.unlocked[acc.Address]; unlocked {
  95. t.Fatal("expected account to be locked")
  96. }
  97. if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil {
  98. t.Fatal("expected SignHashWithPassphrase to fail with invalid password")
  99. }
  100. }
  101. func TestTimedUnlock(t *testing.T) {
  102. dir, ks := tmpKeyStore(t, true)
  103. defer os.RemoveAll(dir)
  104. pass := "foo"
  105. a1, err := ks.NewAccount(pass)
  106. if err != nil {
  107. t.Fatal(err)
  108. }
  109. // Signing without passphrase fails because account is locked
  110. _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
  111. if err != ErrLocked {
  112. t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err)
  113. }
  114. // Signing with passphrase works
  115. if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
  116. t.Fatal(err)
  117. }
  118. // Signing without passphrase works because account is temp unlocked
  119. _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
  120. if err != nil {
  121. t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
  122. }
  123. // Signing fails again after automatic locking
  124. time.Sleep(250 * time.Millisecond)
  125. _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
  126. if err != ErrLocked {
  127. t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
  128. }
  129. }
  130. func TestOverrideUnlock(t *testing.T) {
  131. dir, ks := tmpKeyStore(t, false)
  132. defer os.RemoveAll(dir)
  133. pass := "foo"
  134. a1, err := ks.NewAccount(pass)
  135. if err != nil {
  136. t.Fatal(err)
  137. }
  138. // Unlock indefinitely.
  139. if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil {
  140. t.Fatal(err)
  141. }
  142. // Signing without passphrase works because account is temp unlocked
  143. _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
  144. if err != nil {
  145. t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
  146. }
  147. // reset unlock to a shorter period, invalidates the previous unlock
  148. if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
  149. t.Fatal(err)
  150. }
  151. // Signing without passphrase still works because account is temp unlocked
  152. _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
  153. if err != nil {
  154. t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
  155. }
  156. // Signing fails again after automatic locking
  157. time.Sleep(250 * time.Millisecond)
  158. _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
  159. if err != ErrLocked {
  160. t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
  161. }
  162. }
  163. // This test should fail under -race if signing races the expiration goroutine.
  164. func TestSignRace(t *testing.T) {
  165. dir, ks := tmpKeyStore(t, false)
  166. defer os.RemoveAll(dir)
  167. // Create a test account.
  168. a1, err := ks.NewAccount("")
  169. if err != nil {
  170. t.Fatal("could not create the test account", err)
  171. }
  172. if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
  173. t.Fatal("could not unlock the test account", err)
  174. }
  175. end := time.Now().Add(500 * time.Millisecond)
  176. for time.Now().Before(end) {
  177. if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
  178. return
  179. } else if err != nil {
  180. t.Errorf("Sign error: %v", err)
  181. return
  182. }
  183. time.Sleep(1 * time.Millisecond)
  184. }
  185. t.Errorf("Account did not lock within the timeout")
  186. }
  187. // Tests that the wallet notifier loop starts and stops correctly based on the
  188. // addition and removal of wallet event subscriptions.
  189. func TestWalletNotifierLifecycle(t *testing.T) {
  190. // Create a temporary kesytore to test with
  191. dir, ks := tmpKeyStore(t, false)
  192. defer os.RemoveAll(dir)
  193. // Ensure that the notification updater is not running yet
  194. time.Sleep(250 * time.Millisecond)
  195. ks.mu.RLock()
  196. updating := ks.updating
  197. ks.mu.RUnlock()
  198. if updating {
  199. t.Errorf("wallet notifier running without subscribers")
  200. }
  201. // Subscribe to the wallet feed and ensure the updater boots up
  202. updates := make(chan accounts.WalletEvent)
  203. subs := make([]event.Subscription, 2)
  204. for i := 0; i < len(subs); i++ {
  205. // Create a new subscription
  206. subs[i] = ks.Subscribe(updates)
  207. // Ensure the notifier comes online
  208. time.Sleep(250 * time.Millisecond)
  209. ks.mu.RLock()
  210. updating = ks.updating
  211. ks.mu.RUnlock()
  212. if !updating {
  213. t.Errorf("sub %d: wallet notifier not running after subscription", i)
  214. }
  215. }
  216. // Unsubscribe and ensure the updater terminates eventually
  217. for i := 0; i < len(subs); i++ {
  218. // Close an existing subscription
  219. subs[i].Unsubscribe()
  220. // Ensure the notifier shuts down at and only at the last close
  221. for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ {
  222. ks.mu.RLock()
  223. updating = ks.updating
  224. ks.mu.RUnlock()
  225. if i < len(subs)-1 && !updating {
  226. t.Fatalf("sub %d: event notifier stopped prematurely", i)
  227. }
  228. if i == len(subs)-1 && !updating {
  229. return
  230. }
  231. time.Sleep(250 * time.Millisecond)
  232. }
  233. }
  234. t.Errorf("wallet notifier didn't terminate after unsubscribe")
  235. }
  236. // Tests that wallet notifications and correctly fired when accounts are added
  237. // or deleted from the keystore.
  238. func TestWalletNotifications(t *testing.T) {
  239. // Create a temporary kesytore to test with
  240. dir, ks := tmpKeyStore(t, false)
  241. defer os.RemoveAll(dir)
  242. // Subscribe to the wallet feed
  243. updates := make(chan accounts.WalletEvent, 1)
  244. sub := ks.Subscribe(updates)
  245. defer sub.Unsubscribe()
  246. // Randomly add and remove account and make sure events and wallets are in sync
  247. live := make(map[common.Address]accounts.Account)
  248. for i := 0; i < 1024; i++ {
  249. // Execute a creation or deletion and ensure event arrival
  250. if create := len(live) == 0 || rand.Int()%4 > 0; create {
  251. // Add a new account and ensure wallet notifications arrives
  252. account, err := ks.NewAccount("")
  253. if err != nil {
  254. t.Fatalf("failed to create test account: %v", err)
  255. }
  256. select {
  257. case event := <-updates:
  258. if !event.Arrive {
  259. t.Errorf("departure event on account creation")
  260. }
  261. if event.Wallet.Accounts()[0] != account {
  262. t.Errorf("account mismatch on created wallet: have %v, want %v", event.Wallet.Accounts()[0], account)
  263. }
  264. default:
  265. t.Errorf("wallet arrival event not fired on account creation")
  266. }
  267. live[account.Address] = account
  268. } else {
  269. // Select a random account to delete (crude, but works)
  270. var account accounts.Account
  271. for _, a := range live {
  272. account = a
  273. break
  274. }
  275. // Remove an account and ensure wallet notifiaction arrives
  276. if err := ks.Delete(account, ""); err != nil {
  277. t.Fatalf("failed to delete test account: %v", err)
  278. }
  279. select {
  280. case event := <-updates:
  281. if event.Arrive {
  282. t.Errorf("arrival event on account deletion")
  283. }
  284. if event.Wallet.Accounts()[0] != account {
  285. t.Errorf("account mismatch on deleted wallet: have %v, want %v", event.Wallet.Accounts()[0], account)
  286. }
  287. default:
  288. t.Errorf("wallet departure event not fired on account creation")
  289. }
  290. delete(live, account.Address)
  291. }
  292. // Retrieve the list of wallets and ensure it matches with our required live set
  293. liveList := make([]accounts.Account, 0, len(live))
  294. for _, account := range live {
  295. liveList = append(liveList, account)
  296. }
  297. sort.Sort(accountsByURL(liveList))
  298. wallets := ks.Wallets()
  299. if len(liveList) != len(wallets) {
  300. t.Errorf("wallet list doesn't match required accounts: have %v, want %v", wallets, liveList)
  301. } else {
  302. for j, wallet := range wallets {
  303. if accs := wallet.Accounts(); len(accs) != 1 {
  304. t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
  305. } else if accs[0] != liveList[j] {
  306. t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j])
  307. }
  308. }
  309. }
  310. }
  311. }
  312. func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
  313. d, err := ioutil.TempDir("", "eth-keystore-test")
  314. if err != nil {
  315. t.Fatal(err)
  316. }
  317. new := NewPlaintextKeyStore
  318. if encrypted {
  319. new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
  320. }
  321. return d, new(d)
  322. }