21个重大版本发布
0.64.1 | 2024年7月22日 |
---|---|
0.63.0 | 2024年4月17日 |
0.62.0 | 2024年2月22日 |
0.55.0 | 2023年12月12日 |
0.39.0 | 2023年3月23日 |
#1132 in 神奇豆子
506 每月下载量
用于 3 个crate(2个直接使用)
1.5MB
30K SLoC
AIR
概述
该crate定义了旨在执行控制Fluence网络执行流的脚本的AIR解释器的核心。从高层次的角度来看,解释器可以被视为一个状态转换函数,它接受两个状态,将它们合并,然后产生一个新的状态。
解释器接口
该解释器只有一个导出函数,名为 invoke
,没有导入函数。导出函数的签名如下
pub fn executed_air(
/// AIR script to execute.
air: String,
/// Previous data that should be equal to the last returned by the interpreter.
prev_data: Vec<u8>,
/// So-called current data that was sent with a particle from the interpreter on some other peer.
data: Vec<u8>,
/// Running parameters that includes different settings.
params: RunParameters,
/// Results of calling services.
call_results: Vec<u8>,
) -> InterpreterOutcome {...}
pub struct InterpreterOutcome {
/// A return code, where 0 means success.
pub ret_code: i32,
/// Contains error message if ret_code != 0.
pub error_message: String,
/// Contains so-called new data that should be preserved in an executor of this interpreter
/// regardless of ret_code value.
pub data: Vec<u8>,
/// Public keys of peers that should receive data.
pub next_peer_pks: Vec<String>,
/// Collected parameters of all met call instructions that could be executed on a current peer.
pub call_requests: Vec<u8>,
}
如前文所述,invoke
接受两个状态(prev_data
和 current_data
),并返回一个新的状态(new_data
)。此外,它接受要执行的AIR脚本、一些运行参数(如 init_peer_id
和 current_peer_id
)以及 call_results
,即调用服务的返回结果。因此,它提供了上文代码片段中描述的 IntepreterOutcome
结构。
主要特性
首先让我们从数据的角度来考虑解释器,因为之前、当前和结果数据是参数和结果中最有趣的部分。假设 X
是数据可能具有的所有可能值的集合,我们将导出函数 executed_air
表示为 f: X * X -> X
。可以看出,在数据方面,f
形成了一个 magma。
更进一步,f
是一个幂等的非交换幺半群,因为
f
是结合的:forall a, b, c from X: f(f(a,b), c) = f(a, f(b,c))
f
有一个中性元素:exists e, forall a from X: f(e, a) = f(a, e) = a
,其中e
是一个带有空轨迹的数据f
是一个非交换函数:exists a, b from X: f(a, b) != f(b, a)
X
可以从构成ExecutedState
枚举(这就是为什么这个幺半群是自由的)的四个基元素中构建f
满足这些幂等性质forall x from X: f(x,x) =x
forall a,b from X: f(a,b) =c, f(c,b) =c, f(c,a) =c
与解释器的交互
解释器允许对等方(节点或浏览器)异步调用服务,通过从执行期间可能调用的每个 call
指令中收集所有参数和其他必要的内容,并在 InterpreterOutcome
中返回它们。然后,主机可以在任何时间执行它们,并通过提供执行服务结果作为 call_results
参数回调解释器。
与解释器交互的方案应如下所示
-
对于从网络接收到的每个新的
current_data
,主机应使用相应的prev_data
和current_data
以及空的call_results
调用解释器。prev_data
这里是解释器返回的最后new_data
-
获得解释器的结果后,在
InterpreterOutcome
中可能会有非空的next_peer_ids
和非空的call_requests
- re
next_peer_pks
:它是对等方的责任决定是否在每次解释器调用后发送粒子,或者在整个粒子执行后(即完成所有call_requests
后)发送粒子。 - re
call_requests
:call_requests
是一个HashMap<u32, CallRequestParams>
,主机需要保持u32
和调用CallRequestParams
之间的对应关系,因为它们将在第3步返回结果时使用。
- re
-
如果第2步中
call_requests
不为空,对等方必须再次调用解释器,并带有提供的调用结果(HashMap<u32, CallServiceResult>
),遵循以下规则:
- 这里不应该提供current_data(实际上,由于
f
是幂等的,它可以提供,但这是不必要的,并且会稍微减慢解释器执行的速度)。 - 在处理下一个粒子之前,没有必要提供
call_results
,实际上,对等方可以在任何时候提供它。 - 在每个执行步骤之后,对等方必须保留
new_data
。这是非常重要的,因为f
不是可交换的,并且解释器在data
中保存额外的信息,期望在下一次启动时看到结果数据作为prev_data
返回。
然后应该从第2点开始重复此流程。
- 如果
call_requests
为空,整个执行完成,必须保留new_data
,并且必须像往常一样向所有new_peer_pks
发送粒子。
交互示例可以在测试中找到。
依赖关系
~14–27MB
~421K SLoC