VM解释器从tipset(基于上tipset的父状态)上编排消息的执行,从而生成一个新的状态和消息收据的序列.这个新状态和收据集合的CID被包含在后续epoch的块中,这些epoch必须就这些CID达成一致才能形成新的tipset。
每个状态的更改都有一条消息来驱动,tipset中所有块中得消息都必须全部执行才能产生下一个状态。来自第一个块得所有消息均在tipset中得第二个和后续块得消息之前执行。对于每个块,首先执行BLS聚合得消息,然后执行SECP签名的消息。
隐式消息
除了显式包含在每个块中的消息外,隐含消息还会在每个epoch进行一些状态更改,隐式消息不在节点之间传输,而是由解释器在评估时构成。
对于tipset中的每个块,都有一个条隐式消息:
- 选举矿工角色成为区块生产者,作为区块中的第一条消息。
- 调用奖励角色将区块奖励支付给矿工 的 owner账户,作为区块中的最后一条消息。
对于每个tipset, 都已以下一个隐式消息:
- 调用 cron角色来处理自动检查和付款,作为tipset的最后一条消息
所有隐式消息的构造都使用一个区别于系统账户的角色的From
地址。他们的Gas费用位0,但是他们必须被计算。为了计算新状态,他们必须成功(推出代码为0)。隐式消息的收据不包括在收据列表中,只用显式的消息才有明确的收据。
Gas 支付
在大多数情况下,消息的发送者向产生包含该消息的块的矿工支付执行该消息所需的gas费。执行该消息后,每次执行该消息所产生的gas费将立即支付给矿工的owner账户。获得块奖励和gas费用没有任何阻碍,他们都是立即支付的。
重复消息
由于不同矿工在同一时期产生区块,因此,单个tips中的多个区块可能包含相同的消息(由CID标识)。发生这种情况时,在tipset的规范顺序中第一次遇到它是才会处理该消息,消息的后续实例将会被忽略,不会导致任何状态变化,也不会产生收据和向区块生产者支付费用
因此,tipset的执行顺序是:
- 支付第一块的奖励
- 选举第一个区块的生产者
- 处理第一个区块的消息(首先处理BLS,其次处理SECP)
- 支付第二个区块的奖励
- 选举第二个区块的生产者
- 处理第二个块的消息(首先处理BLS,其次处理SECP,跳过任何已经遇到的消息)
- [...随后的块处理同上....]
- cron角色标记
正确和失败的消息
有效块中的每一条消息都可以被处理并产生收据(注意,块的有效性表示所有消息在语法上均有效且正确签名)。但是,执行的成功与否取决于消息的执行状态。如果消息执行失败,则相应的收据将携带非零的退出代码。
如过一条消息由于矿工打包在父状态就不可能成功消息的原因而失败,或者由于发送者缺乏资金来支付最大消息成本,则矿工将通过消耗Gas的方式支付罚款(而不是发送者向矿工支付费用)。
消息失败导致的唯一状态变更是:
- 增加发送者的
CallSeqNum
,并且发送者向创造包含该消息的块的矿工的onwer支付Gas费,或者 - 矿工通过支付等同于失败消息的Gas费的罚款(发送者的
CallSeqNum
不改变)
如果发生以下情况,消息将会失败:
-
From
的角色在状态中不存在(矿工罚款) -
From
的角色不是账号角色(矿工罚款) - 消息的
CallSeqNum
和From
的角色的CallSeqNum
不匹配(矿工罚款) -
From
的角色没有足够的资金来支付整个消息消耗的Gas成本GasLimit * GasPrice
(矿工罚款) -
To
的角色不存在并且To
的地址不是一个公钥类型的地址 -
To
的角色存在(或者作为隐式账户创建)但是没有对应于非零的方法MethodNum
。 - 反序列化的
Params
长度不匹配To
的MethodNum
方法的长度的数组 - 反序列化的
Params
对于To
角色的MethodNum
方法执行的类型无效 - 所调用的方法消耗的Gas多于
GasLimit
允许的量。 - 调用的方法以非零代码(通过
Runtime.Abort()
)推出 - 由于以上任何原因,接收方发送的任何内部消息都会失败。
如果To
账户在状态中不存在,并且该地址是有效的H(pubkey)地址,则会为其创建账户角色
翻译自https://spec.filecoin.io/#section-systems.filecoin_vm.interpreter