汪晓明对区块链、以太坊的思考

记录创业、生活的所思所感,探讨去中心化思想,推动区块链的发展。

我推出了《VIP区块链技术开发视频》限时9.6折(2/06之前3795元,以后每周一涨价200元,直到恢复原价3950元),欢迎订购!

以太坊连载(21):账户、交易核心概念及投注合约解析

外部账户 vs 合约账户

以太坊中有两种类型的账户

  • 外部账户
  • 合约账户

它们的区别在Serenity版本中可能会消失。

外部账户(EOA)

外部账户

  • 有以太币余额,
  • 可以发送交易(以太币交易或引发合约代码),
  • 由私钥控制,
  • 没有相关代码。

合约账户

合约

  • 有以太币余额,
  • 有相关代码,
  • 代码执行由从其他合约接收的交易或消息(调用)触发,
  • 执行的时候—执行任意复杂的操作(图灵完备的)—操控它自己的永久存储,例如,可以有自己的持久状态—可以调用其他合约

以太坊区块链上的所有行为都由外部账户引发的交易调动。每次合约账户接收到交易时,它的代码都按照输入参数的指示执行,作为交易的一部分发送。合约代码由参与到网络的每个节点上的以太坊虚拟机执行,作为验证新区块的一部分。

这个执行需要是完全确定性的,它唯一的语境是区块链上区块的位置和所有可见的数据。区块链上的区块代表时间单位,区块链本身是时间维度,代表在链上区块指定的离散的时间点上状态的整个历史。

所有的以太币余额和价值都以wei为单位命名:1个以太币是1e18 wei。

注意:以太坊中的“合约”不应该被看做是要“实现”或“遵守”的事物;它更像是在以太坊执行环境中生存的“自治代理”,被消息或交易“戳到”的时候,总会执行特定的代码片段,并且对自己的以太币余额和钥匙/价值商店有直接的控制,以储存永久状态。

什么是交易?

“交易”这个术语用在以太坊中来指代签署的数据包,数据包存储着要从外部账户发送到区块链上另一账户的消息。

交易包括:

  • 消息接收人,
  • 一个签字,用以确认发送方身份,证明通过区块链向接收者发送消息的意图,
  • VALUE域—从发送方向接收方转移的wei的数量,
  • 可选数据域,包括发送到合约的消息,
  • 一个STARTGAS值,代表交易执行允许采取的运算步骤的最大数量,
  • 一个GASPRICE值,代表发送人愿意支付的gas费用。一个gas单位对应着一个原子指令执行,比如运算步骤。

什么是消息?

合约能够向其他合约发送“消息”。消息是虚拟的事物,永远不能序列化,只存在于以太坊执行环境中。他们可以被想象为功能调用。

消息包括:

  • 消息发送方(内含的)
  • 消息接收方
  • VALUE域—和发送到合约地址的消息一起转移的wei数量,
  • 可选数据域,即发送到合约的实际数据
  • 一个STARTGAS值,限制了消息可以触发的代码执行的gas最大值。

本质上来说,一个消息就像一个交易,只不过消息是由合约而不是由外在因素创造的。当正在执行代码的合约执行CALL或DELEGATECALL 操作码时,消息就产生了。和交易一样,消息可能会导致接收方账户运行代码。因此,就像和外在因素建立关系一样,合约也能以同样的方式和其他合约建立关系。

什么是gas?

以太坊虚拟机(EVM)是以太坊区块链上的可执行环境。 每个参与到网络的节点运行EVM,作为区块验证协议的一部分。他们检查列在区块上的、他们验证的交易,运行EVM内部交易触发的代码。每个网络上的完整节点进行同样的运算,储存相同的值。很明显以太坊并不是关于运算效率的优化。它类似的进程是多余的。它的目的是提供一个有效的方式,在系统状态上不需要信任的第三方、准则或暴力垄断就能达成共识。但重要的是他们不是为了优化运算而存在。事实上,合约执行在节点之间被多余地重复,自然使之更昂贵,通常会鼓励人们如果能在链外操作运算,就不在区块链里进行。

运行去中心化应用(dapp)时,它和区块链互动来阅读和修正状态,但是去中心化应用会很典型地只把业务逻辑和对共识至关重要的状态放在区块链上。

当消息或交易触发结果执行时,每个指令在每个网络节点被执行。这有一个代价:每个执行的操作都有特定的成本,以一定量的gas单元表现。

Gas是交易发送方需要为每个以太坊区块链上发生的操作所支付的执行花费。这个名字gas的灵感来自这样一个观点,这笔花费就像加密燃料,驱使智能合约产生。Gas从执行代码的矿工处购买以获得以太币。Gas和以太币被故意分开,因为gas单位与具备自然成本的运算单位一致,而以太币的价格通常会由于市场力量产生波动。二者由自由市场调和:gas价格实际上由矿工决定,矿工可以拒绝以低于最低限度的gas价格进行交易。为了获得gas,你只需要向账户中添加以太币。以太坊客户端会自动为你的以太币购买gas,数量是你指定的交易最大支出。

在合约或交易执行的每个运算步骤,以太坊协议都要收费,以防止以太坊网络上发生蓄意攻击或滥用。每个交易都必须包含一个gas限度和每gas愿意支付的花费。矿工可以选择是否将交易包括在内和收集花费。如果交易产生的、用于运算步骤的gas总量,包括原始消息和可能引发的子消息,少于或等于gas限额,那么交易就会进行。如果gas总量超过gas限额,那么所有的变化都复原,但是交易仍然有效,矿工仍然可以收集花费。未用于交易执行的、所有多余的gas都会以以太币的形式偿还给发送方。不必担心超支,因为只会对你消费的gas收费。这意味着以高于预估的gas限额发送交易也是有效和安全的。

估算交易成本

交易花费的以太币总量基于两个因素:

gasUsed 是交易消费的gas总量

gasPrice 交易中指定的一个gas单元的价格(换算成以太币)

总成本= gasUsed * gasPrice

gasUsed

以太坊虚拟机上的每个操作都会被指派消费的gas数量。gasUsed是所有执行的操作所需的gas总额。有个电子表格可以看到背后的一些统计。

对于估算gasUsed,可以用estimate Gas API但是有些警告说明。

gasPrice

用户建构并签署交易,每个用户可以说明自己想要的gasPrice,可以是零。然而Frontier发布的以太坊客户端默认gasPrice是0.05e12 wei。由于矿工会使收入最优化,如果大部分交易都以0.05e12 wei 的gasPrice提交,就很难说服矿工接受价格更低或为0的交易。

示例交易成本

我们来做一个只添加2个数字的合约。EVM OPCODE ADD消费3 gas。

大概的成本,以默认gas价格计算 (2016年1月)是:

3 * 0.05e12 = 1.5e11 wei

1以太币是1e18 wei,总成本就是0.00000015以太币。

这是个简化的计算,因为忽略了一些成本,比如将2个数字转移给合约的成本,在他们可以被添加之前。

  • question
  • gas费用
  • gas成本计算器
  • 以太坊Gas价格

账户交互示例 — 投注合约

之前提到过,有两种类型的账户:

  • 外部账户 (EOA):由私钥控制的账户,如果你有和外部账户相关的私钥,就能从账户发送以太币和消息。
  • 合约: 有自己代码的账户,受代码控制。

以太坊默认的执行环境是没有生命的,什么都不会发生,每个账户的状态保持相同。但是,每个用户都可以通过从外部账户发送交易来触发行动,启动以太坊。如果交易的目的地是其他外部账户,交易可能会转移一些以太币,否则什么也不会做。但如果目的地是个合约, 反之合约会激活,自动运行代码。

代码有能力读/写自己的内部存储(一个将32字节钥匙映射到32字节价值的数据库),阅读存储的接收消息,给其他合约发送消息,转而触发执行。一旦执行停止,合约发送的消息所触发的所有的子执行都会停止(这些都以决定好的同步的顺序发生,比如,子调用在父调用进一步之前完全完成),执行环境立即再次停止,直到被下一个交易唤醒。

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

  • 保持数据库代表着对其他合约或外部世界有用的东西;一个例子是合约激励货币,另一个例子是合约在特定的组织里记录会员。
  • 作为某种具有更复杂访问政策的外部账户,被称为“前向合约”,典型地只在特定条件下,把进来的消息转发给到指定目的地;例如,前向合约可能会等到指定3个私钥中的2个都确认了特定的消息之后才会进行转发(例如,多重签名)。更复杂的前向合约基于要发送的消息会有不同的条件。最简单的一个功能使用案例就是撤回限制,在一些更复杂的访问政策中难以驾驭。钱包合约就是个很好的例子。
  • 在多个用户之间管理一个正在进行的合约或关系。例子包括金融合约,有特定中介的第三方保管合约,或一些保险。也可以是开放合约,一方对其他方的随时参与保持开放;一个例子是自动给提交数学问题有效解决方案或是证明提供了一些运算资源的人发奖金的合约。
  • 给其他合约提供功能,本质上作为软件库。 合约通过被交替叫做“调用”或“发送消息”的活动进行互动。“消息”是包含一定量以太币,任何大小的数据字节串,发送方和接收方地址的事物。合约接收消息时,可以选择返还一些数据,消息本来的发送方可以立即使用。 这样发送消息就和调用一个功能一样。

因为合约有这样的作用,我们期望合约可以彼此互动。举个例子,设想一个情景,Alice和Bob赌100 Gav币,明年旧金山的温度不会超过35ºC。但是Alice非常有安全意识,她的第一个账户使用的前向合约,只有在3个私钥中的2个都批准的情况下才可以发送消息。Bob偏执于量子加密图形,他使用的前向合约,只传递有Lamport签名和传统ECDSA的消息(但是因为他很老派,所以更偏向于用基于SHA256的Lamport签名版本,以太坊不直接支持)。

投注合约本身需要从一些合约中取得旧金山天气的数据,当它想要实际发送Gav币给Alice或Bob时,也需要和Gav币合约交谈(或者,更准确地说,Alice或Bob的前向合约)。因次我们可以这样表示账户之间的关系:

Bob 想最终决定赌注的时候,就会发生以下的步骤:

  1. 交易被发出,触发消息从Bob的外部账户发送到他的前向合约。
  2. Bob的前向合约给合约发送消息散表和Lamport签名,发挥Lamport签名确认库的作用。
  3. Lamport签名确认库看到Bob想要基于SHA256的Lamport签名,于是给SHA256库多次发调用来确认签名。
  4. Lamport签名确认库一旦回到1,表明签名已确认,他就会给代表赌注的合约发送消息。
  5. 赌注合约检查提供旧金山天气的合约,查看天气如何。
  6. 赌注合约看到对消息的回应显示天气高于35ºC,就会给Gav币合约发送消息,将Gav币从它的账户转移到Bob的前向合约。

注意Gav币在Gav币合约的数据库中作为一个整体“储存”;第6步语境中“账户”的意思只是说在Gav币合约储存中有数据入口,有钥匙可以进入赌注合约的地址和余额值。接收到消息后,Gav币合约值上减少,与Bob前向账户对应的入口值增加。 我们可以在下表看到这些步骤:

离线签署交易

[可以把这一部分加到常见问题,指向turb以太坊向导的ethkey章节]

  • Resilience Raw Transaction Broadcaster

下一篇文章我们将会介绍《以太坊连载(22):深入浅出智能合约》

感谢朝夕团队Azure, Bob参与《Ethereum Homestead Documentation》的翻译和校验。