|
@ -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 { |
|
|