32-Bitcoin钱包开发
0x-wen

Bitcoin 钱包地址类型

P2PKH(Pay-to-PubKeyHash)普通地址

  • 格式:以1开头,例如 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
  • 特点:最传统和最常见的地址类型,广泛用于比特币早期交易,直接将比特币发送给一个特定的公钥哈希(即地址)
  • 优点:兼容性好,几乎所有钱包和交易所都支持。
  • 缺点:每笔交易都需要公开公钥,从而略微降低了隐私性,并且由于数据结构的限制,增加了交易的大小和费用。

P2SH(Pay-to-Script-Hash)地址

  • 格式:以3开头,例如 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
  • 特点:允许更复杂的交易脚本,如多重签名地址。
  • 优点:支持复杂交易和脚本,提供更高安全性。
  • 缺点:创建和管理比P2PKH地址更复杂。

Bech32编码地址,比特币隔离见证(SegWit)的引入新增了几种地址类型:

  • Pay-to-Witness-Pubkey-Hash (P2WPKH): P2PKH的SegWit版本,使用Bech32编码。

  • **Pay-to-Witness-Script-Hash (P2WSH)**:这是P2SH的SegWit版本,使用Bech32编码。

  • **Pay-to-Taproot (P2TR)**:这是Taproot更新引入的地址类型,用于表示Taproot结构的输出,也使用Bech32编码

  • 格式:以 bc1 开头,例如 bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwfvenl

  • 特点:具有更高的隐私性、安全性和扩展性。

  • 优点:通过利用Schnorr签名和Merkle化的脚本树(MAST),它能够提供更高的效率和隐私,更低的交易费用。

  • 缺点:不是所有的钱包和交易所都支持,尽管支持率在逐步增加。

不同类型的地址之间能相互转账吗?

在比特币网络中,不同类型的地址主要涉及到如何编码和处理交易的细节,但它们都遵循相同的底层比特币协议。

比特币网络根据交易使用的地址类型不同,比特币网络中的节点和矿工会采用不同的处理方法来验证和确认交易。

从任何一种地址类型向另一种类型的地址发送比特币都是可以的,地址类型的不同只是传输的数据格式和方法不同,并不影响发送和接受比特币。

注意事项

一个私钥可以同时生成多个不同类型的地址,并且各地址之间是相互隔离的,类比同一个银行拥有不用的账号,每个账号中的钱是独立的。

钱包开发实践

主要待完成:

  • 离线地址生成
  • 签名发送交易
  • 查询交易状态

可参考资料: https://github.com/btcsuite/btcd

代码 btcd/btcutil/address.go 下提供了具体的地址生成方式:

1
2
3
4
5
NewAddressPubKeyHash
NewAddressScriptHash
NewAddressWitnessPubKeyHash
NewAddressWitnessScriptHash
NewAddressTaproot

离线地址生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func createPrivkey() {
// secp256k1 生成私钥
privKey, err := btcec.NewPrivateKey()
if err != nil {
panic(err)
}

privKeyHex := hex.EncodeToString(privKey.Serialize())
fmt.Println("Private Key:", privKeyHex)

// 创建一个新的WIF编码的私钥
wif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, true) // true 表示压缩公钥
if err != nil {
log.Fatalf("Error creating WIF: %v", err)
}
// 打印WIF格式的私钥
fmt.Println("WIF Private Key:", wif.String())

// 从私钥获取公钥
pubKey := privKey.PubKey()
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())

// 使用公钥和主网络参数创建一个P2PKH地址
addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams)
if err != nil {
log.Fatalf("Unable to create address: %v", err)
}
fmt.Println("P2PKH Address:", addr.EncodeAddress())
}

确定性钱包地址生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
const (
LEGACY = "legacy" // p2pkh bip44
SEGWIT_NESTED = "segwit_nested" // p2sh bip49
SEGWIT_NATIVE = "segwit_native" // p2wpkh bip84
TAPROOT = "taproot" // p2tr bip86
)

func GenerateExtendedKey() (*hdkeychain.ExtendedKey, error) {
// 方式一: 创建一个新的HD钱包种子
// seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
// if err != nil {
// log.Fatalf("Unable to generate seed: %v", err)
// }

entropy, _ := bip39.NewEntropy(128)
mnemonic, _ := bip39.NewMnemonic(entropy)
fmt.Println("助记词:", mnemonic)
// 由助记词生成种子(Seed), password为空可兼容其他钱包
seed := bip39.NewSeed(mnemonic, "")

// 使用种子创建一个新的主私钥
masterPrivKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
if err != nil {
log.Fatalf("Unable to create master private key: %v", err)
}

// Derive according to BIP44 path m/44'/0'/0'/0/0
purposeIndex := hdkeychain.HardenedKeyStart + 44
coinTypeIndex := hdkeychain.HardenedKeyStart + 0 // Bitcoin
accountIndex := hdkeychain.HardenedKeyStart + 0 // Account 0
changeIndex := 0 // External address (receiving)
addressIndex := 0 // First address

// Correct usage of Derive method
purposeKey, err := masterPrivKey.Derive(uint32(purposeIndex))
if err != nil {
log.Fatalf("Unable to derive purpose key: %v", err)
}
coinTypeKey, err := purposeKey.Derive(uint32(coinTypeIndex))
if err != nil {
log.Fatalf("Unable to derive coin type key: %v", err)
}
accountKey, err := coinTypeKey.Derive(uint32(accountIndex))
if err != nil {
log.Fatalf("Unable to derive account key: %v", err)
}
externalKey, err := accountKey.Derive(uint32(changeIndex))
if err != nil {
log.Fatalf("Unable to derive external chain key: %v", err)
}
childKey, err := externalKey.Derive(uint32(addressIndex))
if err != nil {
log.Fatalf("Unable to derive receive address key: %v", err)
}
return childKey, nil
}

func HDWallet() {
childKey, _ := GenerateExtendedKey()
// 获取子私钥
privKey, err := childKey.ECPrivKey()
if err != nil {
log.Fatalf("Unable to get private key: %v", err)
}
privKeyHex := hex.EncodeToString(privKey.Serialize())
fmt.Println("Hex Private Key:", privKeyHex)

// 创建一个新的WIF编码的私钥
wif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, true) // true 表示压缩公钥
if err != nil {
log.Fatalf("Error creating WIF: %v", err)
}
// 打印WIF格式的私钥
fmt.Println("WIF Private Key:", wif.String())

// 从私钥获取公钥
pubKey := privKey.PubKey()

// 使用公钥和主网络参数创建一个P2PKH地址
addr, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(pubKey.SerializeCompressed()), &chaincfg.MainNetParams)
if err != nil {
log.Fatalf("Unable to create address: %v", err)
}
fmt.Println("1 - P2PKH Address:", addr.EncodeAddress())

p2pkh, err := btcutil.NewAddressPubKey(pubKey.SerializeCompressed(), &chaincfg.MainNetParams)
if err != nil {
log.Fatalf("Unable to create address: %v", err)
}
fmt.Println("2 - P2PKH Address:", p2pkh.EncodeAddress())

// pay-to-witness-pubkey-hash (P2WPKH) btcutil.NewAddressWitnessScriptHash
p2wpkh, err := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(pubKey.SerializeCompressed()), &chaincfg.MainNetParams)
if err != nil {
log.Fatalf("Unable to create address: %v", err)
}
fmt.Printf("3 - P2WPKH Address: %s\n", p2wpkh.EncodeAddress())

// pay-to-script-hash (P2SH) btcutil.NewAddressScriptHash
redeemScript, err := txscript.PayToAddrScript(p2wpkh)
if err != nil {
log.Fatalf("Unable to create script: %v", err)
}
p2sh, err := btcutil.NewAddressScriptHash(redeemScript, &chaincfg.MainNetParams)
if err != nil {
log.Fatalf("Unable to create address: %v", err)
}
fmt.Printf("4 - P2SH Address: %s\n", p2sh.EncodeAddress())

// pay-to-taproot (P2TR) btcutil.NewAddressTaproot
internalKey, _ := btcec.ParsePubKey(pubKey.SerializeUncompressed())
p2tr, _ := btcutil.NewAddressTaproot(txscript.ComputeTaprootKeyNoScript(internalKey).SerializeCompressed()[1:], &chaincfg.MainNetParams)
fmt.Printf("5 - P2TR Address: %s\n", p2tr.EncodeAddress())
}
由 Hexo 驱动 & 主题 Keep
总字数 42.8k