比特币转账机制(UTXO)
- 比特币是基于UTXO的结构存储有关用户余额的数据,系统的整个状态就是一组UTXO集合,每个UTXO都有一个所有者和面值(相当于法币的面值),每一笔交易都会花费若干个输入的UTXO,并且根据规则创建若干个新的UTXO
- 每个引用的输入必须是有效的(尚未花费的UTXO),对于一个交易,必须包含与当前输入UTXO相匹配的所有者的签名,并且保证输入必须大于等于总的输出值,比特币系统中用户的余额是用户具有私钥的UTXO的总值
以太坊的转账机制(Accounts)
- 以太坊的使用机制:存储系统中所有用户的列表
- 每一个账户都包括了一个余额(balance),和以太坊特殊定义的数据(代码和内部存储,这个是指智能合约)
- 以太坊的账户类型和银行使用的是一致的,一笔交易的产生,需要先扣除交易发起方的账户余额,如果交易方账户余额足够,则交易生效;然后,交易接收方的账户会记入这笔交易。
- 智能合约:如果接收方的账户存在相关代码(智能合约),则代码会自动运行,并且他的内部存储也可能被改变,比如代码还可能设定向其他账户发起新的交易信息,这就会造成进一步的借贷资金关系。
比特币的UTXO的优缺点
- 高度的隐私性,因为每一笔交易使用的不会是相同的地址,因此很难将其与现实的用户连接起来,保护了用户的隐私性,这样也就限制了比特币只能适用于货币,提升空间很小,比如面对现在的Dapps(分布式app),Dapps通常设计到跟踪和用户绑定的复杂状态,很难像货币一样对于用户状态简单的划分。
- 潜在的可扩展性,UTXO在理论上更符合可扩展性的要求,因为只需要依赖UTXO的人去维护基于Merkle树的所有权证明,即使一笔交易的数据丢失,仅仅所有者会受到对应的UTXO的损失,但是不会影响接下来的交易。如果是账户模式,如果每个人都丢失了与账户相对应的Merkle树的部分,那么就会使得与该账户相关的信息完全无法处理。
以太坊的优缺点
- 节省空间,不将UTXO分开存储,而是合并到特定的用户,每个交易只需要一个输入,一个签名、并且产生一个输出
- 更好的替代性,货币的本质都是同质化、可以替代的,UTXO设计使得货币分为可花费和不可花费两类,这个很难和现实模型对
- 更加简单:容易编码和理解,特别是设计复杂脚本的时候,UTXO在脚本逻辑复杂时候更加令人费解
- 更容易维护持久性节点,只需要沿着特定方向扫描状态树,亲节点可以很容易的随时访问账户相关的所有数据。而UTXO的每一个交易都会使得状态发生改变,这个对于亲节点来说长时间运行分布式APP会造成很大的压力
对比
|
BitCoin |
Ethereum |
设计定位 |
现金系统 |
去中心化应用平台 |
数据组成 |
交易列表(账本) |
交易和账户状态 |
交易对象 |
UTXO |
Accounts |
代码控制 |
脚本 |
智能合约 |
以太坊的账户类型
- 外部用户/普通账户
- 合约账户/内部用户
外部用户/普通账户
- 有对应的以太坊余额
- 可以发起交易(转账/触发合约代码)
- 由用户私钥控制
- 没有关联代码(智能合约有)
合约账户/内部用户
- 有对应的以太坊余额
- 有关联代码
- 由代码控制
- 可以通过交易或者来自其他合约的调用消息来触发代码执行
- 执行代码时候可以操作自己的存储空间,也可以调用其他的合约
消息
- 合约可以向其他合约发送“消息”
- 消息是不会被序列化的虚拟对象,只存在于以太坊的执行环境(EVM)中
- 可以看作是函数的调用
- 内容:消息的发送方、消息的接收方、金额(VALUE)、数据(DATA,可选)、START GAS 最大支付的GAS
合约
- 可以读/写自己的内部存储(32字节key-value的数据库)
- 可以向其他合约发送消息,依次触发执行
- 一旦合约运行结束,并且由它发送的消息触发的所有子执行(sub-exception)结束,EVM就会中止执行,直到下次交易被唤醒
合约的应用
- 维护一个数据存储(账本),存放其他合约或者外部的世界有用的内容比如,发售自己的虚拟货币
- 通过合约账户实现一种具有更加复杂的访问策略的普通账户(EOA),这个被称为“转发合同”,只有在满足某些特定的条件的时候,才会将传入的消息重新发送到某个所需要的目的地址:例如,一个人有一份转发合约,该合约会等待直到给定的三个私钥中的两个确认之后,再重新发送特定的消息,比如钱包合约
- 管理多个用户之间的持续合同或者关系。包括金融合同,以及某些特定的托管合同或者某种保险。遗嘱,遗产分配。
交易数据结构
(交易是包含以下数据的序列化的二进制消息)
- Nonce 由发起人EOA发出的序列号,用于防止消息重播,相当于统计从一个账户中交易记录的次序,防止了双重支付。
- Gas price 交易发起人愿意支付的gas单价(wei)
- Start gas 交易发起人愿意支付方最大gas量
- To:以太坊交易的目的地址
- Value:发送到目的地的以太数量
- Data:可变长度的二进制数据负载(payload)
- v,r,s 发起人EOA的ECDSA签名的三个组成部分
- 交易消息的结构使用递归长度前缀(RLP)编码方案进行序列化,该方案专为以太坊中准确和字节完美的数据序列化而创建。
交易中的nonce
- 黄皮书的定义:一个标量值,等于从这个地址发送的交易数,或者对于关联code的账户来说,就是这个账户创建合约的数量
- nonce不会明确存储为区块链中账户状态的一个部分,相反它是通过计算发送地址已经确认的交易的数量来动态计算的
- nonce值还用于防止错误计算账户的余额。Nonce强制来自任何地址的交易按照顺序处理,没有间隔,无论节点接收他们的顺序如何。
- 使用nonce是为了确保所有的节点计算的是相同的余额和正确的交易,等同于防止了“双重支付/重放攻击”,但是因为不像比特币使用UTXO机制,因此以太坊只有在错误计算账户的余额时候才会发生“双重支付/重放攻击”。
并发和nonce
- 以太坊是一个允许(节点、客户端,Dapps)并发的系统,但是强制单例状态。例如,出块的时候只有一个系统状态
- 假如我们有多个独立的钱包客户端,比如MetaMask和Geth,他们都是使用同一个地址生成交易。如果我们希望同时发起交易,该如何设置交易的nonce呢?
- 方法1,用一台服务器为各个应用分配nonce,先来先服务,(单点故障),如果先来的一个应用发生故障,那么失败的交易会使得后续的交易阻塞
- 方法2,生成交易后不分配nonce,也不签名,而是将其放在一个队列中等待,另一个节点跟踪nonce并签名交易。同样会出现单点故障,而且跟踪nonce和签名的节点是无法实现真正的并发
特殊的交易:创建(部署)合约
-
特殊的交易,具有数据负载但是没有value,则这笔交易就是为了创建新的合约
-
合约的创建交易被发送到特殊目的的地址,即0x0.这个地址既不代表EOA也不代表合约,也不会花费以太币或者发布交易,仅仅具有特殊的含义“创建合约”
-
还有一种目的:销毁以太币
向EOA或合约传递数据
- 当交易包含数据有效负载的时候,一般是发送到合约地址的,但是发送给EOA也是可以的
- 如果发送数据给EOA,数据负载的解释取决于钱包
- 如果发送给数据给合约地址,EVM会解释为函数调用,从payload里面解码出函数的名称和参数,调用该函数并且传入参数
- 发送给合约的数据有效负载是32字节的十六进制序列化编码
- 函数选择器:函数原型的keccak256哈希的前4个字节,允许EVM明确识别将要调用的函数
- 函数参数:根据EVM定义的各种基本类型的规则进行编码
交易的Value和data
交易的“有效负载”包含在两个字段中:value和data。交易可以同时有value和data,仅有value、仅有data、data和value都没有
- 仅有value的交易是一笔以太的付款
- 仅有value的交易一般是合约的调用
- 进行合约的调用的时候,除了传输data,还可以发送以太币,从而使得交易中同时包含data和value
- 没有value也没有data的交易,只是在浪费gas,但是交易是有效的
交易的接收者(to)
- 交易接受者在to字段中指定,是一个20字节的以太坊地址,地址可以是EOA或者合约的地址
- 以太坊无法判定任何20字节的地址是否是有效的,是否是公钥正确派生的,但是这笔交易是有效的,只是钱回不来了
- 如果交易的地址是无效的,将会销毁发送的以太币,使其永远无法访问
- 验证接收人的地址是否是有效的,应该在用户界面一层完成
gas计算
- 发起交易时的gas limit并不是要支付的gas数量,而是给定一个消耗gas的上限,相当于押金,如果存在剩余,会返回到交易发起方的账户
- 实际支付的gas数量是执行过程中消耗的gas(gasUsed),gas limit中剩余的部分会返回给发送人,单价是由设定的gasPrise而确定
- 最终支付的费用 totalCost = gasPrise * gasUsed
- TotalCost会作为交易的手续费支付给矿工
来源:oschina
链接:https://my.oschina.net/u/4313437/blog/4326336