bitcoinjs学习1—P2PKH

1. 概述

在本学习笔记中,我们将深入探讨如何使用 bitcoinjs-lib 库构建和签名一个 P2PKH(Pay-to-PubKey-Hash) 比特币交易。P2PKH 是比特币网络中最常见和最基本的交易类型之一,理解其工作原理是掌握比特币交易构建的关键。

想要详细了解p2pkh工作原理可以查看该网址p2pkh(Pay to PublicKey Hash)


1.1 目标

  • 主要目标 :学习如何使用 bitcoinjs-lib 构建、签名并广播一个 P2PKH 交易。
  • 具体任务
    1. 生成一个 P2PKH 地址。
    2. 构建一个包含输入和输出的交易。
    3. 将交易广播到regtest网络中。

1.2 P2PKH 的工作原理

  1. 锁定资金 : 发送方创建一个交易输出,将比特币锁定到接收方的公钥哈希(即地址)。
  2. 解锁资金
  • 接收方在花费比特币时,需要提供:
    • 公钥。
    • 使用私钥生成的签名。
  • 输入脚本(scriptSig)格式:<Signature> <PublicKey>
  1. 验证过程
    • 比特币网络会验证签名是否与公钥匹配,以及公钥哈希是否与输出脚本中的哈希一致。

在比特币脚本中,OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIGP2PKH(Pay-to-PubKey-Hash) 交易的 锁定脚本(scriptPubKey) 的核心部分。它定义了如何锁定比特币以及如何解锁比特币的条件。

以下是对每个操作码的详细解释:


1. OP_DUP

  • 作用:复制栈顶元素。
  • 操作
    • 将栈顶的元素复制一份,并将副本压入栈中。
  • 示例
    • 如果栈顶是 A,执行 OP_DUP 后,栈变为 [A, A]

2. OP_HASH160

  • 作用:对栈顶元素进行哈希运算。
  • 操作
    • 弹出栈顶元素,先进行 SHA-256 哈希,再进行 RIPEMD-160 哈希,最后将结果压入栈中。
  • 示例
    • 如果栈顶是 A,执行 OP_HASH160 后,栈变为 [Hash160(A)]

3. <PubKeyHash>

  • 作用:将接收方的公钥哈希压入栈中。
  • 操作
    • 这是一个数据推送操作,将接收方的公钥哈希(20 字节)压入栈中。
  • 示例
    • 如果接收方的公钥哈希是 B,执行后栈变为 [Hash160(A), B]

4. OP_EQUALVERIFY

  • 作用:验证栈顶两个元素是否相等,并移除它们。
  • 操作
    • 弹出栈顶两个元素,比较它们是否相等。
    • 如果相等,继续执行;如果不相等,脚本执行失败。
  • 示例
    • 如果栈顶是 [Hash160(A), B],执行 OP_EQUALVERIFY 后:
      • 如果 Hash160(A) == B,栈为空,继续执行。
      • 如果 Hash160(A) != B,脚本失败。

5. OP_CHECKSIG

  • 作用:验证签名和公钥是否匹配。
  • 操作
    • 弹出栈顶两个元素:签名和公钥。
    • 使用公钥验证签名是否对当前交易有效。
    • 如果验证成功,将 true 压入栈中;否则,将 false 压入栈中。
  • 示例
    • 如果栈顶是 [Signature, PublicKey],执行 OP_CHECKSIG 后:
      • 如果签名有效,栈变为 [true]
      • 如果签名无效,栈变为 [false]

6. 整体脚本的执行流程

假设解锁脚本(scriptSig)提供了签名和公钥:<Signature> <PublicKey>,锁定脚本(scriptPubKey)为 OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

执行步骤:
  1. 初始栈

    • 解锁脚本将签名和公钥压入栈中:

      [Signature, PublicKey]
      
  2. 执行 OP_DUP

    • 复制公钥:

      [Signature, PublicKey, PublicKey]
      
  3. 执行 OP_HASH160

    • 对公钥进行哈希:

      [Signature, PublicKey, Hash160(PublicKey)]
      
  4. 压入 <PubKeyHash>

    • 将接收方的公钥哈希压入栈中:

      [Signature, PublicKey, Hash160(PublicKey), PubKeyHash]
      
  5. 执行 OP_EQUALVERIFY

    • 比较 Hash160(PublicKey)PubKeyHash
      • 如果相等,移除这两个元素:

        [Signature, PublicKey]
        
      • 如果不相等,脚本失败。

  6. 执行 OP_CHECKSIG

    • 使用公钥验证签名:
      • 如果签名有效,栈变为:

        [true]
        
      • 如果签名无效,栈变为:

        [false]
        
  7. 脚本结果

    • 如果栈顶为 true,交易有效,资金可以解锁。
    • 如果栈顶为 false,交易无效,资金无法解锁。

1.3 工具和技术栈

1.3.1 bitcoinjs-lib
  • 简介bitcoinjs-lib 是一个用于处理比特币交易的 JavaScript 库。它支持构建、签名和解析比特币交易,适用于 Node.js 和浏览器环境。

  • 安装

bash 复制代码
# 选择一个目录作为项目目录,打开vscode,打开命令行窗口
npm init -y

# 安装依赖
npm i bitcoinjs-lib ecpair tiny-secp256k1
1.3.2 比特币测试网
  • 简介 :比特币测试网(Testnet)是一个用于开发和测试的比特币网络,其功能与主网相同,但比特币没有实际价值。(新手建议还是使用regtest,testnet测试网需要申请领取测试币,每次领取金额都较少,一般为10000聪)
  • 用途
    • 测试交易构建和签名。
    • 避免在主网上浪费真实比特币。
  • 获取测试网比特币
    • 使用测试网水龙头(如 testnet-faucet.com)获取免费的测试网比特币。
1.3.3 比特币测试网浏览器

1.4 p2pkh交易demo

1.4.1准备工作

交易:Alice向Bob转账2个比特币

先使用bitcoin-core向Alice的地址转2个比特币,再用这笔未花费交易给Bob发送

在交易记录中右击刚刚的交易,复制原始交易,再根据上一节预备知识中提到的解析交易

或者使用控制台(命令行窗口)

bash 复制代码
sendtoaddress mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r 2
getrawtransaction txid
decoderawtransaction hex

记录交易的txid、vout中是alice的输出的次序n,金额value

以下是Alice给Bob发送比特币的代码(使用p2pkh)

javascript 复制代码
const bitcoin = require('bitcoinjs-lib');
const network = bitcoin.networks.regtest;

ECPairFactory = require('ecpair').default;
ecc = require('tiny-secp256k1');
const base58check = require('base58check');  //npm i --save base58check
ECPair = ECPairFactory(ecc);
const hashtype = bitcoin.Transaction.SIGHASH_ALL;

// Alice给Bob发送2个比特币
const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex',);
const keyPairAlice = ECPair.fromPrivateKey(ONE, { network });
const TWO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000002', 'hex',);
const keyPairBob = ECPair.fromPrivateKey(TWO, { network });
// alice_address: mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r
// bob_address: mg8Jz5776UdyiYcBb9Z873NTozEiADRW5H

// alice上一笔未花费的交易的txid,
const txid = '4a3cc600d4973479aafb62b5789590dd7738c3f9f1956a513a361e20ef56f9bb'
const vout = 1; // 上一笔交易中的输出
const amount = BigInt(200000000);// 转账金额

// 收款人bob的地址
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPairBob.publicKey })
var PayeeBobAddr = base58check.decode(address, 'hex')
console.log('Bob_hashPubkey:', Buffer.from(PayeeBobAddr.data, 'Hex').toString('hex'));


const tx = new bitcoin.Transaction(network);

// 添加输入
tx.addInput(Buffer.from(txid, 'hex').reverse(), vout);

// 添加输出
const outputScript = bitcoin.payments.p2pkh({ pubkey: keyPairBob.publicKey }).output;
tx.addOutput(outputScript, amount - BigInt(10000));

// 添加签名
// 上一笔交易的输出脚本
const preoutputScript = bitcoin.payments.p2pkh({ pubkey: keyPairAlice.publicKey }).output;
// 生成比特币交易中某个输入的签名哈希值(sighash)。hashtype=1表示对交易的所有输入都进行哈希处理
const signhash = tx.hashForSignature(0, preoutputScript, hashtype);

// 构造p2pkh的解锁脚本: [签名 公钥]
const scriptSig = bitcoin.script.signature.encode(keyPairAlice.sign(signhash), hashtype);
tx.setInputScript(0,
    bitcoin.script.compile([scriptSig, keyPairAlice.publicKey])
)

// 输出交易原始数据
console.log('Raw Transaction:', tx.toHex());

// 广播交易
//sendrawtransaction <raw_transaction_hex>

// 获取txHex: getrawtransaction txid
// 解析交易: decoderawtransaction txHex
相关推荐
饮长安千年月2 小时前
Linksys WRT54G路由器溢出漏洞分析–运行环境修复
网络·物联网·学习·安全·机器学习
红花与香菇2____2 小时前
【学习笔记】Cadence电子设计全流程(二)原理图库的创建与设计(上)
笔记·嵌入式硬件·学习·pcb设计·cadence·pcb工艺
一天八小时4 小时前
Docker学习进阶
学习·docker·容器
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
拥有一颗学徒的心5 小时前
鸿蒙第三方库MMKV源码学习笔记
笔记·学习·性能优化·harmonyos
车端域控测试工程师5 小时前
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑰】
经验分享·学习·汽车·测试用例·capl
车端域控测试工程师5 小时前
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑪】
经验分享·学习·汽车·测试用例·capl
charlie1145141918 小时前
(萌新入门)如何从起步阶段开始学习STM32 —— 0.碎碎念
c语言·stm32·单片机·嵌入式硬件·学习·教程
网安Ruler9 小时前
泷羽Sec-黑客基础之html(超文本标记语言)
前端·学习·网络安全·html
啥也不会的菜鸟·11 小时前
Redis7——基础篇(五)
redis·学习·缓存