lbcutil/wif.go
Dave Collins 22c91fa80a Update for recent chainhash-related API changes. (#78)
This updates all code in the main package and subpackages to make use of
the new chainhash package since the old wire.ShaHash type and functions
have been removed in favor of the abstracted package.

Also, since this required API changes anyways and the hash algorithm is
no longer tied specifically to SHA, all other functions throughout the
code base which had "Sha" in their name have been changed to Hash so
they are not incorrectly implying the hash algorithm.

The following is an overview of the changes:

- Update all references to wire.ShaHash to the new chainhash.Hash type
- Rename the following functions and update all references:
  - Block.Sha -> Hash
  - Block.TxSha -> TxHash
  - Tx.Sha -> Hash
  - bloom.Filter.AddShaHash -> AddHash
- Rename all variables that included sha in their name to include hash
  instead
- Add license headers to coinset package files
2016-08-08 12:38:16 -05:00

170 lines
6.2 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package btcutil
import (
"bytes"
"errors"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcutil/base58"
)
// ErrMalformedPrivateKey describes an error where a WIF-encoded private
// key cannot be decoded due to being improperly formatted. This may occur
// if the byte length is incorrect or an unexpected magic number was
// encountered.
var ErrMalformedPrivateKey = errors.New("malformed private key")
// compressMagic is the magic byte used to identify a WIF encoding for
// an address created from a compressed serialized public key.
const compressMagic byte = 0x01
// WIF contains the individual components described by the Wallet Import Format
// (WIF). A WIF string is typically used to represent a private key and its
// associated address in a way that may be easily copied and imported into or
// exported from wallet software. WIF strings may be decoded into this
// structure by calling DecodeWIF or created with a user-provided private key
// by calling NewWIF.
type WIF struct {
// PrivKey is the private key being imported or exported.
PrivKey *btcec.PrivateKey
// CompressPubKey specifies whether the address controlled by the
// imported or exported private key was created by hashing a
// compressed (33-byte) serialized public key, rather than an
// uncompressed (65-byte) one.
CompressPubKey bool
// netID is the bitcoin network identifier byte used when
// WIF encoding the private key.
netID byte
}
// NewWIF creates a new WIF structure to export an address and its private key
// as a string encoded in the Wallet Import Format. The compress argument
// specifies whether the address intended to be imported or exported was created
// by serializing the public key compressed rather than uncompressed.
func NewWIF(privKey *btcec.PrivateKey, net *chaincfg.Params, compress bool) (*WIF, error) {
if net == nil {
return nil, errors.New("no network")
}
return &WIF{privKey, compress, net.PrivateKeyID}, nil
}
// IsForNet returns whether or not the decoded WIF structure is associated
// with the passed bitcoin network.
func (w *WIF) IsForNet(net *chaincfg.Params) bool {
return w.netID == net.PrivateKeyID
}
// 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 := chainhash.DoubleHashB(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
}
// String creates the Wallet Import Format string encoding of a WIF structure.
// See DecodeWIF for a detailed breakdown of the format and requirements of
// a valid WIF string.
func (w *WIF) String() string {
// Precalculate size. Maximum number of bytes before base58 encoding
// is one byte for the network, 32 bytes of private key, possibly one
// extra byte if the pubkey is to be compressed, and finally four
// bytes of checksum.
encodeLen := 1 + btcec.PrivKeyBytesLen + 4
if w.CompressPubKey {
encodeLen++
}
a := make([]byte, 0, encodeLen)
a = append(a, w.netID)
// Pad and append bytes manually, instead of using Serialize, to
// avoid another call to make.
a = paddedAppend(btcec.PrivKeyBytesLen, a, w.PrivKey.D.Bytes())
if w.CompressPubKey {
a = append(a, compressMagic)
}
cksum := chainhash.DoubleHashB(a)[:4]
a = append(a, cksum...)
return base58.Encode(a)
}
// SerializePubKey serializes the associated public key of the imported or
// exported private key in either a compressed or uncompressed format. The
// serialization format chosen depends on the value of w.CompressPubKey.
func (w *WIF) SerializePubKey() []byte {
pk := (*btcec.PublicKey)(&w.PrivKey.PublicKey)
if w.CompressPubKey {
return pk.SerializeCompressed()
}
return pk.SerializeUncompressed()
}
// paddedAppend appends the src byte slice to dst, returning the new slice.
// If the length of the source is smaller than the passed size, leading zero
// bytes are appended to the dst slice before appending src.
func paddedAppend(size uint, dst, src []byte) []byte {
for i := 0; i < int(size)-len(src); i++ {
dst = append(dst, 0)
}
return append(dst, src...)
}