JavaScript编写自己的比特币交易代码
今天我们将编写第一个比特币交易代码。为了实现这一目标,我们将使用名为bitcore的JavaScript库。JavaScript是最流行的现代编程语言,几乎每个开发人员都知道它,因此它使这篇文章具有普遍性并且对更广泛的受众有用。
在继续阅读本文之前,你应该至少掌握有关比特币区块链如何工作的基本技术知识。如果没有,请花几分钟时间阅读区块链简介。如果你有更多的时间,比如几个小时,我建议你阅读掌握比特币。
让我们从一个具有以下依赖关系的新NPM项目开始:
[...]
"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
并将其粘贴到表单中。
是时候创建我们想要发送测试比特币的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
?阅读确定性钱包以找到答案
大!一切都准备好了!我们现在唯一要做的就是用我们的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。然后将其复制并粘贴到比特币区块链浏览器中,看看它是否真的有效。
完整代码可以在GitHub上找到。