Browse Source

hd: comments and some cleanup

pull/1782/head
Ethan Buchman 7 years ago
parent
commit
87f2005fa8
2 changed files with 97 additions and 49 deletions
  1. +96
    -48
      hd/address.go
  2. +1
    -1
      hd/hd_test.go

+ 96
- 48
hd/address.go View File

@ -21,19 +21,30 @@ import (
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil/base58" "github.com/btcsuite/btcutil/base58"
"github.com/tendermint/go-crypto"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
) )
func ComputeAddress(pubKeyHex string, chainHex string, path string, index int32) string {
/*
This file implements BIP32 HD wallets.
Note it only works for SECP256k1 keys.
It also includes some Bitcoin specific utility functions.
*/
// ComputeBTCAddress returns the BTC address using the pubKeyHex and chainCodeHex
// for the given path and index.
func ComputeBTCAddress(pubKeyHex string, chainCodeHex string, path string, index int32) string {
pubKeyBytes := DerivePublicKeyForPath( pubKeyBytes := DerivePublicKeyForPath(
HexDecode(pubKeyHex), HexDecode(pubKeyHex),
HexDecode(chainHex),
HexDecode(chainCodeHex),
fmt.Sprintf("%v/%v", path, index), fmt.Sprintf("%v/%v", path, index),
) )
return AddrFromPubKeyBytes(pubKeyBytes)
return BTCAddrFromPubKeyBytes(pubKeyBytes)
} }
// ComputePrivateKey returns the private key using the master mprivHex and chainCodeHex
// for the given path and index.
func ComputePrivateKey(mprivHex string, chainHex string, path string, index int32) string { func ComputePrivateKey(mprivHex string, chainHex string, path string, index int32) string {
privKeyBytes := DerivePrivateKeyForPath( privKeyBytes := DerivePrivateKeyForPath(
HexDecode(mprivHex), HexDecode(mprivHex),
@ -43,12 +54,14 @@ func ComputePrivateKey(mprivHex string, chainHex string, path string, index int3
return HexEncode(privKeyBytes) return HexEncode(privKeyBytes)
} }
func ComputeAddressForPrivKey(privKey string) string {
// ComputeBTCAddressForPrivKey returns the Bitcoin address for the given privKey.
func ComputeBTCAddressForPrivKey(privKey string) string {
pubKeyBytes := PubKeyBytesFromPrivKeyBytes(HexDecode(privKey), true) pubKeyBytes := PubKeyBytesFromPrivKeyBytes(HexDecode(privKey), true)
return AddrFromPubKeyBytes(pubKeyBytes)
return BTCAddrFromPubKeyBytes(pubKeyBytes)
} }
func SignMessage(privKey string, message string, compress bool) string {
// SignBTCMessage signs a "Bitcoin Signed Message".
func SignBTCMessage(privKey string, message string, compress bool) string {
prefixBytes := []byte("Bitcoin Signed Message:\n") prefixBytes := []byte("Bitcoin Signed Message:\n")
messageBytes := []byte(message) messageBytes := []byte(message)
bytes := []byte{} bytes := []byte{}
@ -67,25 +80,28 @@ func SignMessage(privKey string, message string, compress bool) string {
PublicKey: ecdsaPubKey, PublicKey: ecdsaPubKey,
D: new(big.Int).SetBytes(privKeyBytes), D: new(big.Int).SetBytes(privKeyBytes),
} }
sigbytes, err := btcec.SignCompact(btcec.S256(), ecdsaPrivKey, crypto.Sha256(crypto.Sha256(bytes)), compress)
sigbytes, err := btcec.SignCompact(btcec.S256(), ecdsaPrivKey, CalcHash256(bytes), compress)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return base64.StdEncoding.EncodeToString(sigbytes) return base64.StdEncoding.EncodeToString(sigbytes)
} }
// returns MPK, Chain, and master secret in hex.
func ComputeMastersFromSeed(seed string) (string, string, string, string) {
secret, chain := I64([]byte("Bitcoin seed"), []byte(seed))
// ComputeMastersFromSeed returns the master public key, master secret, and chain code in hex.
func ComputeMastersFromSeed(seed string) (string, string, string) {
key, data := []byte("Bitcoin seed"), []byte(seed)
secret, chain := I64(key, data)
pubKeyBytes := PubKeyBytesFromPrivKeyBytes(secret, true) pubKeyBytes := PubKeyBytesFromPrivKeyBytes(secret, true)
return HexEncode(pubKeyBytes), HexEncode(secret), HexEncode(chain), HexEncode(secret)
return HexEncode(pubKeyBytes), HexEncode(secret), HexEncode(chain)
} }
// ComputeWIF returns the privKey in Wallet Import Format.
func ComputeWIF(privKey string, compress bool) string { func ComputeWIF(privKey string, compress bool) string {
return WIFFromPrivKeyBytes(HexDecode(privKey), compress) return WIFFromPrivKeyBytes(HexDecode(privKey), compress)
} }
func ComputeTxId(rawTxHex string) string {
// ComputeBTCTxId returns the bitcoin transaction ID.
func ComputeBTCTxId(rawTxHex string) string {
return HexEncode(ReverseBytes(CalcHash256(HexDecode(rawTxHex)))) return HexEncode(ReverseBytes(CalcHash256(HexDecode(rawTxHex))))
} }
@ -103,7 +119,11 @@ func printKeyInfo(privKeyBytes []byte, pubKeyBytes []byte, chain []byte) {
} }
*/ */
func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []byte {
//-------------------------------------------------------------------
// DerivePrivateKeyForPath derives the private key by following the path from privKeyBytes,
// using the given chainCode.
func DerivePrivateKeyForPath(privKeyBytes []byte, chainCode []byte, path string) []byte {
data := privKeyBytes data := privKeyBytes
parts := strings.Split(path, "/") parts := strings.Split(path, "/")
for _, part := range parts { for _, part := range parts {
@ -119,13 +139,15 @@ func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []b
if i < 0 { if i < 0 {
panic(errors.New("index too large.")) panic(errors.New("index too large."))
} }
data, chain = DerivePrivateKey(data, chain, uint32(i), prime)
data, chainCode = DerivePrivateKey(data, chainCode, uint32(i), prime)
//printKeyInfo(data, nil, chain) //printKeyInfo(data, nil, chain)
} }
return data return data
} }
func DerivePublicKeyForPath(pubKeyBytes []byte, chain []byte, path string) []byte {
// DerivePublicKeyForPath derives the public key by following the path from pubKeyBytes
// using the given chainCode.
func DerivePublicKeyForPath(pubKeyBytes []byte, chainCode []byte, path string) []byte {
data := pubKeyBytes data := pubKeyBytes
parts := strings.Split(path, "/") parts := strings.Split(path, "/")
for _, part := range parts { for _, part := range parts {
@ -140,36 +162,42 @@ func DerivePublicKeyForPath(pubKeyBytes []byte, chain []byte, path string) []byt
if i < 0 { if i < 0 {
panic(errors.New("index too large.")) panic(errors.New("index too large."))
} }
data, chain = DerivePublicKey(data, chain, uint32(i))
//printKeyInfo(nil, data, chain)
data, chainCode = DerivePublicKey(data, chainCode, uint32(i))
//printKeyInfo(nil, data, chainCode)
} }
return data return data
} }
func DerivePrivateKey(privKeyBytes []byte, chain []byte, i uint32, prime bool) ([]byte, []byte) {
// DerivePrivateKey derives the private key with index and chainCode.
// If prime is true, the derivation is 'hardened'.
// It returns the new private key and new chain code.
func DerivePrivateKey(privKeyBytes []byte, chainCode []byte, index uint32, prime bool) ([]byte, []byte) {
var data []byte var data []byte
if prime { if prime {
i = i | 0x80000000
index = index | 0x80000000
data = append([]byte{byte(0)}, privKeyBytes...) data = append([]byte{byte(0)}, privKeyBytes...)
} else { } else {
public := PubKeyBytesFromPrivKeyBytes(privKeyBytes, true) public := PubKeyBytesFromPrivKeyBytes(privKeyBytes, true)
data = public data = public
} }
data = append(data, uint32ToBytes(i)...)
data2, chain2 := I64(chain, data)
data = append(data, uint32ToBytes(index)...)
data2, chainCode2 := I64(chainCode, data)
x := addScalars(privKeyBytes, data2) x := addScalars(privKeyBytes, data2)
return x, chain2
return x, chainCode2
} }
func DerivePublicKey(pubKeyBytes []byte, chain []byte, i uint32) ([]byte, []byte) {
// DerivePublicKey derives the public key with index and chainCode.
// It returns the new public key and new chain code.
func DerivePublicKey(pubKeyBytes []byte, chainCode []byte, index uint32) ([]byte, []byte) {
data := []byte{} data := []byte{}
data = append(data, pubKeyBytes...) data = append(data, pubKeyBytes...)
data = append(data, uint32ToBytes(i)...)
data2, chain2 := I64(chain, data)
data = append(data, uint32ToBytes(index)...)
data2, chainCode2 := I64(chainCode, data)
data2p := PubKeyBytesFromPrivKeyBytes(data2, true) data2p := PubKeyBytesFromPrivKeyBytes(data2, true)
return addPoints(pubKeyBytes, data2p), chain2
return addPoints(pubKeyBytes, data2p), chainCode2
} }
// eliptic curve pubkey addition
func addPoints(a []byte, b []byte) []byte { func addPoints(a []byte, b []byte) []byte {
ap, err := btcec.ParsePubKey(a, btcec.S256()) ap, err := btcec.ParsePubKey(a, btcec.S256())
if err != nil { if err != nil {
@ -188,6 +216,7 @@ func addPoints(a []byte, b []byte) []byte {
return sum.SerializeCompressed() return sum.SerializeCompressed()
} }
// modular big endian addition
func addScalars(a []byte, b []byte) []byte { func addScalars(a []byte, b []byte) []byte {
aInt := new(big.Int).SetBytes(a) aInt := new(big.Int).SetBytes(a)
bInt := new(big.Int).SetBytes(b) bInt := new(big.Int).SetBytes(b)
@ -204,15 +233,21 @@ func uint32ToBytes(i uint32) []byte {
return b[:] return b[:]
} }
//-------------------------------------------------------------------
// HexEncode encodes b in hex.
func HexEncode(b []byte) string { func HexEncode(b []byte) string {
return hex.EncodeToString(b) return hex.EncodeToString(b)
} }
// HexDecode hex decodes the str. If str is not valid hex
// it will return an empty byte slice.
func HexDecode(str string) []byte { func HexDecode(str string) []byte {
b, _ := hex.DecodeString(str) b, _ := hex.DecodeString(str)
return b return b
} }
// I64 returns the two halfs of the SHA512 HMAC of key and data.
func I64(key []byte, data []byte) ([]byte, []byte) { func I64(key []byte, data []byte) ([]byte, []byte) {
mac := hmac.New(sha512.New, key) mac := hmac.New(sha512.New, key)
mac.Write(data) mac.Write(data)
@ -220,27 +255,36 @@ func I64(key []byte, data []byte) ([]byte, []byte) {
return I[:32], I[32:] return I[:32], I[32:]
} }
// This returns a Bitcoin-like address.
func AddrFromPubKeyBytes(pubKeyBytes []byte) string {
prefix := byte(0x00) // TODO Make const or configurable
//-------------------------------------------------------------------
const (
btcPrefixPubKeyHash = byte(0x00)
btcPrefixPrivKey = byte(0x80)
)
// BTCAddrFromPubKeyBytes returns a B58 encoded Bitcoin mainnet address.
func BTCAddrFromPubKeyBytes(pubKeyBytes []byte) string {
versionPrefix := btcPrefixPubKeyHash // TODO Make const or configurable
h160 := CalcHash160(pubKeyBytes) h160 := CalcHash160(pubKeyBytes)
h160 = append([]byte{prefix}, h160...)
h160 = append([]byte{versionPrefix}, h160...)
checksum := CalcHash256(h160) checksum := CalcHash256(h160)
b := append(h160, checksum[:4]...) b := append(h160, checksum[:4]...)
return base58.Encode(b) return base58.Encode(b)
} }
func AddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) {
prefix := byte(0x00) // TODO Make const or configurable
// BTCAddrBytesFromPubKeyBytes returns a hex Bitcoin mainnet address and its checksum.
func BTCAddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) {
versionPrefix := btcPrefixPubKeyHash // TODO Make const or configurable
h160 := CalcHash160(pubKeyBytes) h160 := CalcHash160(pubKeyBytes)
_h160 := append([]byte{prefix}, h160...)
_h160 := append([]byte{versionPrefix}, h160...)
checksum = CalcHash256(_h160)[:4] checksum = CalcHash256(_h160)[:4]
return h160, checksum return h160, checksum
} }
// WIFFromPrivKeyBytes returns the privKeyBytes in Wallet Import Format.
func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string { func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string {
prefix := byte(0x80) // TODO Make const or configurable
bytes := append([]byte{prefix}, privKeyBytes...)
versionPrefix := btcPrefixPrivKey // TODO Make const or configurable
bytes := append([]byte{versionPrefix}, privKeyBytes...)
if compress { if compress {
bytes = append(bytes, byte(1)) bytes = append(bytes, byte(1))
} }
@ -249,6 +293,7 @@ func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string {
return base58.Encode(bytes) return base58.Encode(bytes)
} }
// PubKeyBytesFromPrivKeyBytes returns the optionally compressed public key bytes.
func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyBytes []byte) { func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyBytes []byte) {
x, y := btcec.S256().ScalarBaseMult(privKeyBytes) x, y := btcec.S256().ScalarBaseMult(privKeyBytes)
pub := &btcec.PublicKey{ pub := &btcec.PublicKey{
@ -263,27 +308,30 @@ func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyByte
return pub.SerializeUncompressed() return pub.SerializeUncompressed()
} }
// Calculate the hash of hasher over buf.
func CalcHash(buf []byte, hasher hash.Hash) []byte {
hasher.Write(buf)
//--------------------------------------------------------------
// CalcHash returns the hash of data using hasher.
func CalcHash(data []byte, hasher hash.Hash) []byte {
hasher.Write(data)
return hasher.Sum(nil) return hasher.Sum(nil)
} }
// calculate hash160 which is ripemd160(sha256(data))
func CalcHash160(buf []byte) []byte {
return CalcHash(CalcHash(buf, sha256.New()), ripemd160.New())
// CalcHash160 returns the ripemd160(sha256(data)).
func CalcHash160(data []byte) []byte {
return CalcHash(CalcHash(data, sha256.New()), ripemd160.New())
} }
// calculate hash256 which is sha256(sha256(data))
func CalcHash256(buf []byte) []byte {
return CalcHash(CalcHash(buf, sha256.New()), sha256.New())
// CalcHash256 returns the sha256(sha256(data)).
func CalcHash256(data []byte) []byte {
return CalcHash(CalcHash(data, sha256.New()), sha256.New())
} }
// calculate sha512(data)
func CalcSha512(buf []byte) []byte {
return CalcHash(buf, sha512.New())
// CalcSha512 returns the sha512(data).
func CalcSha512(data []byte) []byte {
return CalcHash(data, sha512.New())
} }
// ReverseBytes returns the buf in the opposite order
func ReverseBytes(buf []byte) []byte { func ReverseBytes(buf []byte) []byte {
var res []byte var res []byte
if len(buf) == 0 { if len(buf) == 0 {


+ 1
- 1
hd/hd_test.go View File

@ -112,7 +112,7 @@ func ifExit(err error, n int) {
func gocrypto(seed []byte) ([]byte, []byte, []byte) { func gocrypto(seed []byte) ([]byte, []byte, []byte) {
_, priv, ch, _ := ComputeMastersFromSeed(string(seed))
_, priv, ch := ComputeMastersFromSeed(string(seed))
privBytes := DerivePrivateKeyForPath( privBytes := DerivePrivateKeyForPath(
HexDecode(priv), HexDecode(priv),


Loading…
Cancel
Save