如何使用 Cosmos SDK 根据种子短语生成 HD 钱包密钥和地址?

huangapple go评论102阅读模式
英文:

How to generate hd wallet keys & addresses given seed phrase with cosmos sdk?

问题

我正在尝试使用 Cosmos SDK 生成 HD 钱包的私钥、公钥和地址。以下是在 Python 中生成预期结果的等效实现,但是在尝试使用 Cosmos SDK 的 Golang 版本生成时,它不会生成相同的密钥。非常感谢提供与 Python 实现等效的 Golang 版本的任何输入。谢谢。

Python

seed = Mnemonic.to_seed("blast about old claw current first paste risk involve victory edit current", passphrase="")
print("Seed: ", seed.hex())
purpose = 44
coinType = 118
account = 0
change = 0

hdwallet = HDWallet()
hdwallet.from_seed(seed=seed.hex())

for addressIndex in range(1):
    hdwallet.clean_derivation()
    hdwallet.from_index(purpose, hardened=True)
    hdwallet.from_index(coinType, hardened=True)
    hdwallet.from_index(account, hardened=True)
    hdwallet.from_index(change)
    hdwallet.from_index(addressIndex, hardened=True)

    print("---")
    print("Derivation Path: ", hdwallet.path())
    print("Private Key: ", hdwallet.private_key())
    print("Public Key: ", hdwallet.public_key())

    readdr_bytes = b"\x04" + bytearray.fromhex(hdwallet.public_key())
    readdr_bytes5 = bech32.convertbits(readdr_bytes, 8, 5)
    wallet_addr = bech32.bech32_encode("atom", readdr_bytes5)
    print("Wallet Address: ", wallet_addr)

输出结果:

Derivation Path:  m/44'/118'/0'/0/0'
Private Key:  69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a
Public Key:  03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8
Wallet Address:  atom1qspau72rtj7g57v7lsjvmnna8vvqlvq56hcejj0m34sau0eph8mvr7qgl9avu

> GoLang(生成不同的密钥)

```go
import (
	"github.com/tendermint/tendermint/crypto/secp256k1"
	"github.com/cosmos/cosmos-sdk/crypto/hd"
	"github.com/cosmos/go-bip39"
	"github.com/decred/dcrd/bech32"
)

path := hd.BIP44Params{
	Purpose:      44,
	CoinType:     118,
	Account:      0,
	Change:       false,
	AddressIndex: 0,
}

seed := bip39.NewSeed("blast about old claw current first paste risk involve victory edit current","")

master, ch := hd.ComputeMastersFromSeed(seed)
priv, err := hd.DerivePrivateKeyForPath(master, ch, path.String())
if err != nil {
	log.Fatal(err)
}
var privKey = secp256k1.GenPrivKeySecp256k1(priv)
pubKey := privKey.PubKey()
fmt.Println(hex.EncodeToString(pubKey.Bytes()))

decodeString, _ := hex.DecodeString(fmt.Sprintf("04%x", pubKey.Bytes()))

// Convert test data to base32:
conv, err := bech32.ConvertBits(decodeString, 8, 5, true)
if err != nil {
	fmt.Println("Error:", err)
}
encoded, err := bech32.Encode("atom", conv)
if err != nil {
	fmt.Println("Error:", err)
}

// Show the encoded data.
fmt.Println("Atom address:", encoded)

输出结果:

---
Derivation Path:  m/44'/1022'/0'/0/0'
Private Key:  84925813eac8c1dc39f170e93b3bebe0bf961ac9c46507e858ce178a1a715c26
Public Key:  0204a0bad86cafed2daf1b4080a3e908afcf524e2a9c24e20817920c478d537cc1
Wallet Address:  atom1qsp3yaurlt463pl6pekgae4yudlcwk2dhxt93cxz5d5ymw3j8xmngaqef5j7p
英文:

I am trying to generate hd wallet private keys , public keys and addresess using cosmos sdk. Below is the equivalent implementation in python which generates the keys , address as expected but when trying to generated in golang using cosmos sdk it won't generate same keys. Any inputs for equivalent golang version of the python implementation is much appreciated. Thank you.

> Python

seed = Mnemonic.to_seed("blast about old claw current first paste risk involve victory edit current", passphrase="")
print("Seed: ", seed.hex())
purpose = 44
coinType = 118
account = 0
change = 0

hdwallet = HDWallet()
hdwallet.from_seed(seed=seed.hex())

for addressIndex in range(1):
    hdwallet.clean_derivation()
    hdwallet.from_index(purpose, hardened=True)
    hdwallet.from_index(coinType, hardened=True)
    hdwallet.from_index(account, hardened=True)
    hdwallet.from_index(change)
    hdwallet.from_index(addressIndex, hardened=True)

    print("---")
    print("Derivation Path: ", hdwallet.path())
    print("Private Key: ", hdwallet.private_key())
    print("Public Key: ", hdwallet.public_key())

    readdr_bytes = b"\x04" + bytearray.fromhex(hdwallet.public_key())
    readdr_bytes5 = bech32.convertbits(readdr_bytes, 8, 5)
    wallet_addr = bech32.bech32_encode("atom", readdr_bytes5)
    print("Wallet Address: ", wallet_addr)

> # OUTPUT
> Derivation Path: m/44'/118'/0'/0/0'
Private Key: 69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a
Public Key: 03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8
Wallet Address: atom1qspau72rtj7g57v7lsjvmnna8vvqlvq56hcejj0m34sau0eph8mvr7qgl9avu

> GoLang ( Gernerating differnt keys )

import (
	"github.com/tendermint/tendermint/crypto/secp256k1"
    "github.com/cosmos/cosmos-sdk/crypto/hd"
	"github.com/cosmos/go-bip39"
    "github.com/decred/dcrd/bech32"
 )

path := hd.BIP44Params{
	Purpose:      44,
	CoinType:     118,
	Account:      0,
	Change:       false,
	AddressIndex: 0,
}

seed := bip39.NewSeed("blast about old claw current first paste risk involve victory edit current","")

master, ch := hd.ComputeMastersFromSeed(seed)
priv, err := hd.DerivePrivateKeyForPath(master, ch, path.String())
if err != nil {
	log.Fatal(err)
}
var privKey = secp256k1.GenPrivKeySecp256k1(priv)
pubKey := privKey.PubKey()
fmt.Println(hex.EncodeToString(pubKey.Bytes()))

decodeString, _ := hex.DecodeString(fmt.Sprintf("04%x", pubKey.Bytes()))

// Convert test data to base32:
conv, err := bech32.ConvertBits(decodeString, 8, 5, true)
if err != nil {
	fmt.Println("Error:", err)
}
encoded, err := bech32.Encode("atom", conv)
if err != nil {
	fmt.Println("Error:", err)
}

// Show the encoded data.
fmt.Println("Atom address:", encoded)

> # OUTPUT
>---
> Derivation Path: m/44'/1022'/0'/0/0'
Private Key: 84925813eac8c1dc39f170e93b3bebe0bf961ac9c46507e858ce178a1a715c26
Public Key: 0204a0bad86cafed2daf1b4080a3e908afcf524e2a9c24e20817920c478d537cc1
Wallet Address: atom1qsp3yaurlt463pl6pekgae4yudlcwk2dhxt93cxz5d5ymw3j8xmngaqef5j7p

答案1

得分: 4

两个代码的结果不同,原因有两个:

  1. 在Go代码中,私钥的派生方式不正确:

    在Python代码中,使用路径m/44'/118'/0'/0/0',如hdwallet.path()的输出所示。相反,在Go代码中使用路径m/44'/118'/0'/0/0,如path.String()的输出所示。

    要在Go代码中使用Python代码的路径,可以直接指定路径。为此,需要将以下行:

    priv, err := hd.DerivePrivateKeyForPath(master, ch, path.String())
    

    替换为:

    priv, err := hd.DerivePrivateKeyForPath(master, ch, "m/44'/118'/0'/0/0'")
    

    并且可以删除path变量。

    通过这个改变,Go代码提供了与Python代码相同的私钥。

  2. 在Go代码中,私钥的导入方式不正确。因此,公钥被错误地确定。要修复这个问题,需要将以下行:

    var privKey = secp256k1.GenPrivKeySecp256k1(priv)
    

    替换为:

    var privKey = secp256k1.PrivKey(priv)
    

    通过这个改变,Go代码给出与Python代码相同的公钥。

Go代码的其余部分在功能上与Python代码等效,因此Go代码产生与Python代码相同的地址。

完整的Go代码如下:

package main

import (
	"encoding/hex"
	"fmt"
	"log"

	"github.com/cosmos/cosmos-sdk/crypto/hd"
	"github.com/cosmos/go-bip39"
	"github.com/decred/dcrd/bech32"
	"github.com/tendermint/tendermint/crypto/secp256k1"
)

func main() {

	seed := bip39.NewSeed("blast about old claw current first paste risk involve victory edit current", "")
	fmt.Println("Seed: ", hex.EncodeToString(seed)) // Seed:  dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd

	master, ch := hd.ComputeMastersFromSeed(seed)
	path := "m/44'/118'/0'/0/0'"
	priv, err := hd.DerivePrivateKeyForPath(master, ch, path)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Derivation Path: ", path)                 // Derivation Path:  m/44'/118'/0'/0/0'
	fmt.Println("Private Key: ", hex.EncodeToString(priv)) // Private Key:  69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a

	var privKey = secp256k1.PrivKey(priv)
	pubKey := privKey.PubKey()
	fmt.Println("Public Key: ", hex.EncodeToString(pubKey.Bytes())) // Public Key:  03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8

	decodeString, err := hex.DecodeString(fmt.Sprintf("04%x", pubKey.Bytes()))
	if err != nil {
		log.Fatal(err)
	}

	// Convert test data to base32:
	conv, err := bech32.ConvertBits(decodeString, 8, 5, true)
	if err != nil {
		fmt.Println("Error:", err)
	}
	encoded, err := bech32.Encode("atom", conv)
	if err != nil {
		fmt.Println("Error:", err)
	}

	// Show the encoded data.
	fmt.Println("Wallet Address:", encoded) // Wallet Address: atom1qspau72rtj7g57v7lsjvmnna8vvqlvq56hcejj0m34sau0eph8mvr7qgl9avu
}
英文:

The results of both codes differ because of two issues:

  • In the Go Code, the private key is derived incorrectly:

    In the Python code, the path m/44'/118'/0'/0/0' is used, as the output of hdwallet.path() shows. In the Go code, in contrast, the path m/44'/118'/0'/0/0 is used, as shown by the output of path.String().

    To use the path of the Python code in the Go code, the path can be e.g. specified directly. For this the line:

    priv, err := hd.DerivePrivateKeyForPath(master, ch, path.String())
    

    has to be replaced by:

    priv, err := hd.DerivePrivateKeyForPath(master, ch, "m/44'/118'/0'/0/0'")
    

    and the path variable can be removed.

    With this change, the Go code provides the same private key as the Python code.

  • In the Go Code, the private key is imported incorrectly. Therefore, the public key is determined wrongly. To fix this, the line:

    var privKey = secp256k1.GenPrivKeySecp256k1(priv)
    

    must be replaced by:

    var privKey = secp256k1.PrivKey(priv) 
    

    With this change, the Go code gives the same public key as the Python code.

The rest of the Go code is functionally equivalent to the Python code, so the Go code produces the same address as the Python code.

The complete Go code is:

package main

import (
	"encoding/hex"
	"fmt"
	"log"

	"github.com/cosmos/cosmos-sdk/crypto/hd"
	"github.com/cosmos/go-bip39"
	"github.com/decred/dcrd/bech32"
	"github.com/tendermint/tendermint/crypto/secp256k1"
)

func main() {

	seed := bip39.NewSeed("blast about old claw current first paste risk involve victory edit current", "")
	fmt.Println("Seed: ", hex.EncodeToString(seed)) // Seed:  dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd

	master, ch := hd.ComputeMastersFromSeed(seed)
	path := "m/44'/118'/0'/0/0'"
	priv, err := hd.DerivePrivateKeyForPath(master, ch, path)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Derivation Path: ", path)                 // Derivation Path:  m/44'/118'/0'/0/0'
	fmt.Println("Private Key: ", hex.EncodeToString(priv)) // Private Key:  69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a

	var privKey = secp256k1.PrivKey(priv)
	pubKey := privKey.PubKey()
	fmt.Println("Public Key: ", hex.EncodeToString(pubKey.Bytes())) // Public Key:  03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8

	decodeString, err := hex.DecodeString(fmt.Sprintf("04%x", pubKey.Bytes()))
	if err != nil {
		log.Fatal(err)
	}

	// Convert test data to base32:
	conv, err := bech32.ConvertBits(decodeString, 8, 5, true)
	if err != nil {
		fmt.Println("Error:", err)
	}
	encoded, err := bech32.Encode("atom", conv)
	if err != nil {
		fmt.Println("Error:", err)
	}

	// Show the encoded data.
	fmt.Println("Wallet Address:", encoded) // Wallet Address: atom1qspau72rtj7g57v7lsjvmnna8vvqlvq56hcejj0m34sau0eph8mvr7qgl9avu
}

huangapple
  • 本文由 发表于 2022年2月4日 13:11:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/70981681.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定