基本定義:
比特幣中的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 }