func TestEncodeDecodeWIF(t *testing.T) { priv1, _ := btcec.PrivKeyFromBytes(btcec.S256(), []byte{ 0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27, 0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11, 0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b, 0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d}) priv2, _ := btcec.PrivKeyFromBytes(btcec.S256(), []byte{ 0xdd, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6, 0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81, 0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9, 0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98}) wif1, err := NewWIF(priv1, &chaincfg.MainNetParams, false) if err != nil { t.Fatal(err) } wif2, err := NewWIF(priv2, &chaincfg.TestNet3Params, true) if err != nil { t.Fatal(err) } tests := []struct { wif *WIF encoded string }{ { wif1, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ", }, { wif2, "cV1Y7ARUr9Yx7BR55nTdnR7ZXNJphZtCCMBTEZBJe1hXt2kB684q", }, } for _, test := range tests { // Test that encoding the WIF structure matches the expected string. s := test.wif.String() if s != test.encoded { t.Errorf("TestEncodeDecodePrivateKey failed: want '%s', got '%s'", test.encoded, s) continue } // Test that decoding the expected string results in the original WIF // structure. w, err := DecodeWIF(test.encoded) if err != nil { t.Error(err) continue } if got := w.String(); got != test.encoded { t.Errorf("NewWIF failed: want '%v', got '%v'", test.wif, got) } } }
// This example demonstrates signing a message with a secp256k1 private key that // is first parsed form raw bytes and serializing the generated signature. func Example_signMessage() { // Decode a hex-encoded private key. pkBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2d4f87" + "20ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) // Sign a message using the private key. message := "test message" messageHash := wire.DoubleSha256([]byte(message)) signature, err := privKey.Sign(messageHash) if err != nil { fmt.Println(err) return } // Serialize and display the signature. fmt.Printf("Serialized Signature: %x\n", signature.Serialize()) // Verify the signature for the message using the public key. verified := signature.Verify(messageHash, pubKey) fmt.Printf("Signature Verified? %v\n", verified) // Output: // Serialized Signature: 304402201008e236fa8cd0f25df4482dddbb622e8a8b26ef0ba731719458de3ccd93805b022032f8ebe514ba5f672466eba334639282616bb3c2f0ab09998037513d1f9e3d6d // Signature Verified? true }
// This example demonstrates decrypting a message using a private key that is // first parsed from raw bytes. func Example_decryptMessage() { // Decode the hex-encoded private key. pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + "5ea381e3ce20a2c086a2e388230811") if err != nil { fmt.Println(err) return } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) ciphertext, err := hex.DecodeString("35f644fbfb208bc71e57684c3c8b437402ca" + "002047a2f1b38aa1a8f1d5121778378414f708fe13ebf7b4a7bb74407288c1958969" + "00207cf4ac6057406e40f79961c973309a892732ae7a74ee96cd89823913b8b8d650" + "a44166dc61ea1c419d47077b748a9c06b8d57af72deb2819d98a9d503efc59fc8307" + "d14174f8b83354fac3ff56075162") // Try decrypting the message. plaintext, err := btcec.Decrypt(privKey, ciphertext) if err != nil { fmt.Println(err) return } fmt.Println(string(plaintext)) // Output: // test message }
// PrivKey returns the private key for the address. It can fail if the address // manager is watching-only or locked, or the address does not have any keys. // // This is part of the ManagedPubKeyAddress interface implementation. func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) { // No private keys are available for a watching-only address manager. if a.manager.watchingOnly { return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil) } a.manager.mtx.Lock() defer a.manager.mtx.Unlock() // Account manager must be unlocked to decrypt the private key. if a.manager.locked { return nil, managerError(ErrLocked, errLocked, nil) } // Decrypt the key as needed. Also, make sure it's a copy since the // private key stored in memory can be cleared at any time. Otherwise // the returned private key could be invalidated from under the caller. privKeyCopy, err := a.unlock(a.manager.cryptoKeyPriv) if err != nil { return nil, err } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyCopy) zero.Bytes(privKeyCopy) return privKey, nil }
// ECPrivKey converts the extended key to a btcec private key and returns it. // As you might imagine this is only possible if the extended key is a private // extended key (as determined by the IsPrivate function). The ErrNotPrivExtKey // error will be returned if this function is called on a public extended key. func (k *ExtendedKey) ECPrivKey() (*btcec.PrivateKey, error) { if !k.isPrivate { return nil, ErrNotPrivExtKey } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), k.key) return privKey, nil }
func TestPrivKeys(t *testing.T) { tests := []struct { name string key []byte }{ { name: "check curve", key: []byte{ 0xea, 0xf0, 0x2c, 0xa3, 0x48, 0xc5, 0x24, 0xe6, 0x39, 0x26, 0x55, 0xba, 0x4d, 0x29, 0x60, 0x3c, 0xd1, 0xa7, 0x34, 0x7d, 0x9d, 0x65, 0xcf, 0xe9, 0x3c, 0xe1, 0xeb, 0xff, 0xdc, 0xa2, 0x26, 0x94, }, }, } for _, test := range tests { priv, pub := btcec.PrivKeyFromBytes(btcec.S256(), test.key) _, err := btcec.ParsePubKey( pub.SerializeUncompressed(), btcec.S256()) if err != nil { t.Errorf("%s privkey: %v", test.name, err) continue } hash := []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9} sig, err := priv.Sign(hash) if err != nil { t.Errorf("%s could not sign: %v", test.name, err) continue } if !sig.Verify(hash, pub) { t.Errorf("%s could not verify: %v", test.name, err) continue } serializedKey := priv.Serialize() if !bytes.Equal(serializedKey, test.key) { t.Errorf("%s unexpected serialized bytes - got: %x, "+ "want: %x", test.name, serializedKey, test.key) } } }
// This example demonstrates encrypting a message for a public key that is first // parsed from raw bytes, then decrypting it using the corresponding private key. func Example_encryptMessage() { // Decode the hex-encoded pubkey of the recipient. pubKeyBytes, err := hex.DecodeString("04115c42e757b2efb7671c578530ec191a1" + "359381e6a71127a9d37c486fd30dae57e76dc58f693bd7e7010358ce6b165e483a29" + "21010db67ac11b1b51b651953d2") // uncompressed pubkey if err != nil { fmt.Println(err) return } pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) if err != nil { fmt.Println(err) return } // Encrypt a message decryptable by the private key corresponding to pubKey message := "test message" ciphertext, err := btcec.Encrypt(pubKey, []byte(message)) if err != nil { fmt.Println(err) return } // Decode the hex-encoded private key. pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + "5ea381e3ce20a2c086a2e388230811") if err != nil { fmt.Println(err) return } // note that we already have corresponding pubKey privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) // Try decrypting and verify if it's the same message. plaintext, err := btcec.Decrypt(privKey, ciphertext) if err != nil { fmt.Println(err) return } fmt.Println(string(plaintext)) // Output: // test message }
// Test 2: Byte compatibility with Pyelliptic func TestCiphering(t *testing.T) { pb, _ := hex.DecodeString("fe38240982f313ae5afb3e904fb8215fb11af1200592b" + "fca26c96c4738e4bf8f") privkey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pb) in := []byte("This is just a test.") out, _ := hex.DecodeString("b0d66e5adaa5ed4e2f0ca68e17b8f2fc02ca002009e3" + "3487e7fa4ab505cf34d98f131be7bd258391588ca7804acb30251e71a04e0020ecf" + "df0f84608f8add82d7353af780fbb28868c713b7813eb4d4e61f7b75d7534dd9856" + "9b0ba77cf14348fcff80fee10e11981f1b4be372d93923e9178972f69937ec850ed" + "6c3f11ff572ddd5b2bedf9f9c0b327c54da02a28fcdce1f8369ffec") dec, err := btcec.Decrypt(privkey, out) if err != nil { t.Fatal("failed to decrypt:", err) } if !bytes.Equal(in, dec) { t.Error("decrypted data doesn't match original") } }
// DecodeWIF creates a new WIF structure by decoding the string encoding of // the import format. // // The WIF string must be a base58-encoded string of the following byte // sequence: // // * 1 byte to identify the network, must be 0x80 for mainnet or 0xef for // either testnet3 or the regression test network // * 32 bytes of a binary-encoded, big-endian, zero-padded private key // * Optional 1 byte (equal to 0x01) if the address being imported or exported // was created by taking the RIPEMD160 after SHA256 hash of a serialized // compressed (33-byte) public key // * 4 bytes of checksum, must equal the first four bytes of the double SHA256 // of every byte before the checksum in this sequence // // If the base58-decoded byte sequence does not match this, DecodeWIF will // return a non-nil error. ErrMalformedPrivateKey is returned when the WIF // is of an impossible length or the expected compressed pubkey magic number // does not equal the expected value of 0x01. ErrChecksumMismatch is returned // if the expected WIF checksum does not match the calculated checksum. func DecodeWIF(wif string) (*WIF, error) { decoded := base58.Decode(wif) decodedLen := len(decoded) var compress bool // Length of base58 decoded WIF must be 32 bytes + an optional 1 byte // (0x01) if compressed, plus 1 byte for netID + 4 bytes of checksum. switch decodedLen { case 1 + btcec.PrivKeyBytesLen + 1 + 4: if decoded[33] != compressMagic { return nil, ErrMalformedPrivateKey } compress = true case 1 + btcec.PrivKeyBytesLen + 4: compress = false default: return nil, ErrMalformedPrivateKey } // Checksum is first four bytes of double SHA256 of the identifier byte // and privKey. Verify this matches the final 4 bytes of the decoded // private key. var tosum []byte if compress { tosum = decoded[:1+btcec.PrivKeyBytesLen+1] } else { tosum = decoded[:1+btcec.PrivKeyBytesLen] } cksum := wire.DoubleSha256(tosum)[:4] if !bytes.Equal(cksum, decoded[decodedLen-4:]) { return nil, ErrChecksumMismatch } netID := decoded[0] privKeyBytes := decoded[1 : 1+btcec.PrivKeyBytesLen] privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) return &WIF{privKey, compress, netID}, nil }
func TestRFC6979(t *testing.T) { // Test vectors matching Trezor and CoreBitcoin implementations. // - https://github.com/trezor/trezor-crypto/blob/9fea8f8ab377dc514e40c6fd1f7c89a74c1d8dc6/tests.c#L432-L453 // - https://github.com/oleganza/CoreBitcoin/blob/e93dd71207861b5bf044415db5fa72405e7d8fbc/CoreBitcoin/BTCKey%2BTests.m#L23-L49 tests := []struct { key string msg string nonce string signature string }{ { "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", "sample", "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", "3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124", }, { // This signature hits the case when S is higher than halforder. // If S is not canonicalized (lowered by halforder), this test will fail. "0000000000000000000000000000000000000000000000000000000000000001", "Satoshi Nakamoto", "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15", "3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5", }, { "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "Satoshi Nakamoto", "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90", "3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5", }, { "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", "Alan Turing", "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1", "304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea", }, { "0000000000000000000000000000000000000000000000000000000000000001", "All those moments will be lost in time, like tears in rain. Time to die...", "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3", "30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21", }, { "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d", "3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6", }, } for i, test := range tests { privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), decodeHex(test.key)) hash := fastsha256.Sum256([]byte(test.msg)) // Ensure deterministically generated nonce is the expected value. gotNonce := btcec.TstNonceRFC6979(privKey.D, hash[:]).Bytes() wantNonce := decodeHex(test.nonce) if !bytes.Equal(gotNonce, wantNonce) { t.Errorf("NonceRFC6979 #%d (%s): Nonce is incorrect: "+ "%x (expected %x)", i, test.msg, gotNonce, wantNonce) continue } // Ensure deterministically generated signature is the expected value. gotSig, err := privKey.Sign(hash[:]) if err != nil { t.Errorf("Sign #%d (%s): unexpected error: %v", i, test.msg, err) continue } gotSigBytes := gotSig.Serialize() wantSigBytes := decodeHex(test.signature) if !bytes.Equal(gotSigBytes, wantSigBytes) { t.Errorf("Sign #%d (%s): mismatched signature: %x "+ "(expected %x)", i, test.msg, gotSigBytes, wantSigBytes) continue } } }
// This example demonstrates manually creating and signing a redeem transaction. func ExampleSignTxOutput() { // Ordinarily the private key would come from whatever storage mechanism // is being used, but for this example just hard code it. privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + "d4f8720ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) pubKeyHash := coinutil.Hash160(pubKey.SerializeCompressed()) addr, err := coinutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } // For this example, create a fake transaction that represents what // would ordinarily be the real transaction that is being spent. It // contains a single output that pays to address in the amount of 1 BTC. originTx := wire.NewMsgTx() prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0)) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { fmt.Println(err) return } txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) originTxHash := originTx.TxSha() // Create the transaction to redeem the fake transaction. redeemTx := wire.NewMsgTx() // Add the input(s) the redeeming transaction will spend. There is no // signature script at this point since it hasn't been created or signed // yet, hence nil is provided for it. prevOut = wire.NewOutPoint(&originTxHash, 0) txIn = wire.NewTxIn(prevOut, nil) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, // but for this example don't bother. txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) // Sign the redeeming transaction. lookupKey := func(a coinutil.Address) (*btcec.PrivateKey, bool, error) { // Ordinarily this function would involve looking up the private // key for the provided address, but since the only thing being // signed in this example uses the address associated with the // private key from above, simply return it with the compressed // flag set since the address is using the associated compressed // public key. // // NOTE: If you want to prove the code is actually signing the // transaction properly, uncomment the following line which // intentionally returns an invalid key to sign with, which in // turn will result in a failure during the script execution // when verifying the signature. // // privKey.D.SetInt64(12345) // return privKey, true, nil } // Notice that the script database parameter is nil here since it isn't // used. It must be specified when pay-to-script-hash transactions are // being signed. sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil, nil) if err != nil { fmt.Println(err) return } redeemTx.TxIn[0].SignatureScript = sigScript // Prove that the transaction has been validly signed by executing the // script pair. flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, nil) if err != nil { fmt.Println(err) return } if err := vm.Execute(); err != nil { fmt.Println(err) return } fmt.Println("Transaction successfully signed") // Output: // Transaction successfully signed }
// Test the sigscript generation for valid and invalid inputs, all // hashTypes, and with and without compression. This test creates // sigscripts to spend fake coinbase inputs, as sigscripts cannot be // created for the MsgTxs in txTests, since they come from the blockchain // and we don't have the private keys. func TestSignatureScript(t *testing.T) { t.Parallel() privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyD) nexttest: for i := range sigScriptTests { tx := wire.NewMsgTx() output := wire.NewTxOut(500, []byte{txscript.OP_RETURN}) tx.AddTxOut(output) for _ = range sigScriptTests[i].inputs { txin := wire.NewTxIn(coinbaseOutPoint, nil) tx.AddTxIn(txin) } var script []byte var err error for j := range tx.TxIn { var idx int if sigScriptTests[i].inputs[j].indexOutOfRange { t.Errorf("at test %v", sigScriptTests[i].name) idx = len(sigScriptTests[i].inputs) } else { idx = j } script, err = txscript.SignatureScript(tx, idx, sigScriptTests[i].inputs[j].txout.PkScript, sigScriptTests[i].hashType, privKey, sigScriptTests[i].compress) if (err == nil) != sigScriptTests[i].inputs[j].sigscriptGenerates { if err == nil { t.Errorf("passed test '%v' incorrectly", sigScriptTests[i].name) } else { t.Errorf("failed test '%v': %v", sigScriptTests[i].name, err) } continue nexttest } if !sigScriptTests[i].inputs[j].sigscriptGenerates { // done with this test continue nexttest } tx.TxIn[j].SignatureScript = script } // If testing using a correct sigscript but for an incorrect // index, use last input script for first input. Requires > 0 // inputs for test. if sigScriptTests[i].scriptAtWrongIndex { tx.TxIn[0].SignatureScript = script sigScriptTests[i].inputs[0].inputValidates = false } // Validate tx input scripts scriptFlags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures for j := range tx.TxIn { vm, err := txscript.NewEngine(sigScriptTests[i]. inputs[j].txout.PkScript, tx, j, scriptFlags, nil) if err != nil { t.Errorf("cannot create script vm for test %v: %v", sigScriptTests[i].name, err) continue nexttest } err = vm.Execute() if (err == nil) != sigScriptTests[i].inputs[j].inputValidates { if err == nil { t.Errorf("passed test '%v' validation incorrectly: %v", sigScriptTests[i].name, err) } else { t.Errorf("failed test '%v' validation: %v", sigScriptTests[i].name, err) } continue nexttest } } } }
func TestWatchingWalletExport(t *testing.T) { createdAt := makeBS(0) w, err := New(dummyDir, "A wallet for testing.", []byte("banana"), tstNetParams, createdAt) if err != nil { t.Error("Error creating new wallet: " + err.Error()) return } // Maintain a set of the active addresses in the wallet. activeAddrs := make(map[addressKey]struct{}) // Add root address. activeAddrs[getAddressKey(w.LastChainedAddress())] = struct{}{} // Create watching wallet from w. ww, err := w.ExportWatchingWallet() if err != nil { t.Errorf("Could not create watching wallet: %v", err) return } // Verify correctness of wallet flags. if ww.flags.useEncryption { t.Errorf("Watching wallet marked as using encryption (but nothing to encrypt).") return } if !ww.flags.watchingOnly { t.Errorf("Wallet should be watching-only but is not marked so.") return } // Verify that all flags are set as expected. if ww.keyGenerator.flags.encrypted { t.Errorf("Watching root address should not be encrypted (nothing to encrypt)") return } if ww.keyGenerator.flags.hasPrivKey { t.Errorf("Watching root address marked as having a private key.") return } if !ww.keyGenerator.flags.hasPubKey { t.Errorf("Watching root address marked as missing a public key.") return } if ww.keyGenerator.flags.createPrivKeyNextUnlock { t.Errorf("Watching root address marked as needing a private key to be generated later.") return } for apkh, waddr := range ww.addrMap { switch addr := waddr.(type) { case *btcAddress: if addr.flags.encrypted { t.Errorf("Chained address should not be encrypted (nothing to encrypt)") return } if addr.flags.hasPrivKey { t.Errorf("Chained address marked as having a private key.") return } if !addr.flags.hasPubKey { t.Errorf("Chained address marked as missing a public key.") return } if addr.flags.createPrivKeyNextUnlock { t.Errorf("Chained address marked as needing a private key to be generated later.") return } case *scriptAddress: t.Errorf("Chained address was a script!") return default: t.Errorf("Chained address unknown type!") return } if _, ok := activeAddrs[apkh]; !ok { t.Errorf("Address from watching wallet not found in original wallet.") return } delete(activeAddrs, apkh) } if len(activeAddrs) != 0 { t.Errorf("%v address(es) were not exported to watching wallet.", len(activeAddrs)) return } // Check that the new addresses created by each wallet match. The // original wallet is unlocked so addresses are chained with privkeys. if err := w.Unlock([]byte("banana")); err != nil { t.Errorf("Unlocking original wallet failed: %v", err) } // Test that ExtendActiveAddresses for the watching wallet match // manually requested addresses of the original wallet. var newAddrs []coinutil.Address for i := 0; i < 10; i++ { addr, err := w.NextChainedAddress(createdAt) if err != nil { t.Errorf("Cannot get next chained address for original wallet: %v", err) return } newAddrs = append(newAddrs, addr) } newWWAddrs, err := ww.ExtendActiveAddresses(10) if err != nil { t.Errorf("Cannot extend active addresses for watching wallet: %v", err) return } for i := range newAddrs { if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() { t.Errorf("Extended active addresses do not match manually requested addresses.") return } } // Test ExtendActiveAddresses for the original wallet after manually // requesting addresses for the watching wallet. newWWAddrs = nil for i := 0; i < 10; i++ { addr, err := ww.NextChainedAddress(createdAt) if err != nil { t.Errorf("Cannot get next chained address for watching wallet: %v", err) return } newWWAddrs = append(newWWAddrs, addr) } newAddrs, err = w.ExtendActiveAddresses(10) if err != nil { t.Errorf("Cannot extend active addresses for original wallet: %v", err) return } for i := range newAddrs { if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() { t.Errorf("Extended active addresses do not match manually requested addresses.") return } } // Test (de)serialization of watching wallet. buf := new(bytes.Buffer) _, err = ww.WriteTo(buf) if err != nil { t.Errorf("Cannot write watching wallet: %v", err) return } ww2 := new(Store) _, err = ww2.ReadFrom(buf) if err != nil { t.Errorf("Cannot read watching wallet: %v", err) return } // Check that (de)serialized watching wallet matches the exported wallet. if !reflect.DeepEqual(ww, ww2) { t.Error("Exported and read-in watching wallets do not match.") return } // Verify that nonsensical functions fail with correct error. if err := ww.Lock(); err != ErrWatchingOnly { t.Errorf("Nonsensical func Lock returned no or incorrect error: %v", err) return } if err := ww.Unlock([]byte("banana")); err != ErrWatchingOnly { t.Errorf("Nonsensical func Unlock returned no or incorrect error: %v", err) return } generator, err := ww.Address(w.keyGenerator.Address()) if err != nil { t.Errorf("generator isnt' present in wallet") } gpk := generator.(PubKeyAddress) if _, err := gpk.PrivKey(); err != ErrWatchingOnly { t.Errorf("Nonsensical func AddressKey returned no or incorrect error: %v", err) return } if _, err := ww.ExportWatchingWallet(); err != ErrWatchingOnly { t.Errorf("Nonsensical func ExportWatchingWallet returned no or incorrect error: %v", err) return } pk, _ := btcec.PrivKeyFromBytes(btcec.S256(), make([]byte, 32)) wif, err := coinutil.NewWIF(pk, tstNetParams, true) if err != nil { t.Fatal(err) } if _, err := ww.ImportPrivateKey(wif, createdAt); err != ErrWatchingOnly { t.Errorf("Nonsensical func ImportPrivateKey returned no or incorrect error: %v", err) return } }