切换语言为:繁体

一文详解比特币的Native SegWit(P2WPKH)地址类型

  • 爱糖宝
  • 2024-10-17
  • 2047
  • 0
  • 0

基本定义:

比特币中的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 字节,前缀为 0x020x03 )。

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
}

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.