尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于 WebAssembly 的 Linera 链上 Web 客户端:`@linera/client` 集成指南与 Signer 签名体系实战
基于 WebAssembly 的 Linera 链上 Web 客户端linera/client集成指南与 Signer 签名体系实战【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocollinera/client是 Linera 协议官方发布到 NPM 的浏览器端 Web 客户端包对应仓库中的web/linera/client目录Rust crate 名为linera-web。它把 Linera 客户端的 Rust 核心编译为 WebAssembly并辅以 TypeScript 类型的 JavaScript 封装让前端应用能够直接创建链、查询余额、提交区块并通过统一的Signer接口完成交易签名。读完本文你将掌握该包的整体架构、构建发布流程、initialize初始化细节以及两套开箱即用的签名器PrivateKey与WebCryptoEd25519的选型、用法与底层实现原理。一、定位与架构Rust 核心如何跑进浏览器从 web/linera/client/Cargo.toml 可以看到该包对应的 Rust crate 名为linera-web版本与linera/clientweb/linera/client/package.json 中为0.16.0保持一致crate-type声明为[cdylib, rlib]其中cdylib就是为编译成wasm32目标准备的。核心依赖直连 Linera 的各层库linera-base启用webfeature基础类型与密码学原语如AccountOwner、Ed25519PublicKeylinera-clientwebwasmerindexed-db客户端上下文负责与验证节点交互linera-core、linera-execution、linera-rpc、linera-storage、linera-views均启用web/wasmer/indexeddb相关 feature核心共识、执行、RPC 网络与浏览器存储后端linera-faucet-client与测试网水龙头交互的客户端。web/linera/client/src/lib.rs 通过#![cfg(target_arch wasm32)]显式声明“本 crate 仅面向 Web 编译”并导出Client、Chain、Listener、Formats、Storage、Wallet、Signer、Error等模块同时将类型别名Environment组装为linera_core::environment::ImplStorage, Network, Signer, Wallet——也就是说浏览器端的存储、网络、签名、钱包四件套全部由 Web 适配层注入。在 TypeScript 侧web/linera/client/src/index.ts 是包的统一入口export * from ./wasm/index.js; export * from ./error/index.js; export * as signer from ./signer/index.js; export type { Signer } from ./signer/Signer.js;其中wasm/index.js即 Rust 编译产物对应的 JS 封装wasm-bindgen生成signer命名空间则来自 web/linera/client/src/signer/index.ts导出Composite、PrivateKey、WebCryptoEd25519三个实现类与Signer类型。二、构建与发布文档明确指出构建的前提确保cargo调用的是 nightly 版rustc仓库根目录的 rust-toolchain.toml 与 web/linera/client 目录下的工具链配置共同约束了这一点然后执行pnpm build # 构建 pnpm publish # 发布到 NPM从 web/linera/client/package.json 的脚本可以还原完整的工程化流水线buildbash build.bash --release——以 release 模式编译 Rust 为 Wasm 并产出dist目录exports字段指向./dist/index.js与./dist/index.d.tspreparepnpm build保证安装依赖后自动产出构建产物lintpnpm build cargo fmt --check cargo clippy——同时校验 Rust 与 TS 侧testpnpm exec vitest --run——运行 web/linera/client/tests 下的浏览器测试套件Chain.test.ts、Client.test.ts、Formats.test.ts、PrivateKeySigner.test.ts、WebCryptoEd25519.test.tsdocpnpm exec typedoc——生成 API 文档对应docs/目录下的interfaces/Signer.html等页面。包对外只发布dist目录files: [dist]运行时依赖仅ethersEIP-191 签名与bowser浏览器 UA 检测。三、初始化客户端linera.initialize()在使用任何需要 Wasm 的功能前必须先调用linera.initialize()。其完整签名位于 web/linera/client/src/lib.rs#[derive(serde::Deserialize, Default, tsify::Tsify)] #[tsify(from_wasm_abi)] #[serde(default, rename_all camelCase)] pub struct InitializeOptions { pub log: String, // tracing_subscriber::EnvFilter 格式的日志过滤串默认 INFO pub profiling: bool, // 是否通过 Performance API 上报性能数据 }对应 JS 侧web/linera/client/src/index.ts的行为可以总结为优先从当前 URL 的查询参数读取LINERA_LOG覆盖日志过滤串与LINERA_PROFILING开启性能上报方便开发期通过地址栏直接调试调用 Wasm 模块的default()初始化并执行wasm.initialize(options)Rust 侧使用std::sync::OnceLock保证全局只初始化一次重复调用会输出already initialized警告默认日志过滤串为user_application_logoff,linera_webinfo即默认关闭应用层日志、只保留客户端库自身的信息日志避免刷屏传入自定义log串时则完全使用调用者提供的过滤规则日志输出到浏览器 Consoletracing_web::MakeWebConsoleWriter开启profiling后会额外挂载performance_layer上报到 Performance API。另外initialize()内置了一个针对Safari 26.2的已知崩溃WebKit #303387共享内存 多线程下memory.grow导致崩溃的规避逻辑检测到该版本时先单线程预分配并释放 768 MiB 内存块避免 Wasm 工作线程启动后再触发内存增长。一个完整的最小启动代码import * as linera from linera/client; await linera.initialize(); // 可选传入初始化选项也可用 URL 参数 LINERA_LOG / LINERA_PROFILING 覆盖 // await linera.initialize({ log: linera_webdebug, profiling: true });四、Signer接口跨语言签名桥Signer是连接 JS 签名实现与 Rust 验证逻辑的抽象层定义于 web/linera/client/src/signer/Signer.ts包含三个方法方法作用返回格式要求sign(owner, value)用owner对应的私钥对value签名Address20EIP-191 签名0x前缀、65 字节r||s||vAddress32原生 64 字节 Ed25519 签名r||s均以0x前缀十六进制字符串返回getPublicKey(owner)返回owner对应的公钥Address20未压缩 secp256k1 公钥65 字节Address32原始 32 字节 Ed25519 公钥containsKey(owner)判断签名器是否持有该owner的私钥布尔值接口注释揭示了 Linera 账户所有者的两种地址方案Address20EVM 系 secp256k1 账户使用 EIP-191 签名即 MetaMask 等钱包的签名协议Address32Ed25519 账户对 32 字节CryptoHash预哈希直接做 Ed25519 签名。在 Rust 侧web/linera/client/src/signer/mod.rs这个 JS 对象通过wasm_bindgen的extern C声明被linera_base::crypto::Signertrait 适配。关键逻辑是Rust 把待签名的CryptoHash原始字节而非 BCS 序列化结果传给 JS收到签名字符串后按owner类型分派Address20解析r||s||v格式构造AccountSignature::EvmSecp256k1 { signature, address }Address32额外调用getPublicKey取得公钥若AccountOwner::from(public_key) ! owner则提前报错防止签名器返回“有效签名但公钥不匹配”的异常状态随后构造AccountSignature::Ed25519 { signature, public_key }Reserved直接返回InvalidAccountOwnerType错误。同时该文件定义了SignerError枚举MissingKey、SigningError、PublicKeyParse、JsConversion、UnexpectedSignatureFormat、InvalidAccountOwnerType、Unknown所有 JS 抛出的错误都会被映射为这些可辨识的错误码。五、签名器实现一PrivateKey仅限测试/本地开发PrivateKey实现位于 web/linera/client/src/signer/PrivateKey.ts内部直接持有一个内存中的ethers.Wallet按 EIP-191 方案签名。文档与源码都给出了醒目的安全警告私钥以明文形式留在 JS 内存中一旦页面暴露给不可信代码XSS、恶意依赖即等于泄露只允许用于测试与本地开发严禁用于生产环境。import * as linera from linera/client; import { PrivateKey } from linera/client/signer; // 方式一随机生成内部经由助记词创建 const signer PrivateKey.createRandom(); // 方式二从助记词恢复 const signer2 PrivateKey.fromMnemonic(your mnemonic phrase ...); // 方式三从原始私钥构造 const signer3 new PrivateKey(0x...); // ethers.Wallet 兼容的私钥 hex const owner signer.address(); // 以太坊风格地址即 Address20 owner await signer.sign(owner, new Uint8Array(32)); // EIP-191 签名返回 0x 前缀 65 字节 r||s||v实现要点均可在源码中验证sign()、getPublicKey()、containsKey()三个方法都会先用ethers.isAddress校验 owner 格式并比对wallet.address不匹配即抛出Invalid owner addresscreateRandom()经由ethers.Wallet.createRandom()的助记词间接创建返回的公钥是wallet.signingKey.publicKey即未压缩的 65 字节 secp256k1 公钥符合Signer接口对Address20的要求。对应的测试见 web/linera/client/tests/PrivateKeySigner.test.ts。六、签名器实现二WebCryptoEd25519浏览器生产级会话密钥WebCryptoEd25519web/linera/client/src/signer/WebCryptoEd25519.ts是文档推荐的面向生产环境的签名器其核心安全模型是原始私钥字节永不进入 JavaScript。具体机制通过 Web Crypto API 的crypto.subtle.generateKey({ name: Ed25519 }, false, [sign, verify])生成密钥对extractable: false意味着私钥永远无法被导出私钥以CryptoKey句柄形式持久化到IndexedDB数据库名linera-signerobject store 名keys版本 1刷新页面后依然可用页面内的攻击者如 XSS 或恶意依赖只能在当前标签页存活期间请求签名无法把密钥本身拷走——这就是“会话密钥”级别的安全边界。6.1 常用 API 一览方法说明generate()生成新的密钥对并返回签名器不落盘需手动persist适合先注册链上再落盘的流程persist(recordKey)把密钥对写入 IndexedDB覆盖旧记录load(recordKey)读取已有记录不存在则返回nullloadOrCreate(recordKey)便利方法有则加载无则生成并持久化delete(recordKey)删除本地记录例如链上撤销授权或迁移密钥形态后address()返回Address32账户所有者地址格式0x 64 位小写十六进制sign(owner, value)/getPublicKey(owner)/containsKey(owner)实现Signer接口6.2 owner 地址的派生WebCryptoEd25519的 owner 地址不是公钥哈希直取而是AccountOwner::Address32(Keccak256(BCS(public_key)))——由 Rust 侧导出给 Wasm 的函数accountOwnerFromEd25519PublicKey派生web/linera/client/src/crypto.rs#[wasm_bindgen(js_name accountOwnerFromEd25519PublicKey)] pub fn account_owner_from_ed25519_public_key(public_key: [u8]) - ResultString, JsError { let pubkey Ed25519PublicKey::from_slice(public_key).map_err(|e| JsError::new(e.to_string()))?; Ok(AccountOwner::from(pubkey).to_string()) }该派生路径的正确性由双向测试钉死浏览器测试 web/linera/client/tests/WebCryptoEd25519.test.ts 中用固定的公钥字节0x01..0x20断言 JS 侧派生结果必须等于0xeacee5344cbec9569e836f95029d476c700f4f5bc007c71c0752c73fba149043而该期望值来自linera-base/src/identifiers.rs中的 Rust 已知向量测试两侧互锁防止派生逻辑漂移。6.3 持久化的工程细节源码中值得注意的两处实现细节写入必须等待transaction.oncompletetxWrite只有在 IndexedDB 写事务提交完成后才 resolve确保persist()返回时密钥已经落到磁盘日志避免“返回后立刻关标签页导致密钥丢失”的竞态读取txRead则只等req.onsuccess即可因为此时值已在内存中多标签页阻塞处理openDb()监听onblocked另一个旧版本连接阻塞升级时直接报错并提示关闭其他标签页与onversionchange检测到其他标签页触发升级时主动关闭本连接放行。6.4 签名流程与防御性拷贝sign()内部先校验 owner 匹配只规范化调用方一侧的大小写因为record.owner恒为 Rusthex::encode产出的小写形式随后做一次防御性拷贝new Uint8Array(value).buffer——因为部分浏览器会拒绝为SharedArrayBuffer支持的视图调用crypto.subtle.sign无论调用方如何取得value都能保证签名成功。6.5 使用示例文档给出的最小用法也是标准姿势import * as linera from linera/client; await linera.initialize(); const signer await linera.signer.WebCryptoEd25519.loadOrCreate(my-app-key); const owner signer.address(); // 0x 64 hex chars (AccountOwner::Address32)更完整的生命周期// 生成新密钥不落盘→ 先在链上注册 → 再持久化 const signer await linera.signer.WebCryptoEd25519.generate(); const owner signer.address(); // ... 把 owner 注册为链的所有者/自动签名者 ... await signer.persist(my-app-key); // 下次打开页面直接恢复同一个账户 const restored await linera.signer.WebCryptoEd25519.loadOrCreate(my-app-key); console.log(restored.address() owner); // true // 签名value 为 32 字节预哈希或其他数据 const sig await restored.sign(owner, new Uint8Array(32)); // 本地撤销如链上 forgoDelegation 成功后清理 await linera.signer.WebCryptoEd25519.delete(my-app-key);浏览器兼容性Web Crypto Ed25519 目前已在所有现代稳定浏览器可用Chrome 137、Firefox 129、Safari 17。七、签名器实现三Composite——多签名者编排当应用同时支持多种签名来源例如既内置WebCryptoEd25519会话密钥又允许接入 MetaMask 等 EIP-1193 钱包时可以使用 web/linera/client/src/signer/Composite.ts 提供的Composite实现它按构造顺序依次询问每个子签名器import { signer } from linera/client; const session await signer.WebCryptoEd25519.loadOrCreate(session-key); const composite new signer.Composite(session, metamaskSigner /* 任意实现了 Signer 的对象 */); await composite.sign(owner, value); // 自动路由到持有该 owner 私钥的那个签名器其语义为对sign/getPublicKey遍历子签名器遇到第一个containsKey(owner)为true的即委派给它containsKey则为“任一子签名器持有即返回 true”。若没有任何签名器持有该 owner则抛出no signer found for owner ...。八、签名器接入链路与测试闭环把前面几节串起来一次浏览器内交易签名在 JS 与 Rust 之间的完整数据流是应用代码调用signer.sign(owner, value)JS 侧ethers/ Web Crypto 完成原始签名返回0x前缀 hex 字符串Wasm 桥web/linera/client/src/signer/mod.rs把value作为CryptoHash预哈希原始字节传入按 owner 类型解析签名与公钥Rust 侧构造AccountSignature::EvmSecp256k1或AccountSignature::Ed25519交给linera-client的ClientContext打包进区块/证书验证节点按标准密码学路径校验签名。测试闭环体现在 web/linera/client/tests/WebCryptoEd25519.test.ts 中已知向量测试固定的 Ed25519 公钥必须派生为固定的Address32owner与 Rust 侧测试互锁IndexedDB 往返测试loadOrCreate后load出的签名器地址不变且重新加载的CryptoKey仍可正常签名——测试里先用crypto.subtle.importKey导入公钥、再用crypto.subtle.verify独立验证签名证明签名确实由原始私钥产生幂等性测试同一recordKey多次loadOrCreate返回同一个地址。这些测试都以真实浏览器环境运行vitest vitest/browser playwright而非 mocks。九、安全模型小结结合文档与源码可以对两套签名器的安全边界给出精确结论维度PrivateKeyWebCryptoEd25519适用场景测试、本地开发、CLI 类工具生产浏览器应用会话密钥私钥存放JS 内存明文ethers.Wallet浏览器密码学子系统extractable: false的CryptoKey永不进入 JS持久化无需调用方自行保管助记词/私钥IndexedDBlinera-signer库CryptoKey句柄签名方案secp256k1 EIP-191Address20Ed25519Address32威胁模型页面被攻陷即密钥泄露攻击者仅能在标签页存活期间请求签名无法导出密钥标签页关闭后攻击面消失一句话选型建议测试用PrivateKey生产环境的自动会话密钥用WebCryptoEd25519如果还需要接入 MetaMask 等外部钱包将其封装为实现了Signer接口的对象后用Composite统一编排。十、相关资源包入口与导出web/linera/client/src/index.tsSigner接口定义web/linera/client/src/signer/Signer.ts签名器实现web/linera/client/src/signer/PrivateKey.ts、web/linera/client/src/signer/WebCryptoEd25519.ts、web/linera/client/src/signer/Composite.tsRust 侧 Wasm 桥与 owner 派生web/linera/client/src/signer/mod.rs、web/linera/client/src/crypto.rs浏览器测试套件web/linera/client/tests/WebCryptoEd25519.test.ts 等参与贡献与许可CONTRIBUTING.md、LICENSEApache 2.0【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocol创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

北京GEO优化服务商推荐:服务能力参考

北京GEO优化服务商推荐:服务能力参考

北京GEO服务市场正在形成多种路线并行的格局:有的强调技术平台,有的聚焦垂直行业,也有的主打轻量交付。对企业来说,真正有价值的推荐文章,应当把行业背景、服务商能力、筛选标准和合作风险放在同一套判断框架里。 本文…

📅 2026/9/10 18:31:41
Mojo 编译器剖析:Conformance 的惰性解析与 Witness 表物化机制

Mojo 编译器剖析:Conformance 的惰性解析与 Witness 表物化机制

Mojo 编译器剖析:Conformance 的惰性解析与 Witness 表物化机制 【免费下载链接】mojo The Modular Platform (includes MAX & Mojo) 项目地址: https://gitcode.com/GitHub_Trending/mo/mojo 导读 本文基于 Mojo 编译器仓库(Mojo/docs/comp…

📅 2026/9/10 18:31:41
SpringBoot+Vue+MyBatis全栈实践:足球俱乐部管理系统源码详解

SpringBoot+Vue+MyBatis全栈实践:足球俱乐部管理系统源码详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/10 18:26:40
MORE NEWS

更多资讯

📰

ClickHouse在物联网数据处理中的高性能实践

1. ClickHouse在物联网数据处理中的独特价值第一次接触ClickHouse是在2018年处理智能电表项目时,当时我们每天要处理超过20亿条电表读数记录。传统的关系型数据库完全无法应对这种规模的时间序列数据写入和查询,直到我们发现了这个来自俄罗斯的列式数据库…

📰

Mastra Agent Builder 存储 Agent 全生命周期实战:CRUD、Skill 附件与模型配置冒烟测试指南

Mastra Agent Builder 存储 Agent 全生命周期实战:CRUD、Skill 附件与模型配置冒烟测试指南 【免费下载链接】mastra Mastra is the modern TypeScript framework for AI-powered applications and agents. 项目地址: https://gitcode.com/GitHub_Trending/ma/mas…

📰

Fuel 网络的 UTXO 模型与 fuels-ts:理解 Coin、交易开销与找零机制

Fuel 网络的 UTXO 模型与 fuels-ts:理解 Coin、交易开销与找零机制 【免费下载链接】fuels-ts Fuel Network Typescript SDK 项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts Fuel Network 是建立在 UTXO(Unspent Transaction Output…

📰

GESP三级数学部分历年真题汇总

这里为你整理适配四年级零基础学生的GESP三级数学部分历年真题汇总,全部来自近年官方考试的高频典型题,完全贴合之前梳理的考点范围: 一、 历年真题汇总 1. 进制转换类(2023年12月真题) (1)、…

📰

技能与副业精准匹配系统设计与实现

1. 项目背景与核心价值作为一名拥有十年职业规划经验的咨询师,我经常遇到这样的咨询场景:一位掌握Python和数据分析技能的工程师,业余时间却在接单做简单的PPT美化;一位擅长UI设计的设计师,周末在做毫无技术含量的传单…

📰

Flutter + OpenHarmony 夜间模式实战:主题切换与页面适配指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬