第四章 自己动手写比特币之钱包 (2)

找到这些未消费的交易outputs之后,我们就可以为当前交易创建对应的inputs了:

const toUnsignedTxIn = (unspentTxOut: UnspentTxOut) => { const txIn: TxIn = new TxIn(); txIn.txOutId = unspentTxOut.txOutId; txIn.txOutIndex = unspentTxOut.txOutIndex; return txIn; }; const {includedUnspentTxOuts, leftOverAmount} = findTxOutsForAmount(amount, myUnspentTxouts); const unsignedTxIns: TxIn[] = includedUnspentTxOuts.map(toUnsignedTxIn);

做法很简单,主要就是将每个input中的txOutId指向上面找到的对应的未消费交易output项目。

紧跟着就需要创建示例中的2个outputs了:一个output是给接收者的,另外一个output是发送者自己的,因为需要把剩余的币数还回来。当然,如果inputs中指向的币数总和刚好等于交易量,即leftOverAmount为0, 我们就只需要创建一个发送给目标用户的output就够了。

const createTxOuts = (receiverAddress:string, myAddress:string, amount, leftOverAmount: number) => { const txOut1: TxOut = new TxOut(receiverAddress, amount); if (leftOverAmount === 0) { return [txOut1] } else { const leftOverTx = new TxOut(myAddress, leftOverAmount); return [txOut1, leftOverTx]; } };

最后,我们会进行交易id计算(对交易内容做哈希),并对交易进行签名,最终将交易签名赋予给每个input:

const tx: Transaction = new Transaction(); tx.txIns = unsignedTxIns; tx.txOuts = createTxOuts(receiverAddress, myAddress, amount, leftOverAmount); tx.id = getTransactionId(tx); tx.txIns = tx.txIns.map((txIn: TxIn, index: number) => { txIn.signature = signTxIn(tx, index, privateKey, unspentTxOuts); return txIn; }); 使用钱包

我们会提供'/mineTransaction‘这个api来让用户方便的使用钱包这个功能:

app.post('/mineTransaction', (req, res) => { const address = req.body.address; const amount = req.body.amount; const resp = generatenextBlockWithTransaction(address, amount); res.send(resp); });

如代码所示,使用者只需要提供接收方的地址和交易数量就能通过该api来实现交易。该api调用后为首先进行一次挖矿,获得一笔50个币的原始交易,然后根据接收方地址和交易数量完成指定交易,最终将这些交易记录到新增加的区块中,同时更新我们的「未消费交易outputs」。

测试体验

启动

为了方便测试,本人对package.json中的启动脚本做了些修改, 让我们可以快速的启动两个节点进行测试,而不需要每次启动都输入一堆的参数。

npm run node1 npm run node 2

同时, 在启动第二个节点时,加入PEER参数,让第二个节点自动和节点1建立P2P连接。

"scripts": { "prestart": "npm run compile", "node1": "HTTP_PORT=3001 P2P_PORT=6001 WALLET=1 npm start ", "node2": "HTTP_PORT=3002 P2P_PORT=6002 WALLET=2 PEER=ws://localhost:6001 npm start ", "start": "node src/main.js", "compile": "tsc" },

最后,通过新加的WALLET参数的支持,我们会为每个节点自动初始化一个钱包,这样我们就更容易观察钱包和交易的行为。

提供额外的查看「未消费交易outputs」接口

因为「未消费交易outputs」这个清单是非常重要的,这时我们进行交易的基础。所以很有必要提供一个额外的接口来查看里面的数据的变化。你可以通过GET的方式发送请求到 ":3001/unspentTransactionOutputs" 接口来获得该清单。

注意,如果你起了两个节点,那么端口使用3001和3002都是可以的。 因为该清单是一个分布式清单,和我们的区块链一样,是全网同步的。

有了这些之后,你就可以方便的通过postman调用相应的接口对钱包和交易功能进行体验了。

小结

我们刚刚实现了一个未加密的钱包功能以进行简单的交易。虽然这个交易算法(mineTransaction接口相关的逻辑)中最多只能有两个接收者outputs(一个是接收者,一个是自己), 但是我们底层的区块链接口是能支持任意数量的outputs的。比如你可以创建一个inputs是50个币,outputs分别是5,15和30个币的交易, 但你必须手动填充这些数据并调用/mineRawBlock这个接口来达成。

迄今为止,我们进行一次交易,还是需要先进行一次挖矿,才能将交易信息加入到新的区块里面。我们当前的各个节点不会对未记录在区块中的交易做任何信息交换。 这些问题都将会在下一个章节进行解决。

本章节完整的代码请看这里

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwywyy.html