账户类型、瓦斯和交易

外部账户和合约账户

在以太坊中共有两种类型的账户
  • 外部账户(Externally Owned Accounts)
  • 合约账户(Contracts Accounts)

这种区分可能会在第四个阶段:宁静(Serenity)阶段消除。

外部账户

外部直接控制的账户

  • 有以太币余额
  • 可以发起交易(以太币转移或触发合约代码)
  • 通过私钥控制
  • 没有相关代码

合约账户

合约

  • 有以太币余额
  • 有相关的代码
  • 通过交易或其它合约的消息(调用)来触发代码执行
  • 当代码被执行 - 可以执行任意复杂操作(图灵完备) - 可以操作自己的存储,也就是说,有自己的永久状态 - 可以调用其它的合约

所有以太坊区块链动作的发起,都由外部账户发起的交易设定。每当合约接收到一个交易时,代码会按照指示的参数执行,,合约执行也是交易的一部分。合约代码被参与到网络中每个节点上的以太坊虚拟机执行,验证完毕后会包含于一个新块中。

合约的执行需要是完全确定的,它唯一的语境就是所属区块在区块链上的位置和所有可用数据。 区块链上的区块表示时间单元,链上的区块作为离散的特定时间点表现出整个区块链的历史状态。

所有以太币余额和价值都是以wei为单位计量的,1以太币=1e18wei。

Note

以太坊中的”合约”不应该被看作”履行”或”遵守”的内容,相反,它们更像是以太坊虚拟机内部的”自动代理”,每当被消息或交易调用总会执行特定的代码段,通过直接控制以太币余额或它们的公/私钥来改变它们的永久状态。

什么是交易?

以太坊的术语”交易”通常是指区块链中一个经过签名验证的数据包,包中存储着从一个账户发往另一个账户的消息。

交易包含:
  • 消息的接收者
  • 经过签名验证身份的发送者,证明通过区块链发送消息给接收者的意图
  • VALUE 字段 - 发送者给接收者发送货币的总量(单位:wei)
  • 一个可选字段,可以包含发送给合约的消息内容
  • STARTGAS 值,表示允许交易执行的最大计算步数
  • GASPRICE 值,表示发送者愿意支付的瓦斯费用,一个单位的瓦斯对应一个原子操作,也就是一个计算步骤

什么是消息?

合约由能力给其它的合约发送”消息”,消息是只存在于以太坊虚拟机中的不能被序列化的事物,可以被理解为功能调用。

消息包含
  • 消息的发送者
  • 消息的接收者
  • VALUE 字段 - 发送者给接收者发送货币的总量(单位:wei)
  • 一个可选字段,合约的实际输入数据
  • STARTGAS 值,限制了消息可以调用即将执行代码所消耗的最大瓦斯数量

本质上来讲,消息和交易是非常类似的,区别在于消息是合约创建的而交易是外部账户创建的。消息在合约调用``CALL`` 或 ``DELEGATECALL``操作码时被创建和发送,和交易类似,消息会导致接收者账户执行它的代码。因此,合约可以和其它合约有着外部执行者与合约同样的关系。

什么是瓦斯?

以太坊在区块链上实现了一个叫做以太坊虚拟机的执行环境(EVM),每一个参与到网络中运行EVM的节点都是区块验证协议的一部分,它们验证区块中的交易序列、运行通过交易触发的代码,网络中的每一个完整节点都做着同样的计算并且存储同样的状态,显然以太坊的目标不是优化计算,它的并行过程是冗余的并行,提供了一种不需要第三方、不可操控或不可暴力垄断的共识机制。重要的一点在于它不是用来优化计算的,事实上,合约执行的过程中节点的冗余和重复导致了高额的成本,所以在区块链上不适合做中心化的计算工作。

当你在运行一个去中心化的应用程序(dapp)时,它会读取和修改区块链的状态,但是通常情况下只会把业务逻辑和关键的共识状态放在区块链中。

当一个合约被消息触发或交易激活导致执行时,每一个指令都会在网络上的所有节点上被执行,这就产生了花费:每一个指定的操作都会有指定的成本,成本被用瓦斯单位来计算。

以太坊区块链上交易的发送者会向每个操作支付费用,这个费用的名字就是瓦斯,瓦斯这个名字的灵感来源于,执行费用作为加密燃料,驱动着智能合约。瓦斯通过以太币从执行代码的矿工那里购买,瓦斯和以太币不是耦合的,瓦斯的价格取决于矿工而以太币的价格会随着市场进行波动。这两者是由自由市场调解的:矿工决定瓦斯的价格,他可以拒绝为那些低于他设置最低价格的交易服务。获取瓦斯你只需要在你的账户中添加以太币,以太坊客户端会自动为你兑换你所指定交易最大消耗的瓦斯。

以太坊协议为每一个交易和合约的计算步骤设置费用,目的在于防止对以太坊网络的恶意攻击和滥用。每一笔交易都需要包含瓦斯限制和愿意支付的瓦斯单价,矿工有权选择验证这个交易以获取费用或者拒绝。如果交易执行完成(包含原始消息和所有子消息)所消耗的总费用,小于或等于瓦斯限制,那么交易可以被执行;如果所需瓦斯大于瓦斯限制,那么所有的操作都讲被回滚,交易仍然有效,矿工仍然收取费用。所有超过花销都瓦斯都会以以太币的形式返还给发送者,不必担心超支的情况,你只会支付你总共花费掉掉瓦斯。这也意味着,高于预估瓦斯价格进行交易也是安全和有效的。

预估交易费用

一笔交易的以太币总消耗基于两个因素:

瓦斯使用量 是交易中消耗的所有瓦斯数量总和

瓦斯价格 是交易中指定的一个单位的瓦斯的价格(用以太币计算)

总费用 = 瓦斯使用量 * 瓦斯价格

瓦斯使用量

以太坊虚拟机中每一个操作都指定了需要花费瓦斯的数量,”瓦斯使用量”就是执行所有指令的瓦斯消耗量总和。这里有个 讨论 可以让你有个更直观的感受。

为了预估 瓦斯使用量 ,这里有个 瓦斯预估API 可以用来预估但是有些附加说明。

瓦斯价格

用户构建并签名交易,因为用户可以指定任何的 交易价格 ,甚至是0价格。然而以太坊客户端从前沿版本起就使用一个默认的瓦斯价格0.05e12 wei,由于矿工对其收入进行了优化,如果大部分的提交都适用0.05e18 wei这个价格,那么就很难让矿工以更低的价格或者0价格进行交易验证。

合约花费举例

我么来看一个仅仅让2个数相加的合约,虚拟机中 ADD 操作花费3个瓦斯。

我们使用默认的价格(2016年1月),近似的花费大约是:

3 * 0.05e12 = 1.5e11 wei

根据1以太币相当于1e18 wei,那么总计的花费大约就是0.00000015 以太币。

这仅仅是一个忽略了其他费用的简单情况,例如省略了相加前传输这两个数值的花费。

Operation Name Gas Cost Remark
step 1 default amount per execution cycle
stop 0 free
suicide 0 free
sha3 20  
sload 20 get from permanent storage
sstore 100 put into permanent storage
balance 20  
create 100 contract creation
call 20 initiating a read-only call
memory 1 every additional word when expanding memory
txdata 5 every byte of data or code for a transaction
transaction 500 base fee transaction
contract creation 53000 changed in homestead from 21000

账户间的交互示例 - 投注合约

前面已经提过了,以太坊有两种类型的账户:

  • 外部账户(EOAs): 一个被私钥控制的账户,如果你拥有一个和账户匹配的私钥,那么就能够通过它发送以太币和消息
  • 合约账户: 合约账户拥有自己的代码,并且由代码控制

默认情况下,以太坊的执行环境是静止的,什么也不会发生并且所有账户都保持不变。然而,一个用户通过他拥有的账户发起交易就触发了一个动作,将以太坊的轮子置于运动状态。如果交易的目标是另一个外部账户,那么交易就转移一些以太币或者什么都不做,如果交易的目标是一个合约,那么合约作为回应,就会自动执行代码。

代码有能力读/写它内部的存储(32位键到32位值的映射数据库)、读取接收到信息的存储、发送信息到其他合约、触发代码执行。一旦执行停止,并且所有由消息触发的子合约也停止,那么执行环境就重新挂起,直到下一次交易被重新唤醒。

合约通常服务于四个目的:

  • 维护一份存储的数据,这份数据对别的合约或是外部世界非常有用,这种合约的一个例子就是模拟货币,对外部世界的例子,它可以作为特定组织的会员纪录。
  • 可以实现一种更为复杂访问策略的外部合约账户,被称为”转发合约(代理合同)”,通常只有在满足特定的条件时才简单地将传入消息重新转发给某些所需的接收者,例如,一个代理合同的消息被发送,必须满足3个私钥中的至少2个被确认后才被发送(也就是多重签名)。更复杂的合约会根据不同的消息而情况有所不同,这种功能最简单的应用就是通过复杂的访问过程来对提现做限制,合约钱包就是一个很好的例子。
  • 多方参与管理的生效合约或关系,例如金融合约、一些特定中间人托管的托管契约、或某些保险。这种合约允许其中一方在任何时间离开也允许其他组织在任何时候加入,举个例子就是合约可以自动支付一笔赏金给一些参与者,只要参与者能提交关于某些数学问题的解决方案或者提供某些计算资源。
  • 给其他合约提供功能,本质上是软件类库

合约通过一种叫做”调用”或”发消息”的行为与其他合约进行互动。”消息”就是包含一些数量的以太币、任意大小的数据、发送人和接收人地址的一个事物,当合约接收到消息时,它会做一些操作并且返回一些数据,这些数据可以被发送者直接使用,在这种方式下,发送一条消息差不多等同于调用一个函数。

因为合约可以扮演如此之多的角色,我们期望合约之间可以互相交互。例如,考虑一种情况,Alice和Bob下注100GavCoin对赌明年的任何时间旧金山的温度都不会超过35ºC,Alice是一个非常有安全意识的人,她的主账户使用了代理合约,需要3个私钥中的至少2个私钥签名才能发送消息,Bob是一个量子加密的偏执狂,他使用的代理合约只发送经过Lamport签名和传统ECDSA的消息(因为是老派,所以他更倾向于基于SHA256的Lamport签名,这是不被以太坊直接支持的)。

打赌的合约本身需要从其他合约里获取到有关旧金山天气的数据,并且当需要发送GavCoin给Alice或Bob时还需要通知GavCoin合约(更准确的说是Alice或Bob的代理合约),我们可以通过如下图来看出各账户之间的关系:

../_images/contract_relationship.png

当Bob最终赢得了赌约,下面的步骤会相继发生:

  1. Bob的账户(EAO)发起交易,发送一条消息到他的代理合约(Forwarding contract)。
  2. Bob的代理合约发送一条消息的哈希和Lamport签名到验证合约,验证合约就是提供Lamport签名验证的库。
  3. Lamport签名验证库看到Bob想要验证基于SHA256的Lamport签名,所以多次调用SHA256库进行签名验证。
  4. 一旦Lamport签名验证库返回结果1,表明签名被验证通过,那么验证合约就会给赌注(Bet)合约发送消息。
  5. 赌注合约通过天气合约来获取旧金山的气温。
  6. 赌注合约通过获取温度后发现温度高于35ºC,所以给GavCoin合约发送消息,将赌金从GavCoin账户发送到Bob的代理合约中。

注意,所有的GavCoin作为一个整体存放在GavCoin合约的数据库中,第六步中的”账户”一词意味着赌注合约中包含一个可以进入赌金地址的私钥和账户余额。当接收到消息时,赌金合约账户的资金会减少同时Bob的代理合约资金会增加。我们可以从下面的图中看到整个流程:

../_images/contract_relationship2.png

离线签署交易

[ 这一部分可能会被添加到常见问题解答(FAQ)中,或者放到其他章节中。]