Linera 前端钱包接入指南:通过 Signer 接口集成 MetaMask 与自定义签名器

📅 发布时间:2026/9/10 16:34:49
Linera 前端钱包接入指南:通过 Signer 接口集成 MetaMask 与自定义签名器
Linera 前端钱包接入指南通过 Signer 接口集成 MetaMask 与自定义签名器【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocol导读Linera 的 JavaScript 客户端库linera/client将交易签名抽象为一个轻量级接口Signer使得前端应用可以自由接入软件钱包、硬件钱包乃至联网钱包服务来为 Linera 交易签名。本文以 wallets.md 为核心骨架结合 web/linera/client 与 examples/counter/metamask 的源码实现完整讲解Signer接口的定义、三种官方参考实现PrivateKey、WebCryptoEd25519、Composite、MetaMask 盲签集成方案以及基于 counter 示例的前端接入实战。读完本文你将能够基于Signer接口为任意钱包实现签名器并理解其背后的安全模型与调用链。一、Signer 接口Linera 前端签名的统一抽象Linera 客户端库的核心设计理念是签名行为不绑定任何特定钱包。只要一个对象满足Signer接口它就能被用来签名 Linera 交易。这意味着你既可以集成浏览器插件钱包如 MetaMask、硬件钱包也可以接入云端托管的联网签名服务——正如官方文档所说the only limit is your imagination。Signer接口定义在 web/linera/client/src/signer/Signer.ts只有三个方法方法签名职责sign(owner, value)(owner: string, value: Uint8Array) Promisestring使用owner关联的私钥对value签名返回0x前缀十六进制签名getPublicKey(owner)(owner: string) Promisestring返回owner对应的公钥0x前缀十六进制containsKey(owner)(owner: string) Promiseboolean判断该 signer 是否持有owner的私钥接口注释还明确约束了两种账户所有权owner scheme的签名格式这是理解整套签名抽象的关键Address20EVM secp256k1返回的签名必须遵循 EIP-191 格式——0x前缀、65 字节的r||s||vgetPublicKey返回 65 字节未压缩 secp256k1 公钥。Address32Ed25519返回原始 64 字节 Ed25519 签名r||sgetPublicKey返回 32 字节原始 Ed25519 公钥供 wasm 桥接层构造AccountSignature::Ed25519 { signature, public_key }。从源码结构看签名最终由 wasm 桥接层消费Client构造函数接收一个signer::Signer见 web/linera/client/src/client.rs因此所有实现都必须与这套接口契约严格对齐才能被客户端正确识别。二、官方 Signer 实现三种开箱即用的签名策略linera/client在 web/linera/client/src/signer/index.ts 中导出了三个内置实现覆盖从测试到生产的常见场景。2.1 PrivateKey内存私钥仅限测试/开发PrivateKey.ts 是文档点名推荐的入门参考实现它基于ethers.Wallet在内存中保存一个 secp256k1 私钥按 EIP-191 方案签名。源码头部有醒目的警告⚠️仅用于测试或开发。私钥直接存放在内存中安全性不足以用于生产环境。它的典型能力包括从十六进制私钥构造new PrivateKey(privateKeyHex)随机生成PrivateKey.createRandom()基于ethers.Wallet.createRandom()的助记词从助记词恢复PrivateKey.fromMnemonic(mnemonic)sign()实现会先校验owner是否为合法地址且与钱包地址一致再调用wallet.signMessage(value)完成 EIP-191 签名getPublicKey()返回wallet.signingKey.publicKey。由于它完全在 JS 内存中工作、不依赖浏览器 API也是编写自定义 signer 时最容易对照的模板。2.2 WebCryptoEd25519浏览器生产级会话密钥WebCryptoEd25519.ts 是面向生产浏览器场景的Address32实现其安全模型值得一提私钥以non-extractable不可导出的CryptoKey形式由 Web Crypto API 生成crypto.subtle.generateKey({ name: Ed25519 }, false, [sign, verify])原始私钥字节永远不进入 JavaScript 堆密钥句柄通过 IndexedDB 持久化数据库名linera-signer、store 名keys刷新页面后仍可通过load(recordKey)恢复如 READMEweb/linera/client/README.md所述页面内攻击者XSS、恶意依赖在标签页打开期间可以请求签名但无法把密钥复制出设备owner 地址由accountOwnerFromEd25519PublicKey(publicKey)在 Rust 侧派生格式为AccountOwner::Address32(Keccak256(BCS(public_key)))呈现为0x 64 位小写十六进制。典型用法取自 READMEimport * 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)loadOrCreate(recordKey)是先load再在缺失时generatepersist的便捷组合delete(recordKey)用于本地撤销会话例如链上委托撤销后。需要说明的是Ed25519 的 Web Crypto 支持有浏览器版本门槛README 标注 Chrome 137、Firefox 129、Safari 17接入前需确认目标浏览器环境。2.3 Composite多 signer 串行路由Composite.ts 实现了一种简单而实用的组合策略按顺序尝试多个 signer把请求路由给第一个持有该 owner 密钥的实现。const client new linera.Client(wallet, new linera.signer.Composite(autosigner, signer));这在自动签名 手动确认混合场景中非常关键详见下文 counter 实战客户端先尝试内存中的自动签名器无 UI 打扰未命中时再回退到 MetaMask 等需要用户交互的 signer。若所有 signer 都不持有该 owner 的密钥则抛出no signer found for owner ...错误。三、MetaMask 集成基于盲签名的 Linera 交易签名MetaMask 是目前 Web 上最流行的加密钱包虽然主要面向 Ethereum但其灵活的盲签名blind-signing能力足以签署任意数据。Linera 为此提供了官方 npm 包linera/metamask仓库内实现位于 web/linera/metamask/src/signer.ts。3.1 实现原理该 signer 依赖 MetaMask 注入到页面的全局window.ethereum对象MetaMaskInpageProvider核心交互如下构造时校验若window.ethereum不存在直接抛出MetaMask is not availablesign(owner, value)先通过eth_requestAccounts获取已连接账户列表校验owner是否在已连接账户中大小写不敏感比对否则抛错将待签名数据Uint8Array编码为十六进制字符串0x前缀调用personal_sign请求签名containsKey(owner)同样通过eth_requestAccounts比对账户列表getPublicKey(owner)明确抛错。注释解释了原因——MetaMask 只服务Address20EVM secp256k1owner而 wasm 桥接层在Address20路径上不会调用getPublicKey因为 EVM 签名将地址内联携带在AccountSignature::EvmSecp256k1中。这个刻意抛错的设计是为了阻止调用方越过桥接契约误用该接口。注意这里签名的是personal_signEIP-191 盲签MetaMask 会弹出确认框让用户对原始消息字节签名——这正是文档所说足够灵活以签署其他类型数据的体现。3.2 依赖与发布形态web/linera/metamask/package.json 显示该包依赖linera/clientworkspace、metamask/providers与ethers以 ESM 形式发布dist/index.js 类型声明入口导出即Signer类index.ts。四、实战counter 示例的 MetaMask 前端文档特别指出examples/counter/metamask 提供了一个基于 MetaMask 的完整前端演示如何用 MetaMask 签名应用交易。它的index.htmlexamples/counter/metamask/index.html是一个几乎无需脚手架的单页演示其初始化流程完整展示了 Linera 前端钱包接入的典型链路import * as linera from linera/client; import * as linera_metamask from linera/metamask; // 1. 初始化 wasm 运行时 await linera.initialize(); // 2. 连接 Faucet创建钱包 const faucet await new linera.Faucet(import.meta.env.LINERA_FAUCET_URL); const signer await new linera_metamask.Signer(); const wallet await faucet.createWallet(); const owner await signer.address(); const chainId await faucet.claimChain(wallet, owner); // 3. 创建自动签名器作为链的默认 owner const autosigner linera.signer.PrivateKey.createRandom(); wallet.setOwner(chainId, autosigner.address()); // 4. 用 Composite 组合自动 MetaMask两个 signer const client await new linera.Client(wallet, new linera.signer.Composite(autosigner, signer)); const chain await client.chain(chainId, { owner }); // 5. 把自动签名器添加为链 owner允许其无感签名 await chain.addOwner(autosigner.address()); // 6. 连接链上的 counter 应用监听新区块并查询状态 const counter await chain.application(import.meta.env.LINERA_APPLICATION_ID); chain.onNotification(notification { /* 渲染新区块与计数 */ });这段代码里的几个设计点与源码一一对应Faucet.createWallet()/Faucet.claimChain()实现在 web/linera/client/src/faucet.rscreateWallet从 faucet 拉取 genesis 配置并构造内存钱包claimChain(wallet, owner)向 faucet 申请一条新链并写入钱包含持久化与加锁逻辑。wallet.setOwner(chainId, owner)对应 web/linera/client/src/wallet.rs 的set_owner把链的默认签名 owner 设置为自动签名器地址。自动签名autosigning工作流默认 owner 用PrivateKey内存密钥无 UI 打扰MetaMask signer 作为Composite的第二顺位兜底代码注释明确说明这允许链上交易由内存 signer 自动签名而应用级交易如 increment 按钮则由 MetaMask 弹窗确认。counter.query(...)通过 GraphQL 查询/变更与合约交互query { value }与mutation { increment(value: 1) }增量按钮触发 MetaMask 签名区块。前端配置package.jsonexamples/counter/metamask/package.json只依赖linera/client、linera/metamask与 Vite环境变量LINERA_FAUCET_URL、LINERA_APPLICATION_ID由部署方注入。合约端则是标准的 counter 应用examples/counter/src/contract.rs、examples/counter/src/service.rs。五、第三方钱包集成Dynamic除自研集成外Linera 的生态合作伙伴Dynamicdynamic.xyz也提供了与 Linera 完全兼容的生产级嵌入式钱包支持通过广泛的Web2 与 Web3 身份提供商如邮箱、社交账号、SSO 等安全登录显著降低终端用户的使用门槛。官方文档说明 Dynamic 已发布两样东西一份Linera 集成配方recipe指导开发者将 Linera 接入 Dynamic 钱包体系一个功能完整的前端示例基于本文介绍的 counter 演示改造用 Dynamic 代替 MetaMask 完成交易签名。需要说明的是Dynamic 的相关代码托管在其合作伙伴仓库本仓库内不包含其实现其签名行为同样遵循本文所述的Signer接口因此集成思路与 MetaMask 一致——实现三个方法、处理 owner 校验、通过Composite或其他策略接入Client。六、编写自定义 Signer 的实操指南综合官方实现编写一个自定义 signer 只需四步实现接口三方法sign、getPublicKey、containsKey签名格式严格按 owner schemeAddress20用 EIP-191 的r||s||vAddress32用原始 Ed25519r||s严格校验 owner参考PrivateKey与 MetaMask 实现中的isAddress/ 大小写归一化比对避免误签他人账户明确定位若只支持Address20如 MetaMaskgetPublicKey可明确抛错并在注释中说明桥接层不会调用若支持Address32必须能返回公钥供 wasm 桥接构造AccountSignature::Ed25519接入客户端将实现实例传入new linera.Client(wallet, signer)或与其他 signer 组合成Composite后传入。对于硬件钱包或云签名服务核心差异仅在sign内部的密钥获取与用户确认机制——Signer接口天然屏蔽了这些差异这正是文档强调可以集成外部软件钱包、硬件钱包、联网钱包服务的原因。七、安全注意事项PrivateKey仅限测试/开发私钥暴露在 JS 内存中页面内任何 XSS 或恶意依赖都可能窃取密钥生产环境请使用WebCryptoEd25519不可导出密钥 IndexedDB 持久化或外部钱包签名MetaMask 签名有浏览器前提要求用户已安装并解锁 MetaMask且目标账户已连接personal_sign会向用户展示盲签确认框需要向用户充分说明签名内容的含义浏览器兼容性WebCryptoEd25519依赖 Web Crypto 的 Ed25519 支持Chrome 137、Firefox 129、Safari 17集成前应做特性检测自动签名权衡counter 示例将PrivateKey设为链默认 owner 实现无感签名务必评估该密钥泄露对链上资产的影响范围并在应用层做好权限边界。延伸阅读Signer 接口定义PrivateKey 内存签名器WebCryptoEd25519 浏览器签名器Composite 组合签名器MetaMask 签名器实现counter 的 MetaMask 前端示例linera/client 包说明与构建方式【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocol创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考