发布201次
新 0.4.0-dev.14 | 2024年8月23日 |
---|---|
0.4.0-dev.11 | 2024年7月17日 |
0.3.0-beta-dev.36 | 2024年3月27日 |
0.3.0-beta-dev.24 | 2023年12月22日 |
0.0.39-alpha4 | 2019年11月25日 |
#221 in 魔法豆
4,094 每月下载量
在 39 个crate(25 个直接) 中使用
695KB
11K SLoC
hdk
Holochain 开发工具包 (HDK) 为编写 Holochain 应用程序提供高和低级别函数。
Holochain 是作为一个客户端-服务器架构构建的。Holochain 的运行时 Conductor 作为服务器。客户端可以通过其 Conductor API 查询来管理 hApps 并向 hApp 函数发送请求。有关 Holochain 架构的更多信息,请参阅 Holochain 架构。
hApp 的功能组织成可重用的组件。在 Holochain 术语中,这些组件被称为 "zomes"。一个或多个 zomes 编译成 WebAssembly (WASM) 二进制文件,并打包成一个称为 DNA 的文件。一个应用程序的所有 DNA 都打包到一个 hApp 中。简而言之,结构是 hApp -> DNA -> zome -> function。
可以使用 HDK 开发 hApps。有关 hApp 开发的信息,请参阅 Holochain 快速入门指南。
示例 zomes 🍭
在多个方面,例如hApp开发,存在大量示例/测试WASM,您可以在GitHub上浏览这些。
每个示例WASM都是特定HDK功能的最小演示,例如生成随机数据、创建条目或定义验证回调。一些示例可能非常牵强,但都不是作为生产级别的hApp示例,但确实突出了关键功能。
组件被分为数据模型和领域逻辑
hApp需要产生和验证数据具有确定性。每个hApp都有一个数据模型和领域逻辑部分。在Holochain中,数据模型在完整性组件中定义,领域逻辑在协调器组件中编写。
完整性组件 📐
完整性组件通过定义一系列条目和链接类型以及提供验证回调函数来描述hApp的领域模型,该回调函数检查操纵这些类型数据的任何操作的完整性。此外,可以实现创世自我检查回调,以基本验证数据,使代理在尝试加入网络之前能够加入网络。
WASM工作区包含类似这样的完整性组件示例: https://github.com/holochain/holochain/blob/develop/crates/test_utils/wasm/wasm_workspace/integrity_zome/src/lib.rs
有关完整性层的更多信息,请参阅HDI组件。
协调器组件 🐜
协调器组件是DNA中完整性组件的对立面。它们包含数据读取和写入的领域逻辑。在完整性组件中定义和验证数据,而在协调器组件中实现操纵数据的功能。
可以在Holochain存储库的wasm工作区中找到示例协调器组件: https://github.com/holochain/holochain/blob/develop/crates/test_utils/wasm/wasm_workspace/coordinator_zome/src/lib.rs。
HDK结构 🧱
HDK实现了多个关键功能
- 用于标准化、模拟和单元测试支持的Base HDKT trait的基本模块:[
hdk
]模块 - 能力和函数级访问控制:
capability
模块 - [Holochain Deterministic Integrity (HDI)]
- 源链和DHT的应用数据条目定义:
entry
模块和[entry_types]回调 - 在DHT上引用/链接条目,以形成一个图形结构:
link
模块 - 使用链接和条目定义树状结构,以实现可发现性和可扩展性:
hash_path
模块 - 对上述内容执行创建、读取、更新、删除(CRUD)操作
- 与Libsodium兼容的对称/秘密(secretbox)和非对称/密钥对(box)加密:
x_salsa20_poly1305
模块 - 数据的Ed25519签名和验证:
ed25519
模块 - 暴露有关当前执行上下文的信息,例如组件名称:
info
模块 - 主机提供的其他实用函数,如生成随机数和时间戳,这些在WASM中是不可能实现的:实用模块
- 将函数和回调暴露给外部进程和主机:宏
hdk_extern!
和map_extern!
- 与Rust tracing 库集成
- 提供常用类型和函数的
prelude
以便使用
通常,这些功能逻辑上组织成模块,但抽象层之间有一些灵活性。
HDK基于回调👂
在WASM内执行逻辑的唯一方式是让主持人(主机)调用zome(访客)标记为extern
的函数。
注意:从WASM中hApp开发的角度来看,“访客”是WASM,“主机”是运行的Holochain主持人。这里的“主机”不是“主机操作系统”。
同样,访客除了处理数据和计算之外,唯一能做的事情就是调用主机在运行时提供的函数。
主机函数都由Holochain主持人定义并由HDK为您实现,但访客函数需要由您的应用程序定义。
任何不使用HDK的WASM都需要定义主机函数的占位符和接口。
所有主机函数都可以直接调用:
use crate::prelude::*;
let _output: HDK.with(|h| h.borrow().host_fn(input));
并且Holochain定义的每个主机函数在HDK中都有一个便利的包装器,为您处理类型转换。
导体和WASM二进制之间的底层通信,如类型和数据的序列化,由HDK抽象化。使用HDK,hApp开发者可以专注于他们的应用程序逻辑。了解Holochain中的WASM更多信息。
外部回调= Zome函数
要扩展Rust函数以便它可以由主机调用,请添加hdk_extern!
属性。
- 函数可以没有参数或有一个参数,如果提供,必须实现
serde::Serialize + std::fmt::Debug
。 - 函数必须返回一个
ExternResult
,其中成功值实现serde::Serialize + std::fmt::Debug
。 - 函数必须在所有externs中具有唯一名称,因为它们在WASM中共享全局命名空间
- 函数内部的所有内容都是Rust-as-usual,包括
?
与失败作为WasmError
的ExternResult
交互 - 使用
wasm_error!
宏以及WasmErrorInner::Guest
变体来处理主机或外部进程需要了解的失败条件 - 外部的函数可以像正常一样在同一个WASM内的其他函数中调用
例如
use crate::prelude::*;
// This function can be called by any external process that can provide and accept messagepack serialized u32 integers.
#[hdk_extern]
pub fn increment(u: u32) -> ExternResult<u32> {
Ok(u + 1)
}
// Extern functions can be called as normal by other rust code.
assert_eq!(2, increment(1));
大多数externs都可用于外部进程,并且必须显式调用,例如通过通过websockets的RPC。外部进程只需要确保正确处理输入和输出数据作为messagepack。
内部回调
一些外部函数作为回调,在Holochain内部系统工作流程的关键点上由宿主调用。这些回调允许访客定义宿主在这些决策点上的行为。它们在类似于上述extern回调的zome中定义,但具有以下保留名称。
回调可以通过名称简单地调用,并且它们是“稀疏”的,因为它们从最具体的名称到最不具体的名称逐个匹配。例如,以下回调 validate_{{ create|update|delete }}_}{{ agent|entry }}
都会匹配,并在验证期间运行。所有具有多个选项的功能组件都是可选的,例如,validate
将执行,同样 validate_create
也会执行。
Holochain将以上下文敏感的方式合并同一回调的多个回调结果。例如,如果任何初始化回调失败,宿主将考虑初始化失败。
回调如下(请参阅上面的示例)
fn entry_defs() -> ExternResult<EntryDefsCallbackResult>
:- 通常由HDK中的宏自动实现,因此不需要手动编写extern。
EntryDefs
是一个定义此应用程序所有条目的向量。- DNA中的所有zome将同时为宿主定义它们的所有条目。
- 所有条目定义都组合成一个按zome排序的单独列表,并暴露给DNA生成等工具。
- 条目定义通过
u8
数值位置在外部和DHT操作中引用,并通过id/名称(例如稀疏回调中的“post”)引用。
fn genesis_self_check(_: GenesisSelfCheckData) -> ExternResult<ValidateCallbackResult>
:- 允许每个代理在尝试加入网络之前验证自身。
- 接收包括DNA信息、候选源链的代理密钥和膜证明的
GenesisSelfCheckData
。 - 在代理在网络上存在之前运行,因此无法使用网络,通常只能访问确定性HDK函数。
fn init() -> ExternResult<InitCallbackResult>
:- 允许访客通过
InitCallbackResult
来通过/失败/重试初始化。 - 延迟执行 - 只有当DNA的任何zome首次被调用时才运行。
- DNA中的所有zome同时初始化。
- 任何zome的失败都会使DNA初始化失败,任何zome的重试(缺少依赖项)都会使DNA重试。
- 失败会覆盖重试。
- 有关如何在
init
中设置能力的说明,请参阅create_cap_grant
。
- 允许访客通过
fn migrate_agent_{{open|close}} -> ExternResult<MigrateAgentCallbackResult>
:- 允许访客通过/失败从另一个DNA迁移到/从另一个DNA迁移的尝试。
- 当代理从一个旧源链启动新源链时运行。
- 当代理废弃旧源链以支持新源链时运行。
- DNA中的所有zome同时迁移。
- 任何失败都会使迁移失败。
fn post_commit(actions: Vec<SignedActionHashed>)
:- 在触发提交的WASM调用之后执行,因此不受原始原子事务的约束。
- 输入是所有已提交的操作哈希。
- 调用触发提交的zome。
fn validate(op:Op) -> ExternResult<ValidateCallbackResult>
:- 允许访客通过/失败/重试任何操作。
- 只调用触发操作的zome。
- 失败会覆盖重试。
HDK有层次结构 🧅
HDK的设计采用了分层结构,遵循80/20原则。代码并非严格按照这种方式组织,但随着您编写自己的hApps,您会逐渐适应。
大体来说,80%的应用可以通过使用HDK的20%功能和代码达到生产就绪状态。这些是“高级”功能,例如crate::entry::create_entry
以及宏如hdk_extern!
。每个Holochain功能都通过带类型和文档的包装器提供,并且有一系列宏用于暴露功能和定义条目。
在需要深入研究的情况下,还有另一层遵循其自身的80/20原则。80%的时间,您可以通过使用host_call
或编写自己的条目定义逻辑来从上层填补空缺。例如,您可能需要实现通用的类型接口或条目的结构体和枚举组合,而这些是默认无法处理的。
如果需要更深入地研究,下一层是holochain_wasmer_guest
、holochain_zome_types
和holochain_serialization
库。在这里,您可以自定义外部函数的调用方式以及如何序列化和存储数据。理想情况下,您不需要走这么远,但在某些罕见情况下可能需要这样做。例如,您可能需要接受外部源的数据(例如json),或者您可能希望自定义跟踪工具和错误处理。
最低层是定义主机和客户端之间如何通信的结构和序列化。您不能更改这一点,但可以通过引用Rust zome类型和外部函数签名来在您选择的语言(例如Haskell)中重新实现它。
HDK在源链上是原子的⚛
在单个extern/callback调用中,对源链的所有写入都是原子的。
这意味着所有数据将一起验证和写入,或者什么都不写。
对于其他副作用,没有这样的保证。值得注意的是,我们无法控制网络或Holochain数据库之外的内容。
远程调用在接收方的设备上将是原子的,但可能在本地代理出错并回滚链时成功完成。这意味着您不应该依赖在代理之间存在的数据,除非您有其他来源的完整性,如加密计数签名。
如果您需要通知其他代理有关完成的提交,请使用post commit钩子和信号或远程调用。
HDK应该被固定📌
HDK的基本功能是使用特定的类型接口与Holochain执行器进行通信。
如果以下任何内容相对于执行器发生变化,您的WASM将确实存在错误
- 主机和客户端用于通信的共享类型
- 用于生成加密算法中使用的字节的序列化逻辑
- 在主机和客户端之间协商共享内存
- 客户端可以在主机上调用的函数
- 客户端需要提供给主机的回调
因此,我们为序列化和内存处理创建了专门的库,这些库很少更改。HDK使用Cargo.toml中的=x.y.z
语法引用这些库,以明确表示这一点。
由于设计上HDK的发布周期比Holochain执行器慢,因此更容易固定和跟踪更改。
您应该使用=x.y.z
语法将HDK的依赖项固定!
您不需要将所有Rust依赖项都固定,只需那些参与定义宿主/客座界面的依赖项。
HDK与Rust tracing集成,以实现更好的调试 🐛
使用hdk_extern!
属性定义的每个extern都会注册一个tracing subscriber,该subscriber在WASM中运行。
所有基本跟踪宏trace!
、debug!
、warn!
、error!
都已实现。
然而,目前跟踪跨度在尝试#[instrument]
时不会工作,您可能会使WASM发生panic。
可以使用与Holochain导体和其他Rust二进制文件中的RUST_LOG
相同的方式,通过运行时使用环境变量WASM_LOG
来过滤WASM跟踪。
最常见的内部错误,例如WASM与外部进程之间的无效反序列化,默认情况下都会被跟踪为error!
。
HDK需要在客座和宿主之间显式处理错误 ⚠
调用宿主提供的函数的所有调用都可能无法干净地执行,至少序列化可能会总是失败。
还有许多其他可能的失败情况,例如损坏的数据库或尝试没有密钥的加密操作。
当宿主遇到失败Result
时,它将序列化错误并将其传递回WASM客座
。客座必须处理这个错误,并将它返回给宿主,然后宿主回滚写入(见上文),或者实现某种优雅的失败或重试逻辑。
在宿主调用的情况下,宿主的Result
表示执行是否成功完成,并且是除其他Result-like枚举之外的信息。例如,从宿主的视角来看,远程调用可以是Ok
,但可能包含来自远程代理的“失败”枚举变体的ZomeCallResponse::Unauthorized
。两者都需要在上下文中处理。
许可:CAL-1.0
依赖项
~12–27MB
~422K SLoC