内容简介:比特币的交易,由一个或多个输出和一个或多个输入(交易的每个输出上,都会附上一个加密难题,定义将来在花费这笔交易的每个输入上,都要提供一个解锁脚本,解决或满足之前附在这笔
比特币的交易,由一个或多个输出和一个或多个输入( coinbase
交易是一种特殊情况)构成
交易的每个输出上,都会附上一个加密难题,定义将来在花费这笔 UTXO
时需要满足的条件
交易的每个输入上,都要提供一个解锁脚本,解决或满足之前附在这笔 UTXO
上的加密难题或条件,解锁 UTXO
用于支付
如果你从前面的文章一路看过来,理解了比特币交易的细节,你应该能设计出下面的数据结构
对交易的每个输出 TxOut
,需要有
UTXO
对交易的每个输入 TxIn
,需要有
- 这笔
UTXO
来自之前哪笔交易的第几个输出(需要表达 交易链条 ) - 解锁脚本
对交易,需要有
TxIn TxOut
这样的设计能满足需求,同时又足够精简
这篇文章,介绍比特币交易的数据结构
输出
/** * An output of a transaction. It contains the public key that the next input * must be able to sign with to claim it. */ class CTxOut { public: Amount nValue; // UTXO的币值,8字节整数,单位是聪 CScript scriptPubKey; // 锁定脚本 ...... };
Amount
的类声明在 这里
struct Amount { private: int64_t amount; ...... };
CScript
的类声明在 这里
输入
/** * An input of a transaction. It contains the location of the previous * transaction's output that it claims and a signature that matches the output's * public key. */ class CTxIn { public: COutPoint prevout; // UTXO的来源,包含一个交易哈希和一个索引号,用来表示哪笔交易的第几个输出 CScript scriptSig; // 解锁脚本 uint32_t nSequence; ...... };
COutPoint
的类声明在 这里
/** * An outpoint - a combination of a transaction hash and an index n into its * vout. */ class COutPoint { private: TxId txid; // 交易哈希 uint32_t n; // 输出的序号 ...... };
输入有一个 4
字节的 nSequence
字段,以后的文章中再说
交易
/** * The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ class CTransaction { public: const int32_t nVersion; // 交易结构的版本标识,4字节 const std::vector<CTxIn> vin; // 输入数组 const std::vector<CTxOut> vout; // 输出数组 const uint32_t nLockTime; ...... private: const uint256 hash; // 交易的哈希 ...... };
交易有一个 4
字节的 nLockTime
字段,以后的文章中再说
序列化和反序列化
程序中,一般用特定的 数据结构 ,来表示和存储具体的数据,就像上面描述的那样
这样的数据,方便人们识别和理解,方便程序操作,但不方便在网络上传输
在传输前需要将数据结构转换成方便网络传输的 字节流 形式,这个过程称为 序列化
从字节流“恢复”数据成数据结构的形式,这个过程称为 反序列化
举个例子,方便理解
我们可以定义下面的数据结构,来表示二十四小时制的时间
Type Time { uint32_t hour; uint32_t minute; uint32_t second; };
时分秒分别用 4
字节整数表示, 20:35:10
可以表示为
Time t; t.hour = 20; // 00 00 00 14 t.minute = 35; // 00 00 00 23 t.second = 10; // 00 00 00 0a
注释后面是数据的十六进制表示
在传输数据时,发送
00 00 00 14 00 00 00 23 00 00 00 0a
并规定
对方在收到数据后,就能把字节流还原成数据结构的形式
注意到
数据结构不仅包含数据的值,还描述“这是什么数据”
当你看到 t.hour = 20
,你知道这个数据表示时间中的 小时
,值为 20
但当你看到 00 00 00 14
,你只知道这个数据的值为 20
,不知道这是 20
时,还是 20
分,还是 20
秒
所以,需要定义序列化的规则
另一个不容易注意到的点是,需要多字节表达的数据项(用 4
字节来表达 小时
字段)的值,如何在在字节流中排列
上面的例子中,你收到第一个 4
字节的顺序为
这默认了,先收到的字节为这个数据的 高位字节 ,后收到的为 低位字节 ,所以你得到 00 00 00 14
换个角度说,如果对方认为先收到的字节为这个数据的低位字节,那他会把这个数据解析成 14 00 00 00
,引起错误
所以,字节流传输时,还需要定义字节的排列模式,这是另一个很有意思的话题,称为 大小端模式 ,你可以搜些文章读一读
比特币系统中,使用 小端模式 ,认为 先收到的字节为数据的低位字节
如果我们以小端模式来传输刚才的数据,字节流应该是
14 00 00 00 23 00 00 00 0a 00 00 00
序列化输出
长度(字节) | 描述 |
---|---|
8 | 以聪为单位的币值 |
1~9 VarInt | 后面紧跟的锁定脚本,有多少字节 |
变长 | 锁定脚本的内容 |
这样一个序列化后的交易输出
60e31600000000001976a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788ac
可以反序列化为
-
60e3160000000000
是币值,小端模式,值为00 00 00 00 00 16 e3 60
,1500000
聪,0.015
比特币 -
19
,后面紧跟的25
字节是锁定脚本 -
76a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788ac
,锁定脚本的内容
序列化输入
长度(字节) | 描述 |
---|---|
32 | 交易哈希,UTXO来自哪笔交易 |
4 | 输出的序号,UTXO是那笔交易的第几个输出,从 0 开始计数 |
1~9 VarInt | 后面紧跟的解锁脚本,有多少字节 |
变长 | 解锁脚本的内容 |
4 | nSequence |
这样一个序列化后的交易输入(我加了空格和换行方便识别)
186f9f998a5aa6f048e51dd8419a14d8a0f1a8a2836dd734d2804fe65fa35779 00000000 8b 483045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301410484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf ffffffff
可以反序列化为
- 这笔
UTXO
,来自之前的交易7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18
的第0
个输出 -
8b
,后面紧跟的139
字节,是解锁脚本, -
48304502...8d752adf
,解锁脚本的内容 -
ffffffff
是nSequence
的值
序列化交易
长度(字节) | 描述 |
---|---|
4 | 交易结构的版本 |
1~9 VarInt | 交易包含几个输入 |
变长 | 输入数组 |
1~9 VarInt | 交易包含几个输出 |
变长 | 输出数组 |
4 | nLockTime |
你注意到没有,交易序列化后, 没有 交易哈希的部分
只需要对序列化后的交易数据做哈希运算,就可以得到交易的哈希值,这种冗余的信息,并不需要传输
Alice去Bob的咖啡店支付 0.015
比特币购买咖啡,生成了交易 0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2
下面是这笔交易序列化后的样子(我替你加了一些空格和换行),你能从中找到各个字段的信息吗?
01000000 01 186f9f998a5aa6f048e51dd8419a14d8a0f1a8a2836dd734d2804fe65fa35779 00000000 8b 483045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301410484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf ffffffff 02 60e3160000000000 19 76a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788ac d0ef800000000000 19 76a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac 00000000
One more thing
你有没有发现,序列化规则中,描述脚本长度、数组个数的字段,其长度也是变化的
60e3160000000000 19 76a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788ac
这是刚才的例子,前 8
字节 60e3160000000000
表示币值是确定的,因为规则定义了币值用 8
个字节表达
但“锁定脚本的大小”字段,其长度是不确定的,可以用 1~9
个字节来表达
为什么我们能确定后面跟着的锁定脚本长度是 19
,而不是 76 19
?欢迎留言 :yum:
参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 数据结构 – 用于构建文件系统的数据结构?
- 荐 用Python解决数据结构与算法问题(三):线性数据结构之栈
- 数据结构和算法面试题系列-C指针、数组和结构体
- 请问二叉树等数据结构的物理存储结构是怎样的?
- 数据结构——单链表
- 常用数据结构
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
互联网思维的企业
[美] Dave Gray Thomas Vander Wal / 张 玳 / 人民邮电出版社 / 2014-4-25 / 59.00元
本书指导企业跳出仅更新自家产品和服务的怪圈,在管理方式、组织结构和公司文化方面进行变革,建立具有互联网思维的企业。书中通过大量图示和示例阐述了互联式公司必需的基础元素(透明的互动和交流平台,推崇自治和应变的组织结构,实验和学习的企业文化),以及一套鼓励员工创新的新式管理和奖励体系。最后,讨论板可方便你在工作时间和同事探讨如何增加公司的互联程度。一起来看看 《互联网思维的企业》 这本书的介绍吧!