今天我们将编写第一个比特币交易代码. 为了实现这一目标, 我们将使用名为 https://github.com/bitpay/bitcore-lib 的 JavaScript 库. JavaScript 是最流行的现代编程语言, 几乎每个开发人员都知道它, 因此它使这篇文章具有普遍性并且对更广泛的受众有用.
在继续阅读本文之前, 你应该至少掌握有关比特币区块链如何工作的基本技术知识. 如果没有, 请花几分钟时间阅读区块链简介. 如果你有更多的时间, 比如几个小时, 我建议你阅读掌握比特币.
让我们从一个具有以下依赖关系的新 NPM 项目 https://docs.npmjs.com/cli/init 开始:
- [...]
- "dependencies": {
- "bitcore-explorers": "^1.0.1",
- "bitcore-lib": "^0.13.19"
- }
- [...]
打开 index.JS 文件并导入 bitcore 库:
var bitcore = require("bitcore-lib");
要花费比特币我们需要一个包含比特币的地址和一个允许我们花钱的私钥. 我们将导入 WIF 版本的私钥. WIF 是 Wallet Import Format 的缩写. 它可以在比特币钱包之间轻松导入密钥. 然后我们将从该私钥创建一个 testnet 地址:
- var privateKeyWIF = 'cQN511BWtc2dSUMWySmZpr6ShY1un4WK42JegGwkSFX5a8n9GWr3';
- var privateKey = bitcore.PrivateKey.fromWIF(privateKeyWIF);
- var sourceAddress = privateKey.toAddress(bitcore.Networks.testnet);
警告! 在那个例子中, 我与你分享我的私钥. 你不应该在现实生活中这样做. 拥有私钥的人是分配给该密钥的地址的比特币的所有者. 这是所有权的标志.
在这种情况下, 我只是与你分享了用于创建 testnet 地址的密钥. Testnet 是一个为软件和脚本测试而创建的比特币网络. 它不包含真正的比特币, 只包含测试的比特币. 你可以免费获得它们. 即使有人偷了他们也没什么大不了的. 我可以冒这个风险为你提供开箱即用的示例.
如果有人使用 / 偷走了这个地址的所有测试比特币, 你可以给它接着充. 复制地址 mibK5jk9eP7EkLH175RSPGTLR27zphvvxa 并将其粘贴到表单中 http://tpfaucet.appspot.com/ .
是时候创建我们想要发送测试比特币的 targetAddress 了.
var targetAddress = (new bitcore.PrivateKey).toAddress(bitcore.Networks.testnet);
如果有任何比特币, 请检查我们的源地址. 比特币网络使用 UTXO 来存储该信息. UTXO 是 Unspent Transaction Output 的缩写.
我们有一个问题, 我们没有比特币网络客户端. 整个节点需要至少 125 GB 的硬盘空间, 这对我可怜的 MacBook Air 来说太多了. 我们必须找到一种解决方法. 我们不得不请某人为我们读比特币网络. 并广播我们的交易.
在这种情况下, 我们正在失去比特币区块链的最大优势. 系统的架构使我们不必信任任何一方. 网络共识, 数学和加密使得区块链中存储的数据可信. 但现在我们要求中间人为我们读取这些数据. 他可能会向我们提供虚假或过时的数据.
我们将使用来自 bitcore-explorers 库的 Insight. 由于它非常受欢迎, 我们只是在这里学习, 我们可以假设它可以信任. 最终的解决方案应该是拥有自己的比特币全节点.
好吧, 让我们使用 Insight 来检查我们要花多少比特币.
- var Insight = require("bitcore-explorers").Insight;
- var insight = new Insight("testnet");
- insight.getUnspentUtxos(sourceAddress, function(error, utxos) {
- if (error) {
- console.log(error);
- } else {
- console.log(utxos);
- // transaction code goes here
- }
UTXOs 的输出是一个数组. 它的每个元素都包含有关作为 UTXO 所有者的地址和 Satoshis(1 Satoshi = 0.00000001 比特币) 的信息. 它看起来像这样:
[ <UnspentOutput: dbe9ce2ae27d7ffcba40195e7ee628e9165568115931386b27b0c0674fa019c5:1, satoshis: 5047177248, address: mibK5jk9eP7EkLH175RSPGTLR27zphvvxa> ]
是时候创建我们的交易了:
var tx = new bitcore.Transaction();
让我们将收到的 UTXOs 设置为交易的 input. 需要注意的一件重要事情是: 我们不是从 address 而是从 UTXOs 获得比特币.
tx.from(utxos);
让我们设置我们想要交付给他的交易和金额的接收者. 数量以 Satoshis 给出, 这是比特币的最小单位: 1 Satoshi = 0.00000001 比特币. 这是我们交易的 output:
tx.to(targetAddress, 10000);
是时候讨论值的 the change.UTXOs 是指向我们地址但尚未用完的交易的输出. UTXOs 就像一张钞票. 如果你的口袋里有 5 美元的钞票并且想购买 2 美元的啤酒, 你就不会削减一部分账单并将其交给收银员. 你给 5 美元的钞票并收到 3 美元的改变. 它与 UTXOs 完全相同. 你必须在交易中使用整个 UTXO 并指定 change 值和 address, 然后应返回 change.
WTF? 我是否必须指定 change 值? 在商店里, 当我以 5 美元的价格购买 2 美元的啤酒时, 我收到 3 美元的回报. 这很明显. 无需计算.
在比特币中, 存在一点差异. 实际上, change 只是交易的另一个输出. outputs 的总和应该比 input 的总和小一点. 差异称为 mining fee. 你将其支付给矿工以包含在交易区块中. 像 bitcore.io 这样的钱包或库估算了我们的 mining fee. 所以在我们的例子中, 我们应该返回 change 到指定 address.
tx.change(sourceAddress);
你可以注意到我们使用了 sourceAddress. 结果, 该地址的一些现有 UTXOs 消失了 (它们将被用完), 但也会创建一个新的 (来自 change 的那个).
在现实生活中, 钱包为你的每笔交易使用新地址. 这样做的目的是改善匿名性. 怎么可能从一个 private key 中钱包能够创建许多 public keys 和 address? 阅读确定性钱包以找到答案 https://en.bitcoin.it/wiki/Deterministic_wallet
大! 一切都准备好了! 我们现在唯一要做的就是用我们的 private key 签署交易并将其发送到比特币区块链. 正如我之前提到的, 我们没有自己的比特币客户端. 我们使用外部工具与区块链进行通信. 问题是: 我们能否相信它. 当我们广播交易时, 该工具不存在捕获私钥或操纵交易的风险 (例如, 更改 targetAddress). 如果该工具进行了上面列出的任何更改, 则签名将不再有效, 并且将拒绝交易. 唯一的风险是该工具根本不会发送交易. 但我们可以在任何区块链资源管理器中验证它. 所以我们可以毫不畏惧地再次使用 Insight:
- tx.sign(privateKey);
- tx.serialize();
- insight.broadcast(tx, function(error, transactionId) {
- if (error) {
- console.log(error);
- } else {
- console.log(transactionId);
- }
- });
这就是所有人! 该交易被广播到网络. 如果一切顺利, 我们将收到交易 ID. 然后将其复制并粘贴到比特币区块链浏览器 http://tbtc.blockr.io/ 中, 看看它是否真的有效.
完整代码可以在 GitHub 上找到.
欢迎访问我的区块链教程和区块链技术博客, 深入了解区块链, 比特币, 加密货币, 以太坊, 和智能合约.
以太坊入门教程, 主要介绍智能合约与 dapp 应用开发, 适合熟悉 JavaScript 或某一开发语言的程序员入门.
以太坊开发进阶教程, 主要是介绍使用 node.JS,MongoDB, 区块链, ipfs 实现去中心化电商 DApp 实战, 适合进阶.
来源: http://www.qdfuns.com/article/51179/42d67995f9f486dc53e6834d52d8b239.html