用NBitcoin进行区块链开发(5)

2019-03-12 19:39:10 浏览数 (1)

请参考以下文章一起阅读:

  • 我生成的比特币地址竟然与别人的重合了
  • 用NBitcoin进行区块链开发(1) : 私钥、公钥、WIF
  • 用NBitcoin进行区块链开发(2) : 公钥哈希、地址
  • 用NBitcoin进行区块链开发(3) : ScriptPubKey
  • 用NBitcoin进行区块链开发(4) : 交易

BTC的区块链(blockchain)存储着许多交易(transaction),transaction简单来讲是指BTC从某个地址到某个地址的转移记录。与我们平常的交易记录方式不太一样,一个交易主要由输入(value input)和输出(value output)构成。

你的BTC不是凭空来的,input总是来源于以前某笔交易的某个输出项(PrevOut),这样一层一层地追查下去,总能找到BTC的源头(即挖矿产生的BTC)。而output比较容易理解,就是给张三多少BTC,给李四多少BTC。

transaction实际上就是一串二进制数值,在比特币的协议中有严格的定义,满足一系列规则的交易可以被矿工打包确认,不符合规则的交易会被直接抛弃,手工构造一笔交易,剖析其二进制表示可以更深入地了解区块链的内部原理。

本文尝试用NBitcoin构建一笔交易,其中的例子来源于下面这篇文章:

https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand/

从下面这个网址可以看到这笔交易的详细信息:

https://blockchair.com/bitcoin/transaction/5d42b45d5a3ddcf2421b208885871121551acf6ea5cc1c1b4e666537ab6fcbef

先直接上代码:

Step 1. 构建一个transaction

Transaction tx = Transaction.Create(Network.Main);

需要注意一点的是以前的 new Transaction()方法已经被弃用。

Step 2. 构建输入项

TxIn vin = new TxIn(); uint256 prevTX = new uint256("7e3ab0ea65b60f7d1ff4b231016fc958bc0766a46770410caa0a1855459b6e41"); vin.PrevOut = new OutPoint(prevTX, 0); tx.Inputs.Add(vin);

这里的例子只有一个输入项,来自于以前一笔transaction(TXID是7e3...e41)的输出项(正好是第0项)。在NBitcoin里有一个专门的类OutPoint,这个类的定义处于源代码Transaction.cs中。内部实际上就是记录了hash(交易ID)和n(输出序号)。

构建好一个输入项之后,不要忘了用 tx.Inputs.Add() 添加进去。

Step 3. 构建输出项

TxOut vout = new TxOut(); vout.Value = Money.Satoshis(20000); var destination = BitcoinAddress.Create("1NAK3za9MkbAkkSBMLcvmhTD6etgB4Vhpr", Network.Main); vout.ScriptPubKey = destination.ScriptPubKey; tx.Outputs.Add(vout);

给地址1NAK...hpr这个地址发送20000聪的BTC。

以前介绍过,比特币交易的内部细节并不会使用比特币地址,而是使用ScriptPubKey。

最后用 tx.Outputs.Add() 添加输出项。

Step 4. 签名交易

交易经过签名之后,才能发送到比特币网络上被所有节点进行确认,这一步较为复杂,后面再说。

二进制表示

交易就是一串二进制数字,没有被签名,也可以看到内部构成,可以输出 tx.ToHex() 看到这个结果。

0100000001416e9b4555180aaa0c417067a46607bc58c96f0131b2f41f7d0fb665eab03a7e0000000000ffffffff01204e0000000000001976a914e81d742e2c3c7acd4c29de090fc2c4d4120b2bf888ac00000000

内容比较长,拆开仔细看一下。

前4个字节表示比特币协议的版本号:00000001,即1。

后面1个字节表示输入项的个数:1,这个例子只有一项输入,对于每个输入项:

  • 32个字节,41 6e ... 3a 7e,表示前面某笔交易的ID,注意这里是TXID(7e3a...6e41)反序表示,计算机术语称为 little endian 表示法。
  • 4个字节,输出项的序号,这里为0。
  • 由于这笔交易未签名,Script Length为0,以后这里会有内容。

然后是输出项个数:1,对于每个输出项:

  • Value,转帐的BTC个数,单位是聪,这里的 20 4e 00 ... 反序表示即为4e2a,转换为十进制即为20000
  • Script Length:十进制数25
  • ScriptPubKey:76 a9 14 e8 1d 74 2e 2c 3c 7a cd 4c 29 de 09 0f c2 c4 d4 12 0b 2b f8 88 ac

专门解释一下ScriptPubKey那一行的意思,实际上是下面这一笔代码:

OP_DUP OP_HASH160 e81d742e2c3c7acd4c29de090fc2c4d4120b2bf8 OP_EQUALVERIFY OP_CHECKSIG

e8 1d ... 2b f8 是以前介绍过的公钥哈希,20个字节。

关于OP_DUP、OP_HASH160、OP_EQUALVERIFY和OP_CHECKSIG这些代码的二进制表示,就是76、a9、88、ac,可以在下面网址查到:

https://en.bitcoin.it/wiki/Script

跟在0x76(OP_DUP)和0xa9(OP_HASH160)之后的0x14,表示十进制数20,用来将随后的20个字节的公钥哈希压入堆栈。

--- END ---

0 人点赞