虚拟机是Filecoin系统中至关重要的部分,Filecoin中的大部分功能,如转账,维护算力表,记录网络信息等都要通过虚拟机来实现。虚拟机是Filecoin得以运行的核心,不理解虚拟机也就不可能理解Filecoin系统的工作方式。本文中,星际联盟将带领大家对Filecoin中虚拟机系统一探究竟。
Filecoin地址是用来指代Filecoin state中的actor的标识。所有的actor,包括矿工actor,存储市场actor,账户actor都拥有一个地址。地址编码了以下信息:actor所属网络,地址编码的特定类型,地址payload和一个校验和。这种格式的目标是提供一种强健的,既容易使用又能抗错误的地址格式。
可识别性:地址必须能容易地识别为Filecoin地址。
可靠性:地址必须提供一个错误检测机制,因为它们可能会在网络之外传输。
可升级:地址必须是版本化的,以允许采用新的地址格式。
精简:基于以上的约束,地址必须尽可能地短。
Filecoin地址有两种方式表现形式。出现在链上的地址永远都是原始字节串
的形式。地址也可以编码成字符串
,这种编码包括一个校验和和一个网络前缀。编码为字符串的地址永远不会出现在链上,这种格式仅用于在人类之间分享。
使用字节串
表示的地址包含以下部分:
一个标识地址类型和版本的协议指示器字节。
payload用来唯一地标识一个对应协议的actor
协议 | payload |
---|---|
1 字节 | n 字节 |
编码为字符串的Filecoin地址包含以下部分:
一个标识地址所属网络的“网络前缀”字符。
一个标识地址类型和版本的协议指示器字节。
用来唯一地标识一个对应协议的actor的payload。
用以确认地址有效性的校验和。
网络 | 协议 | payload | 校验和 |
---|---|---|---|
'f' 或 't' | 1 字节 | n 字节 | 4 字节 |
地址编码为字符串时会在前面添加网络前缀。网络前缀表明地址属于哪个网络,可取值t
和f
之一,f
代表mainnet而t
代表测试网。值得注意的是网络前缀永远不会出现在链上,并只在编码地址为人类可读取格式时用到。
协议指示器字节就一种方法应该如何诠释地址的payload字段信息进行了描述。对协议指定的任何算法和数据类型的偏差都必须用一个新的协议号赋值。在这种方式下,协议本身就是版本号。
0 : ID
1 : SECP256K1 Public Key
2 : Actor
3 : BLS Public Key
0号协议只是简单的ID。即便没有公钥,所有的actor也都有一个数字ID。ID类型的地址的payload是base10
编码的。ID不会被取哈希,也没有校验和。
字节串形式
协议编号 | payload |
---|---|
0 | leb128-varint |
字符串形式
网络 | 协议 | payload |
---|---|---|
'f' or 't' | '0' | leb128-varint, base10[...............] |
1号协议地址代表了secp256k1公共加密密钥。payload字段包含公钥的 Blake2b 160 哈希值。
字节串形式
protocol | payload |
---|---|
1 | blake2b-160(PubKey) |
字符串形式
network | protocol | payload | checksum |
---|---|---|---|
'f' 或 't' | '1' | blake2b-160(PubKey) ,base32[................................] | 4 bytes |
2号协议地址代表一个actor。payload字段包含一些意义重大数据的SHA256哈希值,数据来自创建actor的结果。
字节串形式
protocol | payload |
---|---|
2 | SHA256(Random) |
字符串形式
网络 | 协议 | payload | 校验和 |
---|---|---|---|
'f' 或 't' | '2' | SHA256(Random) ,base32[..................................] | 4 bytes |
3号协议地址代表BLS公共加密密钥,payload字段包含BLS的公钥。
字节串形式
协议 | payload |
---|---|
3 | 48 byte BLS PubKey |
字符串形式
网络 | 协议 | payload | 校验和 |
---|---|---|---|
'f' 或 't' | '3' | 48 byte BLS PubKey,base32[................................] | 4 bytes |
payload代表着协议指定的数据。除“ID地址协议”外的所有payload都是base32编码的,序列化到人类可读的格式时使用小写字母表。
Filecoin校验和在地址和payload之上使用blake2b-4计算。校验和基于base32编码,并只在编码为字符串时添加进去。遵循ID地址协议的地址没有校验和。
在创建和验证校验和之外,Filecoin中的所有实现也必须有用于创建、编码、和解码地址的方法。
方法New
方法New返回一个包裹着相应payload的地址。对应未知的协议,
New
会失败。
方法Encode
编码Filecoin地址的软件必须:1.产出一个编码到已知网络的地址 2.产出一个编码到已知协议的地址 3.若果适用的话,产出一个带有合法校验和的地址
Encode方法将地址编码为一个字符串,在其前添加网络前缀,计算校验和并编码payload和校验和到base32。
Decode
解码Filecoin地址的软件必须:1.验证网络是否是已知网络2.验证协议是否一个已知协议号 3.验证校验和是否合法
Decode移除网络前缀,验证地址是否为已知协议,解码payload和校验和,并检验校验和,从而从字符串中解码出地址。
Checksum
通过取地址协议和payload的blake2b-4哈希值,Checksum产出一个字节数组。
ValidateChecksum
若数据的校验和与预期的校验和匹配,ValidateChecksum就返回true。
状态树是在Filecoin区块链上应用操作产生的输出。
Indices是从状态树计算得来的一组全球经济指标,一些用以基于用户状态/动作计算策略输出的纯函数。Indices用来计算和实现系统的经济机制和策略。在Indices中不存在持久化状态,Indices也不能引入任何状态修改。注意,indices应该存在何处是一个设计上的决定。将indices打碎成多个文件,或在决定了经济机制后把indices放置到不同的actor中都是有可能的。Indices是用于所有潜在宏观经济指标的临时保留文件,而这些指标正是系统需要知晓的。
actor
VM处理需要两个系统actor:
还有两个VM层的actor:
AccountActor :用于用户账户(非单例)。
RewardActor:用于奖励区块和授予代币(单例)。
InitActor
CronActor
AccountActor
RewardActor是保存未铸造(unminted)和未发行(unvested)的Filecoin代币的地方。RewardMap是从owner地址到Reward结构的映射,RewardActor使用在RewardMap中的投资者账户,代币和发行计划(vesting schedule)进行初始化。
Reward结构包含:
StartEpoch,用以追踪Reward于何时创建
Value,代表奖励代币的总量
ReleaseRate,代表每epoch内代币释放的线性速度,单位为FIL
WithdrawAmount,记录当前从一个Reward结构已提取了多少代币
Owner地址,可以调用WithdrawReward函数,后将提取“投资者”地址拥有的,来自RewardMap的所有“已发行”(vested)代币。当Reward结构中WithdrawAmount等于Value时,Reward结构就会从RewardMap中移除。
RewardMap也用于“铸造区块奖励”,以保留向协议引入“区块奖励发行”的灵活性。MintReward函数创建一个新的Reward结构并将其添加到RewardMap。
消息是两个actor间的沟通单位,也是状态改变的原始诱因。消息结合了:
从发送者转移到接收者的代币数量
接收者上被调用的带有参数的方法(可选) 在处理一条接收到的消息的过程中,actor代码有可能向其它actor发送额外的消息。消息会被同步执行:actor在恢复控制之前会等待发送的消息执行完毕。
消息处理的过程消耗以gas计价的“计算单位”和“存储”。消息的gas limit提供了其计算量的上限。消息的发送者以他们所定的单价,支付消息和所有的内嵌消息处理中消耗的gas。“区块出产者”选择将哪些消息包含到区块里,并依据每条消息的单价和计算量得到奖励,市场也得以形成。
一定不要传输,也不能在消息池中维护,或者包含进区块内“语义不合法”的消息。一条语义合法的签名消息
需要:
有一个格式良好的,非空的To
地址
有一个格式良好的,非空的From
地址
有一个非负的CallSeqNum
其Value
不比0小也不能比代币总供应量2e9 * 1e18
大
其MethodNum
非负
除非MethodNum
为0,否则其 Params
不为空
其GasPrice
非负
其GasLimit
至少要等于消耗的gas量,消耗量与消息序列化后的字节大小有关
其GasLimit
不能大于网络中的gas limit参数
消息在被包含进区块前单独传输时,都会被打包为一个签名消息
,而不论其使用了什么签名方案。一条合法的签名消息序列化后的总大小不能大于message.MessageMaxSize
。
“语义验证”需要消息本身之外的信息。 一条语义合法的签名消息
必须携带一个签名,签名验证消息已使用其 From
地址标识的“账户actor”的公钥进行对payload
进行了签名。
注意:
若
From
地址是一个ID-address,就必须在区块所标识的父state
中的发送方“账户actor”里查找公钥。发送actor必须存在于包含本消息的区块所标识的父
state
中。这意味者,对于一个区块说,既包含某条创建“新账户actor”的消息,又包含本actor发出的消息的行为是非法的。此actor发出的首条消息必须等待一个紧随其后的epoch。消息池可能会不包含来自还未出现在链状态中的actor的消息。
对于一条引发区块包含自身的消息的合法性,不再做进行一步的验证。每条语义合法且签名正确的消息都可以包含进区块内,并在执行时产出一个receipt。但是,消息可能不能成功完成执行过程,这种情况就不会对期望的状态改变带来影响。
“不进行进一步的消息语义验证”证策的原因是,在消息做为tipset的一部分执行之前,不可能知道消息将会被应用到的状态。“区块出产者”不知道是否另外的区块将会在tipset中抢先执行此消息,从而修改将消息应用到的,来自父state
的的状态。
VM解释器在tipset
的父state
之上,安排来自一个此tipset
的消息的执行,产生一个新状态和一系列消息receipt
。新状态和receipt集合的cid会被包含在接下来的epoch
的区块里,这些区块必须就这些cid达成一致,以形成一个新的tipset。
每一个状态改变都由消息执行而驱动。要产生一个新state
,一个tipset内的所有区块里的消息必须按顺序执行。来自tipset内第一个区块的所有消息都会先于来自第二个和之后区块内的消息执行。对每个区块而言,BLS聚合的消息会先执行,而后再是SECP签名消息。
除了被显式地包含进每个区块的消息之外,每个epoch内有少量的状态改变是由隐式消息造成的。隐式的消息不会在两节点间传递,而由解释器在执行时候构建。
对于tipset内的每个区块,隐式消息:
调用“区块出产者”的矿工actor来投递“选举PoSt”,作为区块内的第一条消息。
调用reward actor向矿工的owner账户支付区块奖励,作为区块内的最后一条消息。
对于每个tipset,隐式消息:
调用cron actor处理自动化检测和支付,此为tipset内的最后一条消息。
所有的隐式消息都使用一个From
地址做为特殊的(distinguished)的系统账户actor进行构建。它们设定gas单价为0,但是必会被包含进计算里。它们必须成功执行(退出码为0)才能计算出新的state
。隐式消息的receipt不会包含进receipt列表,只有隐式消息会有隐式的receipt。
多数情况下,消息的发送者向产出包含其消息的区块的矿工支付执行消息的gas费用。为每条消息执行而支付的gas会在消息执行完毕后立即支付给矿工的owner
账户。区块奖励或挣取的gas费用都可能会被立即花掉。
既然在同一个epoch内有不同的矿工产出区块,那在单个tipset内就可能包含进行相同的消息(以相同cid标识)。这种情况出现时,本条消息只会在按照tipset的规范顺序第一次出现时执行。随后的消息实例则会被忽略掉且不会产生任何状态修改,也不会生成receipt或向区块产出者支付gas。
因此总结来说tipset内消息的执行顺序如下:
为首个区块支付奖励
为首个区块进行“选举PoSt”
为首个区块执行消息,BLS签名先于SECP签名
为第二个区块支付奖励
为第二个区进行“选举PoSt”
为第二个区块执行消息,BLS签名先于SECP签名,跳过已出现的消息
[处理接下来的区块]
时钟滴答
一个合法区块内的每一条消息都可以被执行并产生一条receipt(注意区块合法就表明了所有消息都是语义合法的 - 参考Message Syntax - 且被正确签名。然而,依赖于消息被应用到的状态,消息执行可能也可能不成功。如果消息执行失败,相应的的receipt则会携带一个非0的退出码。
如果消息执行失败可以合理归因于矿工包含了某条永远不可能在其父state
里执行成功的消息,或因为发送者的资金不足以覆盖最大的消息耗费,矿工就得燃烧gas经费来支付罚金,而不是发送者向区块矿工支付费用。
消息失败产生的唯一状态变化是以下两者之一:
递增发送actor的CallSeqNum
,和从发送者向包含本条消息的区块的矿工的owner
支付的gas费用
一次与失败消息的gas费用相等的惩罚,由矿工承担,发送方的CallSeqNum
不发生变化
消息执行在以下情况下会立即失败:
From actor不存在于state
中(矿工被处罚)
From acto不是一个账户actor(矿工被处罚)
消息的CallSeqNum
与Fromactor的CallSeqNum
不匹配(矿工被处罚)
From actor没有充足的余额来覆盖消息Value
之和再加上最大的gas消耗GasLimit * GasPrice
(矿工被处罚)
To actor不存在于state
中且To地址不是一个公钥类型的地址
To actor存在(或被隐式地创建为一个账户actor)但没有对应于非0的MethodNum的
方法
反序列化的参数不是一个其长度匹配To actor的MethodNum
方法的参数数量的数组
调用的方法消耗的比GasLimit
所允许的多
调用的方法以非0退出码返回(通过Runtime.Abort()),或接收者发送的任何内部消息因为以上任何一个原因而失败
注意如果To
actor在state
中不存在,且地址是个非法的H(pubkey)
地址,其将会创建为一个账户actor。
[ID:ipfsunion6]
扫描二维码
关注官方公众号