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

  • Louis
  • 更新于 2024-10-17 17:10
  • 阅读 513

比特币中的P2WPKH地址(Pay-to-Witness-Public-Key-Hash)是SegregatedWitness(隔离见证,简称SegWit)的一种地址类型。生成比特币的NativeSegWit地址(也称为bech32地址)

基本定义:

比特币中的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
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
该文章收录于 带你走进web3钱包
0 订阅 6 篇文章

0 条评论

请先 登录 后评论
Louis
Louis
web3 developer,技术交流或者有工作机会可加VX: magicalLouis