1 package keystore
2 3 import (
4 "bytes"
5 "crypto/rand"
6 "github.com/p9c/p9/pkg/btcaddr"
7 "github.com/p9c/p9/pkg/chaincfg"
8 "math/big"
9 "reflect"
10 "testing"
11 12 "github.com/davecgh/go-spew/spew"
13 14 "github.com/p9c/p9/pkg/chainhash"
15 ec "github.com/p9c/p9/pkg/ecc"
16 "github.com/p9c/p9/pkg/txscript"
17 "github.com/p9c/p9/pkg/util"
18 )
19 20 const dummyDir = ""
21 22 var tstNetParams = &chaincfg.MainNetParams
23 24 func makeBS(height int32) *BlockStamp {
25 return &BlockStamp{
26 Hash: new(chainhash.Hash),
27 Height: height,
28 }
29 }
30 func TestBtcAddressSerializer(t *testing.T) {
31 fakeWallet := &Store{net: (*netParams)(tstNetParams)}
32 kdfp := &kdfParameters{
33 mem: 1024,
34 nIter: 5,
35 }
36 var e error
37 if _, e = rand.Read(kdfp.salt[:]); E.Chk(e) {
38 t.Error(e.Error())
39 return
40 }
41 key := kdf([]byte("banana"), kdfp)
42 privKey := make([]byte, 32)
43 if _, e = rand.Read(privKey); E.Chk(e) {
44 t.Error(e.Error())
45 return
46 }
47 addr, e := newBtcAddress(fakeWallet, privKey, nil,
48 makeBS(0), true,
49 )
50 if e != nil {
51 t.Error(e.Error())
52 return
53 }
54 e = addr.encrypt(key)
55 if e != nil {
56 t.Error(e.Error())
57 return
58 }
59 buf := new(bytes.Buffer)
60 if _, e = addr.WriteTo(buf); E.Chk(e) {
61 t.Error(e.Error())
62 return
63 }
64 var readAddr btcAddress
65 readAddr.store = fakeWallet
66 _, e = readAddr.ReadFrom(buf)
67 if e != nil {
68 t.Error(e.Error())
69 return
70 }
71 if _, e = readAddr.unlock(key); E.Chk(e) {
72 t.Error(e.Error())
73 return
74 }
75 if !reflect.DeepEqual(addr, &readAddr) {
76 t.Error("Original and read btcAddress differ.")
77 }
78 }
79 func TestScriptAddressSerializer(t *testing.T) {
80 fakeWallet := &Store{net: (*netParams)(tstNetParams)}
81 script := []byte{txscript.OP_TRUE, txscript.OP_DUP,
82 txscript.OP_DROP,
83 }
84 addr, e := newScriptAddress(fakeWallet, script, makeBS(0))
85 if e != nil {
86 t.Error(e.Error())
87 return
88 }
89 buf := new(bytes.Buffer)
90 if _, e = addr.WriteTo(buf); E.Chk(e) {
91 t.Error(e.Error())
92 return
93 }
94 var readAddr scriptAddress
95 readAddr.store = fakeWallet
96 _, e = readAddr.ReadFrom(buf)
97 if e != nil {
98 t.Error(e.Error())
99 return
100 }
101 if !reflect.DeepEqual(addr, &readAddr) {
102 t.Error("Original and read btcAddress differ.")
103 }
104 }
105 func TestWalletCreationSerialization(t *testing.T) {
106 createdAt := makeBS(0)
107 w1, e := New(dummyDir, "A wallet for testing.",
108 []byte("banana"), tstNetParams, createdAt,
109 )
110 if e != nil {
111 t.Error("ScriptError creating new wallet: " + e.Error())
112 return
113 }
114 buf := new(bytes.Buffer)
115 if _, e = w1.WriteTo(buf); E.Chk(e) {
116 t.Error("ScriptError writing new wallet: " + e.Error())
117 return
118 }
119 w2 := new(Store)
120 _, e = w2.ReadFrom(buf)
121 if e != nil {
122 t.Error("ScriptError reading newly written wallet: " + e.Error())
123 return
124 }
125 e = w1.Lock()
126 if e != nil {
127 t.Log(e)
128 }
129 e = w2.Lock()
130 if e != nil {
131 t.Log(e)
132 }
133 if e = w1.Unlock([]byte("banana")); E.Chk(e) {
134 t.Error("Decrypting original wallet failed: " + e.Error())
135 return
136 }
137 if e = w2.Unlock([]byte("banana")); E.Chk(e) {
138 t.Error("Decrypting newly read wallet failed: " + e.Error())
139 return
140 }
141 // if !reflect.DeepEqual(w1, w2) {
142 // t.ScriptError("Created and read-in wallets do not match.")
143 // spew.Dump(w1, w2)
144 // return
145 // }
146 }
147 func TestChaining(t *testing.T) {
148 tests := []struct {
149 name string
150 cc []byte
151 origPrivateKey []byte
152 nextPrivateKeyUncompressed []byte
153 nextPrivateKeyCompressed []byte
154 nextPublicKeyUncompressed []byte
155 nextPublicKeyCompressed []byte
156 }{
157 {
158 name: "chaintest 1",
159 cc: []byte("3318959fff419ab8b556facb3c429a86"),
160 origPrivateKey: []byte("5ffc975976eaaa1f7b179f384ebbc053"),
161 nextPrivateKeyUncompressed: []byte{
162 0xd3, 0xfe, 0x2e, 0x96, 0x44, 0x12, 0x2d, 0xaa,
163 0x80, 0x8e, 0x36, 0x17, 0xb5, 0x9f, 0x8c, 0xd2,
164 0x72, 0x8c, 0xaf, 0xf1, 0xdb, 0xd6, 0x4a, 0x92,
165 0xd7, 0xc7, 0xee, 0x2b, 0x56, 0x34, 0xe2, 0x87,
166 },
167 nextPrivateKeyCompressed: []byte{
168 0x08, 0x56, 0x7a, 0x1b, 0x89, 0x56, 0x2e, 0xfa,
169 0xb4, 0x02, 0x59, 0x69, 0x10, 0xc3, 0x60, 0x1f,
170 0x34, 0xf0, 0x55, 0x02, 0x8a, 0xbf, 0x37, 0xf5,
171 0x22, 0x80, 0x9f, 0xd2, 0xe5, 0x42, 0x5b, 0x2d,
172 },
173 nextPublicKeyUncompressed: []byte{
174 0x04, 0xdd, 0x70, 0x31, 0xa5, 0xf9, 0x06, 0x70,
175 0xd3, 0x9a, 0x24, 0x5b, 0xd5, 0x73, 0xdd, 0xb6,
176 0x15, 0x81, 0x0b, 0x78, 0x19, 0xbc, 0xc8, 0x26,
177 0xc9, 0x16, 0x86, 0x73, 0xae, 0xe4, 0xc0, 0xed,
178 0x39, 0x81, 0xb4, 0x86, 0x2d, 0x19, 0x8c, 0x67,
179 0x9c, 0x93, 0x99, 0xf6, 0xd2, 0x3f, 0xd1, 0x53,
180 0x9e, 0xed, 0xbd, 0x07, 0xd6, 0x4f, 0xa9, 0x81,
181 0x61, 0x85, 0x46, 0x84, 0xb1, 0xa0, 0xed, 0xbc,
182 0xa7,
183 },
184 nextPublicKeyCompressed: []byte{
185 0x02, 0x2c, 0x48, 0x73, 0x37, 0x35, 0x74, 0x7f,
186 0x05, 0x58, 0xc1, 0x4e, 0x0d, 0x18, 0xc2, 0xbf,
187 0xcc, 0x83, 0xa2, 0x4d, 0x64, 0xab, 0xba, 0xea,
188 0xeb, 0x4c, 0xcd, 0x4c, 0x0c, 0x21, 0xc4, 0x30,
189 0x0f,
190 },
191 },
192 }
193 for _, test := range tests {
194 // Create both uncompressed and compressed public keys for original
195 // private key.
196 origPubUncompressed := pubkeyFromPrivkey(test.origPrivateKey, false)
197 origPubCompressed := pubkeyFromPrivkey(test.origPrivateKey, true)
198 // Create next chained private keys, chained from both the uncompressed
199 // and compressed pubkeys.
200 nextPrivUncompressed, e := chainedPrivKey(test.origPrivateKey,
201 origPubUncompressed, test.cc,
202 )
203 if e != nil {
204 t.Errorf("%s: Uncompressed chainedPrivKey failed: %v", test.name, e)
205 return
206 }
207 nextPrivCompressed, e := chainedPrivKey(test.origPrivateKey,
208 origPubCompressed, test.cc,
209 )
210 if e != nil {
211 t.Errorf("%s: Compressed chainedPrivKey failed: %v", test.name, e)
212 return
213 }
214 // Verify that the new private keys match the expected values
215 // in the test case.
216 if !bytes.Equal(nextPrivUncompressed, test.nextPrivateKeyUncompressed) {
217 t.Errorf("%s: Next private key (from uncompressed pubkey) does not match expected.\nGot: %s\nExpected: %s",
218 test.name, spew.Sdump(nextPrivUncompressed), spew.Sdump(test.nextPrivateKeyUncompressed),
219 )
220 return
221 }
222 if !bytes.Equal(nextPrivCompressed, test.nextPrivateKeyCompressed) {
223 t.Errorf("%s: Next private key (from compressed pubkey) does not match expected.\nGot: %s\nExpected: %s",
224 test.name, spew.Sdump(nextPrivCompressed), spew.Sdump(test.nextPrivateKeyCompressed),
225 )
226 return
227 }
228 // Create the next pubkeys generated from the next private keys.
229 nextPubUncompressedFromPriv := pubkeyFromPrivkey(nextPrivUncompressed, false)
230 nextPubCompressedFromPriv := pubkeyFromPrivkey(nextPrivCompressed, true)
231 // Create the next pubkeys by chaining directly off the original
232 // pubkeys (without using the original's private key).
233 nextPubUncompressedFromPub, e := chainedPubKey(origPubUncompressed, test.cc)
234 if e != nil {
235 t.Errorf("%s: Uncompressed chainedPubKey failed: %v", test.name, e)
236 return
237 }
238 nextPubCompressedFromPub, e := chainedPubKey(origPubCompressed, test.cc)
239 if e != nil {
240 t.Errorf("%s: Compressed chainedPubKey failed: %v", test.name, e)
241 return
242 }
243 // Public keys (used to generate the bitcoin address) MUST match.
244 if !bytes.Equal(nextPubUncompressedFromPriv, nextPubUncompressedFromPub) {
245 t.Errorf("%s: Uncompressed public keys do not match.", test.name)
246 }
247 if !bytes.Equal(nextPubCompressedFromPriv, nextPubCompressedFromPub) {
248 t.Errorf("%s: Compressed public keys do not match.", test.name)
249 }
250 // Verify that all generated public keys match the expected
251 // values in the test case.
252 if !bytes.Equal(nextPubUncompressedFromPub, test.nextPublicKeyUncompressed) {
253 t.Errorf("%s: Next uncompressed public keys do not match expected value.\nGot: %s\nExpected: %s",
254 test.name, spew.Sdump(nextPubUncompressedFromPub), spew.Sdump(test.nextPublicKeyUncompressed),
255 )
256 return
257 }
258 if !bytes.Equal(nextPubCompressedFromPub, test.nextPublicKeyCompressed) {
259 t.Errorf("%s: Next compressed public keys do not match expected value.\nGot: %s\nExpected: %s",
260 test.name, spew.Sdump(nextPubCompressedFromPub), spew.Sdump(test.nextPublicKeyCompressed),
261 )
262 return
263 }
264 // Sign data with the next private keys and verify signature with
265 // the next pubkeys.
266 pubkeyUncompressed, e := ec.ParsePubKey(nextPubUncompressedFromPub, ec.S256())
267 if e != nil {
268 t.Errorf("%s: Unable to parse next uncompressed pubkey: %v", test.name, e)
269 return
270 }
271 pubkeyCompressed, e := ec.ParsePubKey(nextPubCompressedFromPub, ec.S256())
272 if e != nil {
273 t.Errorf("%s: Unable to parse next compressed pubkey: %v", test.name, e)
274 return
275 }
276 privkeyUncompressed := &ec.PrivateKey{
277 PublicKey: *pubkeyUncompressed.ToECDSA(),
278 D: new(big.Int).SetBytes(nextPrivUncompressed),
279 }
280 privkeyCompressed := &ec.PrivateKey{
281 PublicKey: *pubkeyCompressed.ToECDSA(),
282 D: new(big.Int).SetBytes(nextPrivCompressed),
283 }
284 data := "String to sign."
285 sig, e := privkeyUncompressed.Sign([]byte(data))
286 if e != nil {
287 t.Errorf("%s: Unable to sign data with next private key (chained from uncompressed pubkey): %v",
288 test.name, e,
289 )
290 return
291 }
292 ok := sig.Verify([]byte(data), privkeyUncompressed.PubKey())
293 if !ok {
294 t.Errorf("%s: ec signature verification failed for next keypair (chained from uncompressed pubkey).",
295 test.name,
296 )
297 return
298 }
299 sig, e = privkeyCompressed.Sign([]byte(data))
300 if e != nil {
301 t.Errorf("%s: Unable to sign data with next private key (chained from compressed pubkey): %v",
302 test.name, e,
303 )
304 return
305 }
306 ok = sig.Verify([]byte(data), privkeyCompressed.PubKey())
307 if !ok {
308 t.Errorf("%s: ec signature verification failed for next keypair (chained from compressed pubkey).",
309 test.name,
310 )
311 return
312 }
313 }
314 }
315 func TestWalletPubkeyChaining(t *testing.T) {
316 w, e := New(dummyDir, "A wallet for testing.",
317 []byte("banana"), tstNetParams, makeBS(0),
318 )
319 if e != nil {
320 t.Error("ScriptError creating new wallet: " + e.Error())
321 return
322 }
323 if !w.IsLocked() {
324 t.Error("New wallet is not locked.")
325 }
326 // Get next chained address. The wallet is locked, so this will chain
327 // off the last pubkey, not privkey.
328 addrWithoutPrivkey, e := w.NextChainedAddress(makeBS(0))
329 if e != nil {
330 t.Errorf("Failed to extend address chain from pubkey: %v", e)
331 return
332 }
333 // Lookup address info. This should succeed even without the private
334 // key available.
335 info, e := w.Address(addrWithoutPrivkey)
336 if e != nil {
337 t.Errorf("Failed to get info about address without private key: %v", e)
338 return
339 }
340 pkinfo := info.(PubKeyAddress)
341 // sanity checks
342 if !info.Compressed() {
343 t.Errorf("Pubkey should be compressed.")
344 return
345 }
346 if info.Imported() {
347 t.Errorf("Should not be marked as imported.")
348 return
349 }
350 pka := info.(PubKeyAddress)
351 // Try to lookup it's private key. This should fail.
352 _, e = pka.PrivKey()
353 if e == nil {
354 t.Errorf("Incorrectly returned nil error for looking up private key for address without one saved.")
355 return
356 }
357 // Deserialize w and serialize into a new wallet. The rest of the checks
358 // in this test test against both a fresh, as well as an "opened and closed"
359 // wallet with the missing private key.
360 serializedWallet := new(bytes.Buffer)
361 _, e = w.WriteTo(serializedWallet)
362 if e != nil {
363 t.Errorf("ScriptError writing wallet with missing private key: %v", e)
364 return
365 }
366 w2 := new(Store)
367 _, e = w2.ReadFrom(serializedWallet)
368 if e != nil {
369 t.Errorf("ScriptError reading wallet with missing private key: %v", e)
370 return
371 }
372 // Unlock wallet. This should trigger creating the private key for
373 // the address.
374 if e = w.Unlock([]byte("banana")); E.Chk(e) {
375 t.Errorf("Can't unlock original wallet: %v", e)
376 return
377 }
378 if e = w2.Unlock([]byte("banana")); E.Chk(e) {
379 t.Errorf("Can't unlock re-read wallet: %v", e)
380 return
381 }
382 // Same address, better variable name.
383 addrWithPrivKey := addrWithoutPrivkey
384 // Try a private key lookup again. The private key should now be available.
385 key1, e := pka.PrivKey()
386 if e != nil {
387 t.Errorf("Private key for original wallet was not created! %v", e)
388 return
389 }
390 info2, e := w.Address(addrWithPrivKey)
391 if e != nil {
392 t.Errorf("no address in re-read wallet")
393 }
394 pka2 := info2.(PubKeyAddress)
395 key2, e := pka2.PrivKey()
396 if e != nil {
397 t.Errorf("Private key for re-read wallet was not created! %v", e)
398 return
399 }
400 // Keys returned by both wallets must match.
401 if !reflect.DeepEqual(key1, key2) {
402 t.Errorf("Private keys for address originally created without one mismtach between original and re-read wallet.")
403 return
404 }
405 // Sign some data with the private key, then verify signature with the pubkey.
406 hash := []byte("hash to sign")
407 sig, e := key1.Sign(hash)
408 if e != nil {
409 t.Errorf("Unable to sign hash with the created private key: %v", e)
410 return
411 }
412 pubKey := pkinfo.PubKey()
413 ok := sig.Verify(hash, pubKey)
414 if !ok {
415 t.Errorf("ec signature verification failed; address's pubkey mismatches the privkey.")
416 return
417 }
418 nextAddr, e := w.NextChainedAddress(makeBS(0))
419 if e != nil {
420 t.Errorf("Unable to create next address after finding the privkey: %v", e)
421 return
422 }
423 nextInfo, e := w.Address(nextAddr)
424 if e != nil {
425 t.Errorf("Couldn't get info about the next address in the chain: %v", e)
426 return
427 }
428 nextPkInfo := nextInfo.(PubKeyAddress)
429 nextKey, e := nextPkInfo.PrivKey()
430 if e != nil {
431 t.Errorf("Couldn't get private key for the next address in the chain: %v", e)
432 return
433 }
434 // Do a signature check here as well, this time for the next
435 // address after the one made without the private key.
436 sig, e = nextKey.Sign(hash)
437 if e != nil {
438 t.Errorf("Unable to sign hash with the created private key: %v", e)
439 return
440 }
441 pubKey = nextPkInfo.PubKey()
442 ok = sig.Verify(hash, pubKey)
443 if !ok {
444 t.Errorf("ec signature verification failed; next address's keypair does not match.")
445 return
446 }
447 // Chk that the serialized wallet correctly unmarked the 'needs private
448 // keys later' flag.
449 buf := new(bytes.Buffer)
450 _, e = w2.WriteTo(buf)
451 if e != nil {
452 t.Log(e)
453 }
454 _, e = w2.ReadFrom(buf)
455 if e != nil {
456 t.Log(e)
457 }
458 e = w2.Unlock([]byte("banana"))
459 if e != nil {
460 t.Errorf("Unlock after serialize/deserialize failed: %v", e)
461 return
462 }
463 }
464 func TestWatchingWalletExport(t *testing.T) {
465 createdAt := makeBS(0)
466 w, e := New(dummyDir, "A wallet for testing.",
467 []byte("banana"), tstNetParams, createdAt,
468 )
469 if e != nil {
470 t.Error("ScriptError creating new wallet: " + e.Error())
471 return
472 }
473 // Maintain a set of the active addresses in the wallet.
474 activeAddrs := make(map[addressKey]struct{})
475 // Add root address.
476 activeAddrs[getAddressKey(w.LastChainedAddress())] = struct{}{}
477 // Create watching wallet from w.
478 ww, e := w.ExportWatchingWallet()
479 if e != nil {
480 t.Errorf("Could not create watching wallet: %v", e)
481 return
482 }
483 // Verify correctness of wallet flags.
484 if ww.flags.useEncryption {
485 t.Errorf("Watching wallet marked as using encryption (but nothing to encrypt).")
486 return
487 }
488 if !ww.flags.watchingOnly {
489 t.Errorf("Wallet should be watching-only but is not marked so.")
490 return
491 }
492 // Verify that all flags are set as expected.
493 if ww.keyGenerator.flags.encrypted {
494 t.Errorf("Watching root address should not be encrypted (nothing to encrypt)")
495 return
496 }
497 if ww.keyGenerator.flags.hasPrivKey {
498 t.Errorf("Watching root address marked as having a private key.")
499 return
500 }
501 if !ww.keyGenerator.flags.hasPubKey {
502 t.Errorf("Watching root address marked as missing a public key.")
503 return
504 }
505 if ww.keyGenerator.flags.createPrivKeyNextUnlock {
506 t.Errorf("Watching root address marked as needing a private key to be generated later.")
507 return
508 }
509 for apkh, waddr := range ww.addrMap {
510 switch addr := waddr.(type) {
511 case *btcAddress:
512 if addr.flags.encrypted {
513 t.Errorf("Chained address should not be encrypted (nothing to encrypt)")
514 return
515 }
516 if addr.flags.hasPrivKey {
517 t.Errorf("Chained address marked as having a private key.")
518 return
519 }
520 if !addr.flags.hasPubKey {
521 t.Errorf("Chained address marked as missing a public key.")
522 return
523 }
524 if addr.flags.createPrivKeyNextUnlock {
525 t.Errorf("Chained address marked as needing a private key to be generated later.")
526 return
527 }
528 case *scriptAddress:
529 t.Errorf("Chained address was a script!")
530 return
531 default:
532 t.Errorf("Chained address unknown type!")
533 return
534 }
535 if _, ok := activeAddrs[apkh]; !ok {
536 t.Errorf("Address from watching wallet not found in original wallet.")
537 return
538 }
539 delete(activeAddrs, apkh)
540 }
541 if len(activeAddrs) != 0 {
542 t.Errorf("%v address(es) were not exported to watching wallet.", len(activeAddrs))
543 return
544 }
545 // Chk that the new addresses created by each wallet match. The
546 // original wallet is unlocked so addresses are chained with privkeys.
547 if e = w.Unlock([]byte("banana")); E.Chk(e) {
548 t.Errorf("Unlocking original wallet failed: %v", e)
549 }
550 // Test that ExtendActiveAddresses for the watching wallet match
551 // manually requested addresses of the original wallet.
552 var newAddrs []btcaddr.Address
553 for i := 0; i < 10; i++ {
554 var addr btcaddr.Address
555 addr, e = w.NextChainedAddress(createdAt)
556 if e != nil {
557 t.Errorf("Cannot get next chained address for original wallet: %v", e)
558 return
559 }
560 newAddrs = append(newAddrs, addr)
561 }
562 newWWAddrs, e := ww.ExtendActiveAddresses(10)
563 if e != nil {
564 t.Errorf("Cannot extend active addresses for watching wallet: %v", e)
565 return
566 }
567 for i := range newAddrs {
568 if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() {
569 t.Errorf("Extended active addresses do not match manually requested addresses.")
570 return
571 }
572 }
573 // Test ExtendActiveAddresses for the original wallet after manually
574 // requesting addresses for the watching wallet.
575 newWWAddrs = newWWAddrs[:0]
576 for i := 0; i < 10; i++ {
577 var addr btcaddr.Address
578 addr, e = ww.NextChainedAddress(createdAt)
579 if e != nil {
580 t.Errorf("Cannot get next chained address for watching wallet: %v", e)
581 return
582 }
583 newWWAddrs = append(newWWAddrs, addr)
584 }
585 newAddrs, e = w.ExtendActiveAddresses(10)
586 if e != nil {
587 t.Errorf("Cannot extend active addresses for original wallet: %v", e)
588 return
589 }
590 for i := range newAddrs {
591 if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() {
592 t.Errorf("Extended active addresses do not match manually requested addresses.")
593 return
594 }
595 }
596 // Test (de)serialization of watching wallet.
597 buf := new(bytes.Buffer)
598 _, e = ww.WriteTo(buf)
599 if e != nil {
600 t.Errorf("Cannot write watching wallet: %v", e)
601 return
602 }
603 ww2 := new(Store)
604 _, e = ww2.ReadFrom(buf)
605 if e != nil {
606 t.Errorf("Cannot read watching wallet: %v", e)
607 return
608 }
609 // Chk that (de)serialized watching wallet matches the exported wallet.
610 if !reflect.DeepEqual(ww, ww2) {
611 t.Error("Exported and read-in watching wallets do not match.")
612 return
613 }
614 // Verify that nonsensical functions fail with correct error.
615 if e = ww.Lock(); e != ErrWatchingOnly {
616 t.Errorf("Nonsensical func Lock returned no or incorrect error: %v", e)
617 return
618 }
619 if e = ww.Unlock([]byte("banana")); e != ErrWatchingOnly {
620 t.Errorf("Nonsensical func Unlock returned no or incorrect error: %v", e)
621 return
622 }
623 generator, e := ww.Address(w.keyGenerator.Address())
624 if e != nil {
625 t.Errorf("generator isnt' present in wallet")
626 }
627 gpk := generator.(PubKeyAddress)
628 if _, e = gpk.PrivKey(); e != ErrWatchingOnly {
629 t.Errorf("Nonsensical func AddressKey returned no or incorrect error: %v", e)
630 return
631 }
632 if _, e = ww.ExportWatchingWallet(); e != ErrWatchingOnly {
633 t.Errorf("Nonsensical func ExportWatchingWallet returned no or incorrect error: %v", e)
634 return
635 }
636 pk, _ := ec.PrivKeyFromBytes(ec.S256(), make([]byte, 32))
637 wif, e := util.NewWIF(pk, tstNetParams, true)
638 if e != nil {
639 t.Fatal(e)
640 }
641 if _, e = ww.ImportPrivateKey(wif, createdAt); e != ErrWatchingOnly {
642 t.Errorf("Nonsensical func ImportPrivateKey returned no or incorrect error: %v", e)
643 return
644 }
645 }
646 func TestImportPrivateKey(t *testing.T) {
647 createHeight := int32(100)
648 createdAt := makeBS(createHeight)
649 w, e := New(dummyDir, "A wallet for testing.",
650 []byte("banana"), tstNetParams, createdAt,
651 )
652 if e != nil {
653 t.Error("ScriptError creating new wallet: " + e.Error())
654 return
655 }
656 if e = w.Unlock([]byte("banana")); E.Chk(e) {
657 t.Errorf("Can't unlock original wallet: %v", e)
658 return
659 }
660 pk, e := ec.NewPrivateKey(ec.S256())
661 if e != nil {
662 t.Error("ScriptError generating private key: " + e.Error())
663 return
664 }
665 // verify that the entire wallet's sync height matches the
666 // expected createHeight.
667 if _, h := w.SyncedTo(); h != createHeight {
668 t.Errorf("Initial sync height %v does not match expected %v.", h, createHeight)
669 return
670 }
671 // import priv key
672 wif, e := util.NewWIF(pk, tstNetParams, false)
673 if e != nil {
674 t.Fatal(e)
675 }
676 importHeight := int32(50)
677 importedAt := makeBS(importHeight)
678 address, e := w.ImportPrivateKey(wif, importedAt)
679 if e != nil {
680 t.Error("importing private key: " + e.Error())
681 return
682 }
683 addr, e := w.Address(address)
684 if e != nil {
685 t.Error("privkey just imported missing: " + e.Error())
686 return
687 }
688 pka := addr.(PubKeyAddress)
689 // lookup address
690 pk2, e := pka.PrivKey()
691 if e != nil {
692 t.Error("error looking up key: " + e.Error())
693 }
694 if !reflect.DeepEqual(pk, pk2) {
695 t.Error("original and looked-up private keys do not match.")
696 return
697 }
698 // verify that the sync height now match the (smaller) import height.
699 if _, h := w.SyncedTo(); h != importHeight {
700 t.Errorf("After import sync height %v does not match expected %v.", h, importHeight)
701 return
702 }
703 // serialise and deseralise and check still there.
704 // Test (de)serialization of wallet.
705 buf := new(bytes.Buffer)
706 _, e = w.WriteTo(buf)
707 if e != nil {
708 t.Errorf("Cannot write wallet: %v", e)
709 return
710 }
711 w2 := new(Store)
712 _, e = w2.ReadFrom(buf)
713 if e != nil {
714 t.Errorf("Cannot read wallet: %v", e)
715 return
716 }
717 // Verify that the sync height match expected after the reserialization.
718 if _, h := w2.SyncedTo(); h != importHeight {
719 t.Errorf("After reserialization sync height %v does not match expected %v.", h, importHeight)
720 return
721 }
722 // Mark imported address as partially synced with a block somewhere inbetween
723 // the import height and the chain height.
724 partialHeight := (createHeight-importHeight)/2 + importHeight
725 if e = w2.SetSyncStatus(address, PartialSync(partialHeight)); E.Chk(e) {
726 t.Errorf("Cannot mark address partially synced: %v", e)
727 return
728 }
729 if _, h := w2.SyncedTo(); h != partialHeight {
730 t.Errorf("After address partial sync, sync height %v does not match expected %v.", h, partialHeight)
731 return
732 }
733 // Test serialization with the partial sync.
734 buf.Reset()
735 _, e = w2.WriteTo(buf)
736 if e != nil {
737 t.Errorf("Cannot write wallet: %v", e)
738 return
739 }
740 w3 := new(Store)
741 _, e = w3.ReadFrom(buf)
742 if e != nil {
743 t.Errorf("Cannot read wallet: %v", e)
744 return
745 }
746 // Test correct partial height after serialization.
747 if _, h := w3.SyncedTo(); h != partialHeight {
748 t.Errorf("After address partial sync and reserialization, sync height %v does not match expected %v.",
749 h, partialHeight,
750 )
751 return
752 }
753 // Mark imported address as not synced at all, and verify sync height is now
754 // the import height.
755 if e = w3.SetSyncStatus(address, Unsynced(0)); E.Chk(e) {
756 t.Errorf("Cannot mark address synced: %v", e)
757 return
758 }
759 if _, h := w3.SyncedTo(); h != importHeight {
760 t.Errorf("After address unsync, sync height %v does not match expected %v.", h, importHeight)
761 return
762 }
763 // Mark imported address as synced with the recently-seen blocks, and verify
764 // that the sync height now equals the most recent block (the one at wallet
765 // creation).
766 if e = w3.SetSyncStatus(address, FullSync{}); E.Chk(e) {
767 t.Errorf("Cannot mark address synced: %v", e)
768 return
769 }
770 if _, h := w3.SyncedTo(); h != createHeight {
771 t.Errorf("After address sync, sync height %v does not match expected %v.", h, createHeight)
772 return
773 }
774 if e = w3.Unlock([]byte("banana")); E.Chk(e) {
775 t.Errorf("Can't unlock deserialised wallet: %v", e)
776 return
777 }
778 addr3, e := w3.Address(address)
779 if e != nil {
780 t.Error("privkey in deserialised wallet missing : " +
781 e.Error(),
782 )
783 return
784 }
785 pka3 := addr3.(PubKeyAddress)
786 // lookup address
787 pk2, e = pka3.PrivKey()
788 if e != nil {
789 t.Error("error looking up key in deserialized wallet: " + e.Error())
790 }
791 if !reflect.DeepEqual(pk, pk2) {
792 t.Error("original and deserialized private keys do not match.")
793 return
794 }
795 }
796 func TestImportScript(t *testing.T) {
797 createHeight := int32(100)
798 createdAt := makeBS(createHeight)
799 w, e := New(dummyDir, "A wallet for testing.",
800 []byte("banana"), tstNetParams, createdAt,
801 )
802 if e != nil {
803 t.Error("ScriptError creating new wallet: " + e.Error())
804 return
805 }
806 if e = w.Unlock([]byte("banana")); E.Chk(e) {
807 t.Errorf("Can't unlock original wallet: %v", e)
808 return
809 }
810 // verify that the entire wallet's sync height matches the
811 // expected createHeight.
812 if _, h := w.SyncedTo(); h != createHeight {
813 t.Errorf("Initial sync height %v does not match expected %v.", h, createHeight)
814 return
815 }
816 script := []byte{txscript.OP_TRUE, txscript.OP_DUP,
817 txscript.OP_DROP,
818 }
819 importHeight := int32(50)
820 stamp := makeBS(importHeight)
821 address, e := w.ImportScript(script, stamp)
822 if e != nil {
823 t.Error("error importing script: " + e.Error())
824 return
825 }
826 // lookup address
827 ainfo, e := w.Address(address)
828 if e != nil {
829 t.Error("error looking up script: " + e.Error())
830 }
831 sinfo := ainfo.(ScriptAddress)
832 if !bytes.Equal(script, sinfo.Script()) {
833 t.Error("original and looked-up script do not match.")
834 return
835 }
836 if sinfo.ScriptClass() != txscript.NonStandardTy {
837 t.Error("script type incorrect.")
838 return
839 }
840 if sinfo.RequiredSigs() != 0 {
841 t.Error("required sigs funny number")
842 return
843 }
844 if len(sinfo.Addresses()) != 0 {
845 t.Error("addresses in bogus script.")
846 return
847 }
848 if sinfo.Address().EncodeAddress() != address.EncodeAddress() {
849 t.Error("script address doesn't match entry.")
850 return
851 }
852 if string(sinfo.Address().ScriptAddress()) != sinfo.AddrHash() {
853 t.Error("script hash doesn't match address.")
854 return
855 }
856 if sinfo.FirstBlock() != importHeight {
857 t.Error("funny first block")
858 return
859 }
860 if !sinfo.Imported() {
861 t.Error("imported script info not imported.")
862 return
863 }
864 if sinfo.Change() {
865 t.Error("imported script is change.")
866 return
867 }
868 if sinfo.Compressed() {
869 t.Error("imported script is compressed.")
870 return
871 }
872 // verify that the sync height now match the (smaller) import height.
873 if _, h := w.SyncedTo(); h != importHeight {
874 t.Errorf("After import sync height %v does not match expected %v.", h, importHeight)
875 return
876 }
877 // Chk that it's included along with the active payment addresses.
878 found := false
879 for _, wa := range w.SortedActiveAddresses() {
880 if wa.Address() == address {
881 found = true
882 break
883 }
884 }
885 if !found {
886 t.Errorf("Imported script address was not returned with sorted active payment addresses.")
887 return
888 }
889 if _, ok := w.ActiveAddresses()[address]; !ok {
890 t.Errorf("Imported script address was not returned with unsorted active payment addresses.")
891 return
892 }
893 // serialise and deseralise and check still there.
894 // Test (de)serialization of wallet.
895 buf := new(bytes.Buffer)
896 _, e = w.WriteTo(buf)
897 if e != nil {
898 t.Errorf("Cannot write wallet: %v", e)
899 return
900 }
901 w2 := new(Store)
902 _, e = w2.ReadFrom(buf)
903 if e != nil {
904 t.Errorf("Cannot read wallet: %v", e)
905 return
906 }
907 // Verify that the sync height matches expected after the reserialization.
908 if _, h := w2.SyncedTo(); h != importHeight {
909 t.Errorf("After reserialization sync height %v does not match expected %v.", h, importHeight)
910 return
911 }
912 // lookup address
913 ainfo2, e := w2.Address(address)
914 if e != nil {
915 t.Error("error looking up info in deserialized wallet: " + e.Error())
916 }
917 sinfo2 := ainfo2.(ScriptAddress)
918 // Chk all the same again. We can't use reflect.DeepEquals since
919 // the internals have pointers back to the wallet struct.
920 if sinfo2.Address().EncodeAddress() != address.EncodeAddress() {
921 t.Error("script address doesn't match entry.")
922 return
923 }
924 if string(sinfo2.Address().ScriptAddress()) != sinfo2.AddrHash() {
925 t.Error("script hash doesn't match address.")
926 return
927 }
928 if sinfo2.FirstBlock() != importHeight {
929 t.Error("funny first block")
930 return
931 }
932 if !sinfo2.Imported() {
933 t.Error("imported script info not imported.")
934 return
935 }
936 if sinfo2.Change() {
937 t.Error("imported script is change.")
938 return
939 }
940 if sinfo2.Compressed() {
941 t.Error("imported script is compressed.")
942 return
943 }
944 if !bytes.Equal(sinfo.Script(), sinfo2.Script()) {
945 t.Errorf("original and serailised scriptinfo scripts "+
946 "don't match %s != %s", spew.Sdump(sinfo.Script()),
947 spew.Sdump(sinfo2.Script()),
948 )
949 }
950 if sinfo.ScriptClass() != sinfo2.ScriptClass() {
951 t.Errorf("original and serailised scriptinfo class "+
952 "don't match: %s != %s", sinfo.ScriptClass(),
953 sinfo2.ScriptClass(),
954 )
955 return
956 }
957 if !reflect.DeepEqual(sinfo.Addresses(), sinfo2.Addresses()) {
958 t.Errorf("original and serailised scriptinfo addresses "+
959 "don't match (%s) != (%s)", spew.Sdump(sinfo.Addresses),
960 spew.Sdump(sinfo2.Addresses()),
961 )
962 return
963 }
964 // if sinfo.RequiredSigs() != sinfo.RequiredSigs() {
965 // t.Errorf("original and serailised scriptinfo requiredsigs "+
966 // "don't match %d != %d", sinfo.RequiredSigs(),
967 // sinfo2.RequiredSigs())
968 // return
969 // }
970 // Chk that it's included along with the active payment addresses.
971 found = false
972 for _, wa := range w.SortedActiveAddresses() {
973 if wa.Address() == address {
974 found = true
975 break
976 }
977 }
978 if !found {
979 t.Errorf("After reserialiation, imported script address was not returned with sorted " +
980 "active payment addresses.",
981 )
982 return
983 }
984 if _, ok := w.ActiveAddresses()[address]; !ok {
985 t.Errorf("After reserialiation, imported script address was not returned with unsorted " +
986 "active payment addresses.",
987 )
988 return
989 }
990 // Mark imported address as partially synced with a block somewhere inbetween
991 // the import height and the chain height.
992 partialHeight := (createHeight-importHeight)/2 + importHeight
993 if e = w2.SetSyncStatus(address, PartialSync(partialHeight)); E.Chk(e) {
994 t.Errorf("Cannot mark address partially synced: %v", e)
995 return
996 }
997 if _, h := w2.SyncedTo(); h != partialHeight {
998 t.Errorf("After address partial sync, sync height %v does not match expected %v.", h, partialHeight)
999 return
1000 }
1001 // Test serialization with the partial sync.
1002 buf.Reset()
1003 _, e = w2.WriteTo(buf)
1004 if e != nil {
1005 t.Errorf("Cannot write wallet: %v", e)
1006 return
1007 }
1008 w3 := new(Store)
1009 _, e = w3.ReadFrom(buf)
1010 if e != nil {
1011 t.Errorf("Cannot read wallet: %v", e)
1012 return
1013 }
1014 // Test correct partial height after serialization.
1015 if _, h := w3.SyncedTo(); h != partialHeight {
1016 t.Errorf("After address partial sync and reserialization, sync height %v does not match expected %v.",
1017 h, partialHeight,
1018 )
1019 return
1020 }
1021 // Mark imported address as not synced at all, and verify sync height is now
1022 // the import height.
1023 if e = w3.SetSyncStatus(address, Unsynced(0)); E.Chk(e) {
1024 t.Errorf("Cannot mark address synced: %v", e)
1025 return
1026 }
1027 if _, h := w3.SyncedTo(); h != importHeight {
1028 t.Errorf("After address unsync, sync height %v does not match expected %v.", h, importHeight)
1029 return
1030 }
1031 // Mark imported address as synced with the recently-seen blocks, and verify
1032 // that the sync height now equals the most recent block (the one at wallet
1033 // creation).
1034 if e = w3.SetSyncStatus(address, FullSync{}); E.Chk(e) {
1035 t.Errorf("Cannot mark address synced: %v", e)
1036 return
1037 }
1038 if _, h := w3.SyncedTo(); h != createHeight {
1039 t.Errorf("After address sync, sync height %v does not match expected %v.", h, createHeight)
1040 return
1041 }
1042 if e = w3.Unlock([]byte("banana")); E.Chk(e) {
1043 t.Errorf("Can't unlock deserialised wallet: %v", e)
1044 return
1045 }
1046 }
1047 func TestChangePassphrase(t *testing.T) {
1048 createdAt := makeBS(0)
1049 var e error
1050 var w *Store
1051 w, e = New(dummyDir, "A wallet for testing.",
1052 []byte("banana"), tstNetParams, createdAt,
1053 )
1054 if e != nil {
1055 t.Error("ScriptError creating new wallet: " + e.Error())
1056 return
1057 }
1058 // Changing the passphrase with a locked wallet must fail with ErrWalletLocked.
1059 if e = w.ChangePassphrase([]byte("potato")); e != ErrLocked {
1060 t.Errorf("Changing passphrase on a locked wallet did not fail correctly: %v", e)
1061 return
1062 }
1063 // Unlock wallet so the passphrase can be changed.
1064 if e = w.Unlock([]byte("banana")); E.Chk(e) {
1065 t.Errorf("Cannot unlock: %v", e)
1066 return
1067 }
1068 // Get root address and its private key. This is compared to the private
1069 // key post passphrase change.
1070 rootAddr := w.LastChainedAddress()
1071 rootAddrInfo, e := w.Address(rootAddr)
1072 if e != nil {
1073 t.Error("can't find root address: " + e.Error())
1074 return
1075 }
1076 rapka := rootAddrInfo.(PubKeyAddress)
1077 rootPrivKey, e := rapka.PrivKey()
1078 if e != nil {
1079 t.Errorf("Cannot get root address' private key: %v", e)
1080 return
1081 }
1082 // Change passphrase.
1083 if e = w.ChangePassphrase([]byte("potato")); E.Chk(e) {
1084 t.Errorf("Changing passphrase failed: %v", e)
1085 return
1086 }
1087 // Wallet should still be unlocked.
1088 if w.IsLocked() {
1089 t.Errorf("Wallet should be unlocked after passphrase change.")
1090 return
1091 }
1092 // Lock it.
1093 if e = w.Lock(); E.Chk(e) {
1094 t.Errorf("Cannot lock wallet after passphrase change: %v", e)
1095 return
1096 }
1097 // Unlock with old passphrase. This must fail with ErrWrongPassphrase.
1098 if e = w.Unlock([]byte("banana")); e != ErrWrongPassphrase {
1099 t.Errorf("Unlocking with old passphrases did not fail correctly: %v", e)
1100 return
1101 }
1102 // Unlock with new passphrase. This must succeed.
1103 if e = w.Unlock([]byte("potato")); E.Chk(e) {
1104 t.Errorf("Unlocking with new passphrase failed: %v", e)
1105 return
1106 }
1107 // Get root address' private key again.
1108 rootAddrInfo2, e := w.Address(rootAddr)
1109 if e != nil {
1110 t.Error("can't find root address: " + e.Error())
1111 return
1112 }
1113 rapka2 := rootAddrInfo2.(PubKeyAddress)
1114 rootPrivKey2, e := rapka2.PrivKey()
1115 if e != nil {
1116 t.Errorf("Cannot get root address' private key after passphrase change: %v", e)
1117 return
1118 }
1119 // Private keys must match.
1120 if !reflect.DeepEqual(rootPrivKey, rootPrivKey2) {
1121 t.Errorf("Private keys before and after unlock differ.")
1122 return
1123 }
1124 }
1125