基本定义:
比特币中的P2WPKH地址(Pay-to-Witness-Public-Key-Hash)是Segregated Witness(隔离见证,简称SegWit)的一种地址类型。生成比特币的 Native SegWit 地址(也称为 bech32 地址),SegWit是对比特币协议的一项更新,旨在改善交易吞吐量、降低交易费用并提高安全性和灵活性。P2WPKH地址通常以“bc1”开头,且直接嵌入见证数据,不再占用区块的常规部分,从而减小交易大小和费用。
地址生成步骤
Native SegWit (P2WPKH) 是比特币的一种地址类型,它支持隔离见证(Segregated Witness),优化了交易签名和存储效率。生成 Native SegWit 地址的过程可以概括为以下步骤:
1. 生成密钥对
首先,你需要生成一个椭圆曲线(Elliptic Curve)密钥对,这是基于 secp256k1 算法的。这个过程包括生成一个私钥和一个对应的公钥。(也可以通过公钥直接生成地址,这种方式可以验证函数是否正确)
私钥:从一个随机数生成 32 字节长度的数值。
公钥:使用 secp256k1 算法从私钥推导出压缩公钥(33 字节,前缀为
0x02
或0x03
)。
2. 生成公钥哈希(PubKeyHash)
使用 SHA256 和 RIPEMD160 哈希算法生成公钥的哈希值。
先对压缩公钥进行 SHA256 哈希。
再对 SHA256 的结果进行 RIPEMD160 哈希,得到 20 字节的公钥哈希(PubKeyHash)。
3. 创建Witness Program
Native SegWit 地址的 Witness Program 由版本号和公钥哈希组成:
版本号是
0
(表示 P2WPKH)。公钥哈希(20 字节)。
所以 Witness Program 是 0x00
后跟 20 字节的公钥哈希。
4. Bech32 编码
使用 Bech32 编码将 Witness Program 转换为人类可读的比特币地址格式。Bech32 编码包括两部分:
地址前缀:对于比特币主网是
bc
,测试网是tb
。Witness Program:转换为 Base32 格式并进行 Bech32 校验和编码。
5. 最终的 Native SegWit 地址
经过 Bech32 编码后的字符串就是 Native SegWit 地址,它通常以 bc1
开头(主网)或 tb1
开头(测试网)。
代码示例:
ts 版本:
import { createHash } from 'crypto'; import { bech32 } from 'bech32'; // 计算 SHA256 + RIPEMD160 function hash160(data: Buffer): Buffer { // Step 1: SHA-256 const sha256Hash = createHash('sha256').update(data).digest(); // Step 2: RIPEMD-160 const ripemd160Hash = createHash('ripemd160').update(sha256Hash).digest(); return ripemd160Hash; } export function generateNativeSegWitAddress(publicKeyHex: string): string { // Step 1: 解码 16 进制公钥 const pubKeyBytes = Buffer.from(publicKeyHex, 'hex'); // Step 2: 计算公钥哈希 const pubKeyHash = hash160(pubKeyBytes); // Step 3: Bech32 编码 const witnessVersion = 0x00; // P2WPKH 的版本号为 0 // 将 witness version 转换为 Bech32 words const words = [witnessVersion, ...bech32.toWords(pubKeyHash)]; // Bech32 编码 const address = bech32.encode('bc', words); // 返回结果 return address; }
go 版本:
package utils import ( "crypto/sha256" "encoding/hex" "fmt" "github.com/btcsuite/btcutil/bech32" "golang.org/x/crypto/ripemd160" ) // Hash160 计算 SHA256 + RIPEMD160 func hash160(data []byte) []byte { sha256Hash := sha256.Sum256(data) ripemd160Hasher := ripemd160.New() ripemd160Hasher.Write(sha256Hash[:]) return ripemd160Hasher.Sum(nil) } func GenerateNativeSegWitAddress(publicKeyHex string) (string, error) { // Step 1: 解码 16 进制公钥 pubKeyBytes, err := hex.DecodeString(publicKeyHex) if err != nil { return "", fmt.Errorf("failed to decode public key: %v", err) } // Step 2: 计算公钥哈希 pubKeyHash := hash160(pubKeyBytes) // Step 3: 准备 SegWit 地址编码数据 witnessVersion := byte(0x00) // P2WPKH 的版本号为 0 // Step 4: 使用 bech32 库进行编码 data, err := bech32.ConvertBits(pubKeyHash, 8, 5, true) // 将 8-bit 转换为 5-bit if err != nil { return "", fmt.Errorf("failed to convert bits: %v", err) } // 将版本号和转换后的数据合并 combinedData := append([]byte{witnessVersion}, data...) // Bech32 编码 address, err := bech32.Encode("bc", combinedData) if err != nil { return "", fmt.Errorf("failed to encode Bech32 address: %v", err) } // 返回 Bech32 编码的地址 return address, nil }