diff --git a/account.go b/account.go index b1b9415..832544e 100644 --- a/account.go +++ b/account.go @@ -307,14 +307,14 @@ func (a *Account) DumpPrivKeys() ([]string, error) { if !ok { continue } - encKey, err := pka.ExportPrivKey() + wif, err := pka.ExportPrivKey() if err != nil { // It would be nice to zero out the array here. However, // since strings in go are immutable, and we have no // control over the caller I don't think we can. :( return nil, err } - privkeys = append(privkeys, encKey) + privkeys = append(privkeys, wif.String()) } return privkeys, nil @@ -334,16 +334,20 @@ func (a *Account) DumpWIFPrivateKey(addr btcutil.Address) (string, error) { return "", fmt.Errorf("address %s is not a key type", addr) } - return pka.ExportPrivKey() + wif, err := pka.ExportPrivKey() + if err != nil { + return "", err + } + return wif.String(), nil } // ImportPrivateKey imports a private key to the account's wallet and // writes the new wallet to disk. -func (a *Account) ImportPrivateKey(pk []byte, compressed bool, - bs *wallet.BlockStamp, rescan bool) (string, error) { +func (a *Account) ImportPrivateKey(wif *btcutil.WIF, bs *wallet.BlockStamp, + rescan bool) (string, error) { // Attempt to import private key into wallet. - addr, err := a.Wallet.ImportPrivateKey(pk, compressed, bs) + addr, err := a.Wallet.ImportPrivateKey(wif, bs) if err != nil { return "", err } diff --git a/rpcserver.go b/rpcserver.go index 60102ab..7ac33c6 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -772,14 +772,14 @@ func ImportPrivKey(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return nil, &e } - pk, net, compressed, err := btcutil.DecodePrivateKey(cmd.PrivKey) - if err != nil || net != a.Net() { + wif, err := btcutil.DecodeWIF(cmd.PrivKey) + if err != nil || !wif.IsForNet(a.Net()) { return nil, &btcjson.ErrInvalidAddressOrKey } // Import the private key, handling any errors. bs := &wallet.BlockStamp{} - switch _, err := a.ImportPrivateKey(pk, compressed, bs, cmd.Rescan); err { + switch _, err := a.ImportPrivateKey(wif, bs, cmd.Rescan); err { case nil: // If the import was successful, reply with nil. return nil, nil @@ -1742,12 +1742,6 @@ type pendingTx struct { inputs []uint32 // list of inputs that care about this tx. } -// keyInfo is used to store provided keys in SignRawTransaction. -type keyInfo struct { - key *ecdsa.PrivateKey - compressed bool -} - // SignRawTransaction handles the signrawtransaction command. func SignRawTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { cmd, ok := icmd.(*btcjson.SignRawTransactionCmd) @@ -1854,13 +1848,12 @@ func SignRawTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { // Parse list of private keys, if present. If there are any keys here // they are the keys that we may use for signing. If empty we will // use any keys known to us already. - var keys map[string]keyInfo + var keys map[string]*btcutil.WIF if len(cmd.PrivKeys) != 0 { - keys = make(map[string]keyInfo) + keys = make(map[string]*btcutil.WIF) for _, key := range cmd.PrivKeys { - key, net, compressed, err := - btcutil.DecodePrivateKey(key) + wif, err := btcutil.DecodeWIF(key) if err != nil { return nil, &btcjson.Error{ Code: btcjson.ErrDeserialization.Code, @@ -1868,7 +1861,7 @@ func SignRawTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { } } - if net != cfg.Net() { + if !wif.IsForNet(cfg.Net()) { return nil, &btcjson.Error{ Code: btcjson.ErrDeserialization.Code, Message: "key network doesn't match " + @@ -1876,35 +1869,15 @@ func SignRawTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { } } - privk, pubk := btcec.PrivKeyFromBytes(btcec.S256(), - key) - - var addr btcutil.Address - if compressed { - pkc := pubk.SerializeCompressed() - addr, err = btcutil.NewAddressPubKey(pkc, - cfg.Net()) - if err != nil { - return nil, &btcjson.Error{ - Code: btcjson.ErrDeserialization.Code, - Message: err.Error(), - } - } - } else { - pku := pubk.SerializeUncompressed() - addr, err = btcutil.NewAddressPubKey(pku, - cfg.Net()) - if err != nil { - return nil, &btcjson.Error{ - Code: btcjson.ErrDeserialization.Code, - Message: err.Error(), - } + addr, err := btcutil.NewAddressPubKey(wif.SerializePubKey(), + cfg.Net()) + if err != nil { + return nil, &btcjson.Error{ + Code: btcjson.ErrDeserialization.Code, + Message: err.Error(), } } - keys[addr.EncodeAddress()] = keyInfo{ - key: privk.ToECDSA(), - compressed: compressed, - } + keys[addr.EncodeAddress()] = wif } } @@ -1984,12 +1957,12 @@ func SignRawTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { getKey := btcscript.KeyClosure(func(addr btcutil.Address) ( *ecdsa.PrivateKey, bool, error) { if len(keys) != 0 { - info, ok := keys[addr.EncodeAddress()] + wif, ok := keys[addr.EncodeAddress()] if !ok { return nil, false, errors.New("no key for address") } - return info.key, info.compressed, nil + return wif.PrivKey.ToECDSA(), wif.CompressPubKey, nil } address, err := AcctMgr.Address(addr) if err != nil { diff --git a/wallet/wallet.go b/wallet/wallet.go index 0e4398d..a4be9df 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -1333,16 +1333,17 @@ func (w *Wallet) SetBetterEarliestBlockHeight(height int32) { } } -// ImportPrivateKey creates a new encrypted btcAddress with a -// user-provided private key and adds it to the wallet. -func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStamp) (btcutil.Address, error) { +// ImportPrivateKey imports a WIF private key into the keystore. The imported +// address is created using either a compressed or uncompressed serialized +// public key, depending on the CompressPubKey bool of the WIF. +func (w *Wallet) ImportPrivateKey(wif *btcutil.WIF, bs *BlockStamp) (btcutil.Address, error) { if w.flags.watchingOnly { return nil, ErrWalletIsWatchingOnly } // First, must check that the key being imported will not result // in a duplicate address. - pkh := btcutil.Hash160(pubkeyFromPrivkey(privkey, compressed)) + pkh := btcutil.Hash160(wif.SerializePubKey()) if _, ok := w.addrMap[addressKey(pkh)]; ok { return nil, ErrDuplicate } @@ -1353,7 +1354,8 @@ func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStam } // Create new address with this private key. - btcaddr, err := newBtcAddress(w, privkey, nil, bs, compressed) + privKey := wif.PrivKey.Serialize() + btcaddr, err := newBtcAddress(w, privKey, nil, bs, wif.CompressPubKey) if err != nil { return nil, err } @@ -2067,8 +2069,8 @@ type PubKeyAddress interface { // It can fail if the wallet is watching only, the wallet is locked, // or the address doesn't have any keys. PrivKey() (*ecdsa.PrivateKey, error) - // ExportPrivKey exports the private key in WIF format as a string. - ExportPrivKey() (string, error) + // ExportPrivKey exports the WIF private key. + ExportPrivKey() (*btcutil.WIF, error) } // newBtcAddress initializes and returns a new address. privkey must @@ -2523,16 +2525,14 @@ func (a *btcAddress) PrivKey() (*ecdsa.PrivateKey, error) { }, nil } -// ExportPrivKey exports the private key in WIF format as a string. Implementing -// PubKeyAddress. -func (a *btcAddress) ExportPrivKey() (string, error) { +// ExportPrivKey exports the private key as a WIF for encoding as a string +// in the Wallet Import Formt. +func (a *btcAddress) ExportPrivKey() (*btcutil.WIF, error) { pk, err := a.PrivKey() if err != nil { - return "", err + return nil, err } - - return btcutil.EncodePrivateKey(pad(32, pk.D.Bytes()), - a.wallet.Net(), a.Compressed()) + return btcutil.NewWIF((*btcec.PrivateKey)(pk), a.wallet.Net(), a.Compressed()) } // watchingCopy creates a copy of an address without a private key. diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index f3b6863..2e4b38f 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -714,7 +714,12 @@ func TestWatchingWalletExport(t *testing.T) { t.Errorf("Nonsensical func ExportWatchingWallet returned no or incorrect error: %v", err) return } - if _, err := ww.ImportPrivateKey(make([]byte, 32), true, createdAt); err != ErrWalletIsWatchingOnly { + pk, _ := btcec.PrivKeyFromBytes(btcec.S256(), make([]byte, 32)) + wif, err := btcutil.NewWIF(pk, btcwire.MainNet, true) + if err != nil { + t.Fatal(err) + } + if _, err := ww.ImportPrivateKey(wif, createdAt); err != ErrWalletIsWatchingOnly { t.Errorf("Nonsensical func ImportPrivateKey returned no or incorrect error: %v", err) return } @@ -754,9 +759,13 @@ func TestImportPrivateKey(t *testing.T) { } // import priv key + wif, err := btcutil.NewWIF((*btcec.PrivateKey)(pk), btcwire.MainNet, false) + if err != nil { + t.Fatal(err) + } importHeight := int32(50) importedAt := &BlockStamp{Height: importHeight} - address, err := w.ImportPrivateKey(pad(32, pk.D.Bytes()), false, importedAt) + address, err := w.ImportPrivateKey(wif, importedAt) if err != nil { t.Error("importing private key: " + err.Error()) return