在比特币系统中并没有账户的概念,有的是遍布全网区块链的UTXO。所谓UTXO是指关联比特币地址的比特币金额的集合,是一个包含数据和可执行代码的数据结构。一个UTXO的基本单位是“聪”,“聪”是比特币的最小计量单位,一个比特币等于10^8聪。一个UTXO一旦被创建则不可分割,只能当做交易的输入被花费掉,花费后产生新的UTXO,花费后产生新的UTXO,这样周而复始地实现货币的价值转移。因此我们使用的比特币钱包看到的账户余额实际上时UTXO聚合计算的产物。
比特币的交易由交易输入和交易输出组成,UTXO是交易输入和输出的一部分,所以说UTXO是交易最基本的组成单元。
一个交易输入指向特定的UTXO,并且包含签名脚本,这个签名脚本用来满足UTXO的花费条件,为真则宣布自己对这笔资金拥有所有权。实际上节点在构造交易时会根据一定的算法选择一定数量的UTXO,同时生成相应的脚本签名作为输入的一部分加入到交易中。
每一个交易输出都伴随着资金的转移,交易输出包含一定数量的比特币和锁定脚本。锁定脚本作为资产指向一个比特币地址而设置的花费条件,只有满足这个花费条件的人才可以花费这笔资金。
总得来说,交易的输入和输出总是在大都数交易中都会同时出现,这样就实现比特币资金的价值转移,当然还有一种交易模型有些特殊,它只有输出,这就是比特币区块中第一笔交易称为coinbase交易,也称这为创币交易,它没有输入,是系统用于对矿工工作量证明的奖励,稳定增加相应的货币供应。
UTXO作为比特币独创的价值转移基本单元。比特币网络安全运行多年证明UTXO模型经受住考验,是与账户差异化的一种金融交易模式,对整个金融行业具有重大的积极影响!UTXO和Account各有各的优劣,在区块链项目和落地场景中选择合适的模型是极为关键的。目前比特币采用UTXO,以太坊选用的是Account,其它绝大多数区块链项目都基于这两种模型或变种。UTXO和Account的特点小编总结如下:
UTXO:原理简单、易于扩展、高度并行、隐匿性强
Account:易于理解、节省空间、易于实现、模式成熟
链接:http://www.qukuaiwang.com.cn/news/4331.html
UTXO还是Account模型是原链就需要确定的重要数据结构,团队的选择还是聚焦在两种典型的模型系统中,Account模型和UTXO模型,和其他大多数区块链设计一样, 选择了模型就决定了协议层的重要实现,两种模型各有利弊,不同区块链针对想聚焦的场景自身会有判断。
以太坊黄皮书的设计者Gavin Wood 对UTXO的理解,十分深刻, 既然UTXO有这么多的优点,他为什么弃用UTXO了? 这时你应该提出个问题,以太坊的最大亮点是什么?你肯定会回答:智能合约。正是因为智能合约的考虑,Gavin Wood要基于UTXO去实现图灵完备的智能合约(功能多样性的超级电脑)是困难的。而账户模型是天然的面向对象的,对每一笔交易,都会在相对应账户上进行记录(nonce++)。为了易于管理账户,而引入了世界状态,每一笔交易都会改变这个世界状态。这和现实世界是相对应的,每一个微小的改变,都会改变这个世界。
以太坊的账户模型很容易的实现了超级电脑模型。然而,性能一直是一道难以逾越的坎。在性能方面,utxo天然的可以并行运行,而基于世界状态的以太坊难以扩展。Gavin Wood当然是认识到这一点的,但要去改变,很难。那到不如用带有函数式编程特点的rust 去重写以太坊,也算是一种折中方案。
马克思哲学的否定之否定规律,事物的发展变化是螺旋式上升的。在区块链领域也是适合的,前进一步,也需要后退半步。基于UTXO模型去实现堆栈式虚拟机, 那还是会失去灵活性,用UTXO去结合以太坊EVM, 难度极大,也是不太实用的,这好比用haskell语言,去实现cpp风格的面向对象编程, 看不到有什么实际的意义。世界上没有银弹,比原链必须舍弃部分,妥协部分才能更好地适应场景。
我们在采用了比特币UTXO的易于并行运算的模型前提下,还做了针对性的改进,加了个资产号字段,使不同的资产可以在同一笔交易中处理转换,只要满足总输入等于总输出就可以。
但为了数据易于管理,易于编程, 我们引入以太坊的世界状态的概念,每一种资产都维持一个全局世界状态,该全局世界状态具有快速可查找,不可更改,简单易提供证明的特性。它的具体实现会参考以太坊的PAT树(一种扩展的基数树),比特币的merkle树,以及cosmos的IAVL树(一种不可更改的平衡二叉树)。每一种资产的所有outputs在一个全局的UTXO数据库中会有一个索引计数(每一个output的计数不能超过1,保持并行计算时,一个output最多能被一个BVM实例所使用,确保了数据一致性)。BVM是比原链实现的智能合约虚拟机模型, 每一笔交易的的执行,都会实例化一个BVM实例,只有在BVM实例中,各资产的世界状态才能在保持有效性,一致性的前提下更新状态。BVM可以并行创造多个”合约沙盒”实例, 在沙盒中合约的运行不受外界影响。
比原链创造的初衷是解决数字资产登记流转的问题, 对于公有链项目,保持简洁,保持高效,保持专注,就是保障安全, 新的扩展型UTXO模型正是基于这种场景实现的融合和改进。
链接:https://zhuanlan.zhihu.com/p/27406034
想要了解区块到底是什么,最简单快捷的办法就是分析它的数据结构,以 Bitcoin 中的区块 #514095 为例:
{
"hash":"00000000000000000018b0a6ae560fa33c469b6528bc9e0fb0c669319a186c33",
"confirmations":1009,
"strippedsize":956228,
"size":1112639,
"weight":3981323,
"height":514095,
"version":536870912,
"versionHex":"20000000",
"merkleroot":"5f8f8e053fd4c0c3175c10ac5189c15e6ba218909319850936fe54934dcbfeac",
"tx":[
// ...
],
"time":1521380124,
"mediantime":1521377506,
"nonce":3001236454,
"bits":"17514a49",
"difficulty":3462542391191.563,
"chainwork":"0000000000000000000000000000000000000000014d2b41a340e60b72292430",
"previousblockhash":"000000000000000000481ab128418847dc25db4dafec464baa5a33e66490990b",
"nextblockhash":"0000000000000000000c74966205813839ad1c6d55d75f95c9c5f821db9c3510"
}
在这个 Block 的结构体中,previousblockhash
和 merkleroot
是两个最重要的字段;前者是一个哈希指针,它其实是前一个 Block 的哈希,通过 previousblockhash
我们能递归地找到全部的 Block,也就是整条主链,后者是一个 Merkle 树的根,Merkle 树中包含整个 Block 中的全部交易,通过保存 merkleroot
,我们可以保证当前 Block 中任意交易都不会被修改。
Ethereum 的区块链模型虽然与 Bitcoin 有非常大的不同,但是它的 Block 结构中也有着类似的信息:
{
"jsonrpc":"2.0",
"result":{
"author":"0x00d8ae40d9a06d0e7a2877b62e32eb959afbe16d",
"difficulty":"0x785042b0",
"extraData":"0x414952412f7630",
"gasLimit":"0x47b784",
"gasUsed":"0x44218a",
"hash":"0x4de91e4af8d135e061d50ddd6d0d6f4119cd0f7062ebe8ff2d79c5af0e8344b9",
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner":"0x00d8ae40d9a06d0e7a2877b62e32eb959afbe16d",
"mixHash":"0xb8155224974967443d8b83e484402fb6e1e18ff69a8fc5acdda32f2bcc6dd443",
"nonce":"0xad14fb6803147c7c",
"number":"0x2000f1",
"parentHash":"0x31919e2bf29306778f50bbc376bd490a7d056ddfd5b1f615752e79f32c7f1a38",
"receiptsRoot":"0xa2a7af5e3b9e1bbb6252ba82a09302321b8f0eea7ec8e3bb977401e4f473e672",
"sealFields":[
"0xa0b8155224974967443d8b83e484402fb6e1e18ff69a8fc5acdda32f2bcc6dd443",
"0x88ad14fb6803147c7c"
],
"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size":"0x276",
"stateRoot":"0x87e7e54cf229003014f453d64f0344e2ba4fc7ee3b95c7dd2642cca389fa1efe",
"timestamp":"0x5a10968a",
"totalDifficulty":"0x1804de0c47ffe1",
"transactions":[...],
"transactionsRoot":"0xc2091b032961ca23cf8323ea827e8956fe6dda9e68d75bcfaa8b910035397e35",
"uncles":[]
},
"id":1
}
parentHash
和 transactionsRoot
分别对应着 Bitcoin 中 previousblockhash
和 merkleroot
,这两者在整个区块链网络中是非常重要的。
Block 结构体中的哈希指针在区块链中有两个作用,它不仅能够连接不同的区块,还能够对 Block 进行验证,保证 Block 中的数据不会被其他恶意节点篡改。
除了第一个 Block,每一个 Block 中的 prev_hash
都是前一个 Block 的哈希,如果某一个节点想要修改主链上 Block 的交易,就会改变当前 Block 的哈希,后面的 Block 就没有办法通过 prev_hash
找到前面的链,所以当前节点篡改交易的行为就会被其他节点发现。
另一个字段 merkleroot
其实就是一个 Merkle 树 的根节点,它其实是一种使用哈希指针连接的数据结构;虽然 Merkle 树有叶节点和非叶节点,但是它只有叶节点会存储数据,所有的非叶结点都是用于验证数据完整性的哈希。
每一个 Block 中的全部交易都是存储在这个 Merkle 树中并将 merkleroot
保存在 Block 的结构体中,保证当前 Block 中任意交易的篡改都能被立刻发现。
在 Bitcoin 以及其他使用 UTXO 模型的加密货币中,某一个『账户』中的余额并不是由一个数字表示的,而是由当前区块链网络中所有跟当前『账户』有关的 UTXO 组成的。
{
"addr":"14uhqGYDEhqwfdoP59QdLWdt4ha5CHttwQ",
"n":1,
"script":"76a9142ae017a5bd24a3f935897085253e503fbfd66f4e88ac",
"spent":false,
"tx_index":335926477,
"type":0,
"value":21680000
}
上述的 UTXO 中包含了很多信息,例如:包含当前 UTXO 属于的交易索引 tx_index
、交易接收方的地址 addr
、交易的数额 value
。
UTXO 其实就是交易的一部分,基于 UTXO 模型的交易由输入和输出两个部分组成:
{
"txid":"5be7a9e47f56c98e5297a44df52da0475f448ece98bb51489103cdf70653092f",
"hash":"5be7a9e47f56c98e5297a44df52da0475f448ece98bb51489103cdf70653092f",
"version":1,
"size":224,
"vsize":224,
"locktime":0,
"vin": [...],
"vout": [...],
"hex":"0100000001a90b4101e6cbb75e1ff885b6358264627581e9f96db9ae609acec98d72422067000000006b483045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e0121025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4ffffffff02a037a0000000000017a91477df4f8c95e3d35a414d7946362460d3844c2c3187e6f6030b000000001976a914aba7915d5964406e8a02c3202f1f8a4a63e95c1388ac00000000",
"blockhash":"0000000000000000000c23ca00756364067ce5e815deb5982969df476bfc0b5c",
"confirmations":5,
"time":1521981077,
"blocktime":1521981077
}
在每一笔合法的交易中,所有的输入的 value
之和必须大于所有输出的 value
之和,这两者之间的差值就是矿工费:
sum(inputs.value) = sum(outputs.value) + fee
基于 UTXO 的交易模型,与我们在日常生活中使用纸币的场景是非常相似的,每一张纸币都是不可分割的整体,当我们想要使用现金购买商品或者服务时,往往都会获得找零。
inputs = price + change + fee
<<<<<<<<<<<< Vitalic 解释
在比特币中,一笔交易“在黑盒子里”实际运作的方式是:花费 一种东西的集合,这种东西被称为 “未被花费的交易输出”(即 “UTXO” ),这些输出由一个或多个之前的交易所创造,并在其后 制造 出一笔或多笔新的 UTXO ,可以在未来的交易中花费。每一笔 UTXO 可以被理解为一个 “coin(币)”:它有面额、有一个所有者。而且,一笔交易若要有效,必须满足的两个规则是:1)该交易必须包含一个有效的签名,来自它所花费的 UTXO 的拥有者;2)被花费的 UTXO 的总面额必须等于或者大于该交易产生的 UTXO 的总面额。一个用户的余额因此并不是作为一个数字储存起来的;而是用他占有的 UTXO 的总和计算出来的。
如果一个用户想要发送一笔交易,发送 X 个币到一个特定的地址,有时候,他们拥有的 UTXO 的一些子集组合起来面值恰好是 X,在这种情况下,他们可以创造一个交易:花费他们的 UTXO 并创造出一笔新的、价值 X 的 UTXO ,由目标地址占有。当这种完美的配对不可能的时候,用户就必须打包其和值 大于 X 的 UTXO 输入集合,并添加一笔拥有第二个目标地址的 UTXO ,称为“变更输出”,分配剩下的币到一个由他们自己控制的地址。
>>>>>>>>>>>https://ethfans.org/posts/thoughts-on-utxo
输入和签名
UTXO 模型中的每一笔交易都是由多个交易输入组成的,这些输入其实就是 UTXO + 签名:
"inputs":[
{
"sequence":4294967295,
"witness":"",
"prev_out":{
"spent":true,
"tx_index":338283541,
"type":0,
"addr":"1Djf4kCa1hZ58h18FjjWFc5ZxQKxJSV5sg",
"value":195659190,
"n":0,
"script":"76a9148bb2c126e768c64a61b8ca95cf9602fceba0404b88ac"
},
"script":"483045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e0121025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4"
}
]
上述 JSON 其实就是 Bitcoin 交易 #338309214 的输入,这里的 prev_out
就来自于另一笔交易 #338283541 的输出,通过不停的回溯,最终我们会找到当前交易涉及的 Coinbase,也就是当前 UTXO 相关 Bitcoin 被挖出来的 Block 的首笔交易。 通过 txid
和 vout
两个字段,我们能够在区块链网络中定位到唯一一个 UTXO,这个 UTXO 加上持有当前 UTXO 的地址对交易的签名构成了一个交易输入。(简述:输入是一个或多个UTXO+签名,通过prev_out回溯到Coinbase)
输出
每一个交易都可能会有多个输出,也就是 vout
数组,每一个 vout
都可以指向不同的地址,其中也有当前输出包含的值 value
,在这里也就是 Bitcoin 的单位:
"out":[
{
"spent":true,
"tx_index":338309214,
"type":0,
"addr":"3CcqrGq4oQcfx3u75ijj4tDiqf4HJvhoeP",
"value":10500000,
"n":0,
"script":"a91477df4f8c95e3d35a414d7946362460d3844c2c3187"
},
{
"spent":true,
"tx_index":338309214,
"type":0,
"addr":"1GedHcxdxq2tab98hqAmREUK9BBYHKznof",
"value":184809190,
"n":1,
"script":"76a914aba7915d5964406e8a02c3202f1f8a4a63e95c1388ac"
}
],
每一个未被使用的 out
就是一个 UTXO(Unspent Transaction Output),我们可以通过其中的 addresses
字段找到持有当前输出的地址。
UTXO 模型通过链式的方式组织所有交易的输入和输出,每一个交易的输出最终都能追寻到一个 Coinbase,也就是当前 Bitcoin 被挖出时的区块的第一笔交易。
由于在 UTXO 中没有账户的概念,所以并行地处理交易不会出现任何问题,同时不可变的账本能够让我们在 Bitcoin 节点快速更新时,也能分析某一时刻整个网络中数据的快照。
虽然 UTXO 模型的不可变账本条目带来一些好处,但是当我们需要计算某个地址中的余额时,需要遍历整个网络中的全部相关区块,同时,并行的处理交易虽然可行,不过并行的创建交易却会出现很多问题,例如多笔交易使用了同一个 UTXO,导致双花,最终只有一笔交易能够被网络确认。
UTXO 模型确实能够解决区块链世界中的各种问题,它的核心思想就是保证已经写入的数据不可变,链式的 UTXO 就是基于这一核心思想的,通过哈希指针连接不同交易的输入和输出,保证所有交易的合法性。
与 UTXO 模型不同的就是账户余额模型,它跟现实世界中的银行账户非常相似,Ethereum 就使用了账户余额模型存储区块链中的数据。
相比于 UTXO 模型,账户余额模型更加容易实现和理解,如果忽略很多 Ethereum 的实现细节,那么在整个网络中只存在三种对象:账户、交易和区块。在这里,我们会介绍该模型中的三个最重要的概念,虽然 Block 并不是账户余额模型中独有的概念,但是我们也会介绍它在 Ethereum 中有什么特殊之处。
Ethereum 其实就是一个巨大的状态机,其中的状态都是由多个账户组成的,每一个账户都包含四个字段 (nonce, ether_balance, contact_code, storage)
:
在 Ethereum 中有两种类型的账户,一种是被私钥控制的账户,它没有任何的代码,与 Bitcoin 地址基本有完全相同的功能,能够向网络中发送已签名的交易;另一种是被合约代码控制的账户,能够在每一次收到消息时,执行保存在 contract_code
中的代码,所有的合约在网络中都能够响应其他账户的请求和消息并提供一些服务。
所有账户的 nonce
都必须从 0
开始递增,当前账户每使用 nonce
签发并广播一笔交易之后,都会将其 +1
;UTXO 模型决定了来自同一地址的多笔金额相同的交易完全不同,从原理上避免了重放攻击,因为账户余额模型中不存在 UTXO,交易仅仅是账户 ether_balance
的变动,所以在这里引入 nonce
来解决重放攻击的问题。
合约账户
被私钥控制的账户与 Bitcoin 中地址其实差不多,所以没有什么可以解释的,这里简单介绍一些合约账户。目前 Ethereum 网络上最多的合约账户应该就是 ERC20 的合约了,我们平时经常说的 Token 就是 Ethereum 上的合约,这些合约其实也是 Ethereum 账户:
contract ERC20Interface {
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
Token 的转账其实就是合约的调用,所有的账户余额都是存储在这个合约的 balances
中:
mapping(address => uint256) balances;
每一笔 Token 的转账都会改变这个 mapping
中对应地址的值并发出 Transfer
事件,这也就是 Token 的实现原理;部署 Token 的智能合约其实非常简单,很多加密货币项目其实都只是一个 ERC20 的 Token,发行的成本几乎为 0。
由于没有 Bitcoin 复杂的 UTXO 模型,Ethereum 的交易模型也简单,交易中没有输入和输出的 TransactionIO
结构,只有 from
和 to
两个地址:
{
"blockHash":"0xb4a992ff99a487db8421f516be998920f06dfe5d355d88e3b7f22b7422e6340d",
"blockNumber":"0x24f85c",
"chainId":null,
"condition":null,
"creates":null,
"from":"0x8b56adcf332ff80a1f1bf433975dcb28b730d110",
"to":"0xe94b04a0fed112f3664e45adb2b8915693dd5ff3",
"value":"0x10d43fb8311ca800",
"gas":"0x2062a",
"gasPrice":"0x560aab7c5",
"hash":"0xfea448d11cfa863c8b3c38d3b65649e66c1957f9ac16638e3a0edff45a6b3d84",
"input":"0x0f2c9329000000000000000000000000fbb1b73c4f0bda4f67dca266ce6ef42f520fbb98000000000000000000000000e592b0d8baa2cb677034389b76a71b0d1823e0d1",
"nonce":"0x3fe",
"publicKey":"0x765b0f012e49f6a4cc5c917fb176984b24814bdaf5f9464db1a7f9ffcc730cb678f69e49f78aa9de8249cce138bbc25cf8842374d8d09089dff7f1ef6906f4fb",
"r":"0x4275d35821dec971f6d58c2adae077ffcdfa3ec74af542a2d29ab4e5239d8b25",
"raw":"0xf8b48203fe850560aab7c58302062a94e94b04a0fed112f3664e45adb2b8915693dd5ff38810d43fb8311ca800b8440f2c9329000000000000000000000000fbb1b73c4f0bda4f67dca266ce6ef42f520fbb98000000000000000000000000e592b0d8baa2cb677034389b76a71b0d1823e0d11ca04275d35821dec971f6d58c2adae077ffcdfa3ec74af542a2d29ab4e5239d8b25a036221b525c758c45e60f964eec698ae33208dfa74bea7f77dff002ceec418b0a",
"s":"0x36221b525c758c45e60f964eec698ae33208dfa74bea7f77dff002ceec418b0a",
"standardV":"0x1",
"transactionIndex":"0x8",
"v":"0x1c"
}
交易的手续费也不再是交易输入输出 value
的差值,而是使用 gas
和 gasPrice
来表示,为了防止重放攻击,这里也引入了 nonce
字段。
(nonce, from, to, value, input)
是一个 Transaction 包含的最重要的几个字段,通过 nonce
防止重放攻击,from
和 to
分别表示了当前交易的发出者和接受者,value
是当前交易包含的 Ether
,input
中包含了合约调用相关的二进制信息。
每当一个 Transaction 被 Ethereum 主网挖到后,from
和 to
账户的 Ether
余额就会变动,Ethereum 就像一个状态机,它接受一个又一个的 Transaction 并不停改变自己的状态。
账户余额模型是一种非常容易理解的区块链应用模型,它与我们生活中的账户模型非常相似,只是为了保证账户的安全,使用了签名以及 nonce
的机制阻止恶意的攻击。这种基于账户余额模型的应用包含了一个包含所有账户余额的全局状态,在进行转账时,需要由节点对账户的余额进行验证,判断当前账户是否有足够的 Ether
进行转账。
无论是 UTXO 模型还是账户余额模型,都能够很好地解决区块链世界中的『安全』问题,保证交易的合法,从原理上杜绝一些可能的攻击行为,实现原理的不同其实也只是由于出发点不同,在设计时权衡了利弊;UTXO 模型相比于账户余额模型有以下的两个优点:
而账户余额模型也有它的优点:
在比特币交易系统中,没有账户iD和账户密码一说,也没有账户余额计算一说。但是 有公钥(钱包地址)和秘钥,可以浅显的类比理解为:公钥就是账户ID,私钥就是账户密码。在比特币交易系统中,知道对方公钥,就可以给对方转账进行交易。拥有了私钥,就代表着拥有了该地址下所有UTXO的使用权和控制权。
总而言之,软件开发到最后就是进行权衡的过程,选择一些优势必然会在另外一些领域上处于劣势,如何在不同的需求进行权衡是开发区块链应用以及所有应用都需要考虑的事情。