但是这并不意味着一个合约不能和另一个合约对话。一个存在于以太坊状态全局范围内的合约是可以和同一范围内的其他合约对话。这种方式是通过“消息”或“内部交易”的方式实现的的, 你可以把这类实现方式类比为交易,但是区别在于发起方不是外部账户,而是合约账户。不像交易行为,它们没有被序列化且只是以虚拟对象的方式存在于以太坊的执行环境中。
如果一个合约向另一个合约发送了内部交易,此时作为接收方的合约中的代码会被执行。
一个内部交易或者消息并不需要gasl limit,因为gas limit是由原始交易的外部账户决定的。外部账户设置的gas limit需要涵盖这个交易行为衍生的子交易行为,例如合约和合约之间的消息。如果在这个交易的执行链条过程中,有一个特定的消息由于gas耗尽而执行失败,那么所有已经被执行的交易都将回滚回初始状态。唯一的例外情况是如果您在合约中使用原始 CALL 操作码(除非不得不这么做,否则不建议这样做)。在这种情况下,对另一个合约的 CALL 可能会失败,但如果父合约不检查并处理该错误,则执行会正常进行,并且结果可能会显示成功。
区块
所有的交易都会被分组成为区块,一系列的链接在一起的区块组成了区块链。
在以太坊中,一个区块包含:
- 区块头
- 这个区块的交易集数据
- 当前区块的一组叔块(ommer)的区块头
什么是叔块(ommer)?
叔块(ommer)也是一个区块,它的父级是当前块的父级的父级(这就是为什么它被称作叔块的原因,有时也会被称为uncle)。让我们简单了解一下叔块的用途,为什么一个区块块需要包含叔块的块头?
由于以太坊的构建方式,出块时间(约 15 秒)相比于比特币等其他区块链(约 10 分钟)要短得多,这可以实现更快的事务处理。 但是较短的出块时间也是有缺点的,缺点之一是同一时间可能产生多个区块,这些由于竞争时产生的区块也被称为“孤块”(即块不会进入主链的区块)。
叔块的设计目的是用于同时奖励开采出有效孤块的矿工,因为这些矿工也付出了计算工作。
但是叔块收到的奖励是要比打包进入主链的区块要低的。尽管如此,因为有奖励的存在,矿工仍然有动力将这些孤块打包在内并获得奖励。
区块头
让我们回到区块本身,上文中有提到每个区块都有个区块头,那么区块头具体是什么什么呢?
区块头由一下字段组成:
- parentHash:父块的hash值(这也是为什么区块组成了区块链的原因所在)
- ommersHash:多个叔块的hash
- beneficiary:生成出这个区块的矿工地址获得的奖励
- stateRoot: Merkle trie 的根哈希,存储系统的整个状态
- transactionsRoot:此区块中列出的所有交易接收方的 trie 根节点的哈希
- logsBloom:包含日志数据的布隆过滤器(一种数据结构)
- diffculty:这个区块开采的难度
- number:当前块的计数(创世块的块号为零;每个后续块的块号增加 1)
- gasLimit:区块中的交易设置的总 gas limit
- gasUsed:这个区块中的所有交易消耗的总gas
- timestamp:矿工开采该区块的时间戳
- extraData:矿工附加在区块中的任何额外数据
- mixHash:一个hash值,和nonce字段结合时,可以用于证明该区块是由足够计算量产生的
- nonce:一个hash值,和mixHash字段结合时,可以用于证明该区块是由足够计算量产生的
每个区块头包含我们前文中提到的梅克尔帕特里夏树的三种trie结构:
- state(stateRoot)
- transactions(transactionsRoot)
- receipts(receiptsRoot)
上面的几个术语我们会在接下来的篇幅中细讲。
日志
以太坊设计了日志用于追中不同的交易和消息。一个合约如果想要记录日志,可以通过定义一个事件来实现。
一个日志包括:
- 记录日志的账户地址
- 不同交易携带的topics
- 与这个事件相关的任何数据
日志存储在布隆过滤器中,这个数据结构能够高效的存储大量数据。
交易收据
区块头中存储的日志来自交易收据中包含的日志信息。就像你去一个超市购买商品后收到一个收据一样,以太坊也会为每笔交易生成一个收据。收据中都包含这笔交易的信息,包括:
- 区块数(the block number)
- 区块hash(block hash)
- 交易hash(transaction hash)
- 交易使用的gas
- 在当前交易执行后这个区块使用的gas累计值
- 执行当前交易的日志生成量
- 等等..
区块难度
区块的“难度”用来衡量挖出一个区块平均所需要的运算次数,反映了在一定难度下用多长时间才能挖到一定数量的区块。 创世区块的难度为131,072,之后使用特殊的公式计算每个区块的难度。 如果某个区块的验证速度比前一个区块快,则以太坊协议会增加该区块的难度。
难度将会影响nonce,因为这个hash必须通过矿工通过挖矿计算而来,使用工作量证明算法。
区块难度和nonce之间关系可以用下面的数学公式表达(表示区块难度):
找到满足难度阈值的随机数的唯一方法是使用工作量证明算法来枚举所有可能性。 找到解决方案的预期时间与难度成正比 —— 难度越高,找到 nonce 就越难,因此验证区块就越难,这反过来又增加了验证新区块所需的时间。因此,通过调整区块的难度,就可以调整验证区块所需的时间。
另一方面,如果验证时间变慢,则协议会降低难度。 这样,验证时间会自我调整以保持恒定速率 — 平均每 15 秒一个块。
交易执行
我们来到了以太坊协议最复杂的一部分:交易执行。当你在以太坊网络上发送一笔交易,以太坊经历了怎样的一个状态转移?