// Copyright (c) 2013 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package btcutil import ( "bytes" "code.google.com/p/go.crypto/ripemd160" "errors" "github.com/conformal/btcwire" ) // ErrUnknownNet describes an error where the Bitcoin network is // not recognized. var ErrUnknownNet = errors.New("unrecognized bitcoin network") // ErrMalformedAddress describes an error where an address is improperly // formatted, either due to an incorrect length of the hashed pubkey or // a non-matching checksum. var ErrMalformedAddress = errors.New("malformed address") // ErrMalformedPrivateKey describes an error where an address is improperly // formatted, either due to an incorrect length of the private key or // a non-matching checksum. var ErrMalformedPrivateKey = errors.New("malformed private key") // Constants used to specify which network a payment address belongs // to. Mainnet address cannot be used on the Testnet, and vice versa. const ( // MainNetAddr is the address identifier for MainNet MainNetAddr = 0x00 // TestNetAddr is the address identifier for TestNet TestNetAddr = 0x6f // MainNetKey is the key identifier for MainNet MainNetKey = 0x80 // TestNetKey is the key identifier for TestNet TestNetKey = 0xef ) // EncodeAddress takes a 20-byte raw payment address (hash160 of a pubkey) // and the Bitcoin network to create a human-readable payment address string. func EncodeAddress(addrHash []byte, net btcwire.BitcoinNet) (encoded string, err error) { if len(addrHash) != ripemd160.Size { return "", ErrMalformedAddress } var netID byte switch net { case btcwire.MainNet: netID = MainNetAddr case btcwire.TestNet3: netID = TestNetAddr default: return "", ErrUnknownNet } tosum := append([]byte{netID}, addrHash...) cksum := btcwire.DoubleSha256(tosum) // Address before base58 encoding is 1 byte for netID, 20 bytes for // hash, plus 4 bytes of checksum. a := make([]byte, 25, 25) a[0] = netID copy(a[1:], addrHash) copy(a[21:], cksum[:4]) return Base58Encode(a), nil } // DecodeAddress decodes a human-readable payment address string // returning the 20-byte decoded address, along with the Bitcoin // network for the address. func DecodeAddress(addr string) (addrHash []byte, net btcwire.BitcoinNet, err error) { decoded := Base58Decode(addr) // Length of decoded address must be 20 bytes + 1 byte for a network // identifier byte + 4 bytes of checksum. if len(decoded) != ripemd160.Size+5 { return nil, 0x00, ErrMalformedAddress } switch decoded[0] { case MainNetAddr: net = btcwire.MainNet case TestNetAddr: net = btcwire.TestNet3 default: return nil, 0, ErrUnknownNet } // Checksum is first four bytes of double SHA256 of the network byte // and addrHash. Verify this matches the final 4 bytes of the decoded // address. tosum := decoded[:ripemd160.Size+1] cksum := btcwire.DoubleSha256(tosum)[:4] if !bytes.Equal(cksum, decoded[len(decoded)-4:]) { return nil, net, ErrMalformedAddress } addrHash = make([]byte, ripemd160.Size, ripemd160.Size) copy(addrHash, decoded[1:ripemd160.Size+1]) return addrHash, net, nil } // EncodePrivateKey takes a 32-byte private key and encodes it into the // Wallet Import Format (WIF). func EncodePrivateKey(privKey []byte, net btcwire.BitcoinNet) (string, error) { if len(privKey) != 32 { return "", ErrMalformedPrivateKey } var netID byte switch net { case btcwire.MainNet: netID = MainNetKey case btcwire.TestNet3: netID = TestNetKey default: return "", ErrUnknownNet } tosum := append([]byte{netID}, privKey...) cksum := btcwire.DoubleSha256(tosum) // Private key before base58 encoding is 1 byte for netID, 32 bytes for // privKey, plus 4 bytes of checksum. a := make([]byte, 37, 37) a[0] = netID copy(a[1:], privKey) copy(a[32+1:], cksum[:4]) return Base58Encode(a), nil } // DecodePrivateKey takes a Wallet Import Format (WIF) string and // decodes into a 32-byte private key. func DecodePrivateKey(wif string) ([]byte, btcwire.BitcoinNet, error) { decoded := Base58Decode(wif) // Length of decoded privkey must be 32 bytes + 1 byte for 0x80 // + 4 bytes of checksum if len(decoded) != 32+5 { return nil, 0, ErrMalformedPrivateKey } var net btcwire.BitcoinNet switch decoded[0] { case MainNetKey: net = btcwire.MainNet case TestNetKey: net = btcwire.TestNet3 default: return nil, 0, ErrUnknownNet } // 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. tosum := decoded[:32+1] cksum := btcwire.DoubleSha256(tosum)[:4] if !bytes.Equal(cksum, decoded[len(decoded)-4:]) { return nil, 0, ErrMalformedPrivateKey } privKey := make([]byte, 32, 32) copy(privKey[:], decoded[1:32+1]) return privKey, net, nil }