状态转换

账户

在以太坊系统中,状态是由被称为“ 账户” (每个账户由一个 20 字节的地址)的对象和在两个账户之间转移价值和信息的状态转换构成的。以太坊的账户包含四个部分:

随机数,用于确定每笔交易只能被处理一次的计数器

账户目前的以太币余额

账户的合约代码,如果有的话

账户的存储(默认为空)

以太坊账户分为两种类型:合约账户( Contracts Accounts) 和外部账户(Externally Owned Accounts,或 EOA) 。

合约账户:存储执行的智能合约代码,只能被外部账户来调用激活;

外部账户:以太币拥有者账户,对应到某公钥。账户包括 nonce、balance、storageRoot、codeHash 等字段,由个人来控制。

当合约账户被调用时,存储其中的智能合约会在矿工处的虚拟机中自动执行,并消耗一定的燃料。燃料通过外部账户中的以太币进行购买

消息和交易

以太坊的消息在某种程度上类似于比特币的交易,但是两者之间存在三点重要的 不同。第一,以太坊的消息可以由外部实体或者合约创建,然而比特币的交易只 能从外部创建。第二,以太坊消息可以选择包含数据。第三,如果以太坊消息的 接受者是合约账户,可以选择进行回应,这意味着以太坊消息也包含函数概念。

以太坊中“ 交易” 是指存储从外部账户发出的消息的签名数据包。交易包含消息 的接收者、用于确认发送者的签名、以太币账户余额、要发送的数据和两个被称 为 STARTGAS 和 GASPRICE 的数值。为了防止代码的指数型爆炸和无限循环,每笔 交易需要对执行代码所引发的计算步骤-包括初始消息和所有执行中引发的消息 -做出限制。STARTGAS 就是限制, GASPRICE 是每一计算步骤需要支付矿工的费用。 如果执行交易的过程中, “ 用完了瓦斯” ,所有的状态改变恢复原状态,但是已 经支付的交易费用不可收回了。如果执行交易中止时还剩余瓦斯,那么这些瓦斯 将退还给发送者。创建合约有单独的交易类型和相应的消息类型;合约的地址是 基于账号随机数和交易数据的哈希计算出来的。

消息机制的一个重要后果是以太坊的“ 头等公民” 财产-合约与外部账户拥有同 样权利,包括发送消息和创建其它合约的权利。这使得合约可以同时充当多个不 同的角色,例如,用户可以使去中心化组织(一个合约)的一个成员成为一个中 介账户(另一个合约),为一个偏执的使用定制的基于量子证明的兰波特签名(第 三个合约)的个人和一个自身使用由五个私钥保证安全的账户(第四个合约)的 共同签名实体提供居间服务。以太坊平台的强大之处在于去中心化的组织和代理 合约不需要关心合约的每一参与方是什么类型的账户。

状态转换

以太坊的状态转换函数: APPLY(S,TX) -> S',可以定义如下:

1. 检查交易的格式是否正确(即有正确数值)、签名是否有效和随机数是否与发送者账户的随机数匹配。如否,返回错误。

2. 计算交易费用:fee=STARTGAS * GASPRICE,并从签名中确定发送者的地址。从发送者的账户中减去交易费用和增加发送者的随机数。如果账户余额不足,返回错误。

3. 设定初值 GAS = STARTGAS,并根据交易中的字节数减去一定量的瓦斯值。

4. 从发送者的账户转移价值到接收者账户。如果接收账户还不存在,创建此账户。如果接收账户是一个合约,运行合约的代码,直到代码运行结束或者瓦斯用完。

5. 如果因为发送者账户没有足够的钱或者代码执行耗尽瓦斯导致价值转移失败,恢复原来的状态,但是还需要支付交易费用,交易费用加至矿工账户。

6. 否则,将所有剩余的瓦斯归还给发送者,消耗掉的瓦斯作为交易费用发送给矿工。 例如,假设合约的代码如下:

if !self.storage[calldataload(0)]:self.storage[calldataload(0)] = calldataload(32)

需要注意的是,在现实中合约代码是用底层以太坊虚拟机( EVM)代码写成的。上面的合约是用我们的高级语言 Serpent 语言写成的,它可以被编译成 EVM 代码。假设合约存储器开始时是空的,一个值为 10 以太,瓦斯为 2000,瓦斯价格为 0.001以太并且 64 字节数据,第一个三十二字节的快代表号码 2 和第二个代表词CHARLIE。的交易发送后,状态转换函数的处理过程如下:

1. 检查交易是否有效、格式是否正确。

2. 检查交易发送者至少有 2000*0.001=2 个以太币。如果有,从发送者账户中减去 2 个以太币。

3. 初始设定 gas=2000,假设交易长为 170 字节,每字节的费用是 5,减去 850,所以还剩 1150。

4. 从发送者账户减去 10 个以太币,为合约账户增加 10 个以太币。

5. 运行代码。在这个合约中,运行代码很简单:它检查合约存储器索引为 2处是否已使用,注意到它未被使用,然后将其值置为 CHARLIE。 假设这消耗了 187 单位的瓦斯,于是剩余的瓦斯为 1150 - 187 = 963。

6. 向发送者的账户增加 963*0.001=0.963 个以太币,返回最终状态。 如果没有合约接收交易,那么所有的交易费用就等于 GASPRICE 乘以交易的字节长度,交易的数据就与交易费用无关了。另外,需要注意的是,合约发起的消息可以对它们产生的计算分配瓦斯限额,如果子计算的瓦斯用完了,它只恢复到消息发出时的状态。因此,就像交易一样,合约也可以通过对它产生的子计算设置严格的限制,保护它们的计算资源。

最后更新于