news 2026/9/16 21:42:25

Foundry Cast 恢复浏览器钱包签名:`cast wallet sign --browser` 的 personal-message 与 EIP-712 支持解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Foundry Cast 恢复浏览器钱包签名:`cast wallet sign --browser` 的 personal-message 与 EIP-712 支持解析

Foundry Cast 恢复浏览器钱包签名:cast wallet sign --browser的 personal-message 与 EIP-712 支持解析

【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry

cast wallet sign是 Foundry 中用于对消息与 EIP-712 结构化数据离线签名的核心命令。本文围绕仓库变更记录 restore-cast-browser-wallet-signing.md 展开,剖析"恢复浏览器钱包 personal-message 与 EIP-712 签名"这一补丁在cast wallet sign中的落地实现:包括--browser参数的引入、两条签名路径的源码细节、地址解析的配套恢复,以及可验证的 CLI 测试用例,帮助读者在使用浏览器钱包(如 MetaMask 类扩展)完成离线签名时,准确理解其能力边界与正确用法。

变更记录解读:一次"恢复"而非"新增"

该变更记录正文只有一句话:

Restored browser wallet personal-message and EIP-712 signing forcast wallet sign.

关键词是Restored(恢复)。这说明浏览器钱包签名能力并非首次引入,而是在某次迭代中被破坏或移除后重新补齐。同类修复在.changelog目录中成组出现,可以从侧面还原这次的修复范围:

  • restore-cast-browser-wallet-readonly-commands.md:恢复了cast wallet addresscast callcast access-listcast estimate四个只读命令的浏览器钱包地址解析;
  • restore-cast-browser-wallet-signing.md(本文主题):恢复了cast wallet sign的浏览器钱包personal-message(personal_sign 风格消息签名)EIP-712 结构化数据签名
  • cast-browser-legacy-type.md:确保浏览器钱包请求中保留显式交易类型(包括 legacy 交易),保证发送时类型不被丢失。

三份记录共同勾勒出一次完整的"浏览器钱包能力回归":只读命令恢复取地址,cast wallet sign恢复两种签名,交易发送恢复显式类型。本文聚焦中间这一环。

cast wallet sign子命令:本地钱包与浏览器钱包的统一入口

命令定义与参数

在 crates/cast/src/cmd/wallet/mod.rs 中,WalletSubcommands::Signcast wallet sign的 CLI 定义(visible_alias = "s"),其核心参数如下:

参数类型/默认值说明
message必填字符串要签名的消息、typed data 或哈希。以0x开头视为十六进制编码,签名前先解码为字节;否则按原始字节处理
--databool将 message 视为 JSON 形式的 EIP-712 typed data
--from-filebool,requires = "data"将 message 视为包含 JSON typed data 的文件路径,必须与--data搭配
--no-hashbool,与--data互斥将 message 视为原始 32 字节哈希,直接签名不再二次哈希
walletWalletOpts(flatten)本地钱包选项:--private-key--mnemonic--account--ledger--aws
browserBrowserWalletOpts(flatten)浏览器钱包选项:--browser

命令的典型用法(来自命令自身的文档注释):

cast wallet sign "hello" --account dev cast wallet sign "hello" --private-key $PK cast wallet sign --data --from-file typed_data.json --ledger

执行流程:先浏览器后本地

从 mod.rs 的Sign分支可以看到,签名路径按"是否指定浏览器钱包"二选一:

  1. 先解析 typed data:let typed_data = data.then(|| parse_typed_data(&message, from_file)).transpose()?;
  2. browser.run::<alloy_network::Ethereum>().await?返回了浏览器钱包 signer,则走浏览器路径;否则走wallet.signer().await?的本地路径;
  3. 无论哪条路径,签名完成后统一输出十六进制签名0x{signature}

CLI 测试 wallet_keystore.rs 中的browser_wallet_commands_expose_browser_option用例专门断言了cast wallet sign --help输出中包含--browser选项,证明该参数对用户可见且已接入帮助文档。

浏览器钱包的两条签名路径

路径一:personal-message 签名(sign_message

当 message 是普通消息时,源码调用:

browser.sign_message(&hex_str_to_bytes(&message)?).await?

这里的关键辅助函数hex_str_to_bytes(mod.rs)负责统一消息编码:

  • 0x开头的字符串:hex::decode解码为原始字节;
  • 不以0x开头:直接s.as_bytes()按 UTF-8 原始字节处理。

解码后的字节会按 Ethereum Signed Message 规范加前缀并哈希后再签名(personal_sign 语义),这与本地钱包路径wallet.sign_message(...)完全一致,保证同一消息由本地密钥与浏览器钱包签名得到的结果一致、可互相验证。

路径二:EIP-712 结构化数据签名(sign_dynamic_typed_data

当指定--data(或--data --from-file <file>)时,源码先通过parse_typed_data解析:

fn parse_typed_data(message: &str, from_file: bool) -> Result<TypedData> { if from_file { Ok(fs::read_json_file(Path::new(message))?) } else { Ok(serde_json::from_str(message)?) } }
  • --data '<json>':将 JSON 字符串直接反序列化为TypedData
  • --data --from-file typed_data.json:从文件读取 JSON。

随后浏览器路径调用browser.sign_dynamic_typed_data(typed_data).await?,按 EIP-712 规范对结构化数据计算哈希并请求浏览器钱包签名。典型用法:

# 直接传 JSON 字符串 cast wallet sign --data '{"types":{...},"primaryType":"Mail",...}' --browser # 从文件读取 cast wallet sign --data --from-file typed_data.json --browser

浏览器钱包的硬性限制:不支持 raw hash

源码在进入分支前有一个前置校验(mod.rs):

if browser.browser && no_hash { eyre::bail!("Raw hash signing is not supported with a browser wallet"); }

--no-hash(直接对 32 字节哈希签名)与--browser互斥。原因是浏览器钱包只能以"加前缀后哈希"或"EIP-712 哈希"的方式签名,无法接受一个已算好的裸哈希。如果同时传入--browser --no-hash,命令会直接报错退出,而不是静默产生语义错误的签名。这也是本补丁恢复的两条路径与本地钱包路径在能力上的唯一分界。

配套恢复:浏览器钱包的地址解析

签名结果本身需要配合签名者地址使用(例如cast wallet verify或链上校验),因此浏览器钱包的地址解析能力是这次回归中不可分割的一部分。

在 mod.rs 的Address分支中,地址解析顺序为:

  1. 显式传入PRIVATE_KEY:直接用私钥派生地址;
  2. 否则若指定--browserbrowser.run::<alloy_network::Ethereum>().await?返回浏览器钱包 signer,取其address()
  3. 否则走本地wallet.signer().await?.address()

与签名恢复同步,restore-cast-browser-wallet-readonly-commands.md 记录了cast wallet addresscast callcast access-listcast estimate四个只读命令同样恢复了浏览器钱包地址解析。这意味着在一个完整工作流中:

# 1. 用浏览器钱包解析地址 ADDR=$(cast wallet address --browser) # 2. 用浏览器钱包签名 EIP-712 数据 cast wallet sign --data --from-file order.json --browser # 3. 离线验证签名归属 cast wallet verify --address "$ADDR" --data --from-file order.json <signature>

每一步都由--browser串联,本地私钥全程不落盘。

输出形态:默认、verbose 与 JSON

签名结果的输出在 mod.rs 中按 verbosity 分三种形态:

场景输出
默认(verbosity == 0仅输出0x<signature>,便于脚本捕获
verbose(-v,非 JSON)依次输出Successfully signed!Message:Address:0x<signature>
verbose + JSON(-v --json输出信封结构{"message":..., "address":..., "signature":...}

测试 wallet_signing.rs 中的wallet_sign_jsonwallet_sign_json_verbose分别验证了默认 JSON 模式只输出裸签名、verbose JSON 模式输出含message/address/signature三个字段的信封,为脚本化集成提供了确定性保证。

测试证据:签名一致性与验证闭环

crates/cast/tests/cli/wallet_signing.rs 是cast wallet sign的 CLI 测试集,可以验证本次恢复所覆盖能力的正确性基线:

  • wallet_sign_message_utf8_data:以私钥0x...01"test"签名,断言输出固定签名,并用cast wallet verify验证成功;对"other msg"验证失败。这证明 personal-message 路径的哈希规则是确定性的、可复现的;
  • wallet_sign_message_hex_data:对0x前缀消息先解码再签名;
  • wallet_sign_and_verify_message_hex_data:以标准测试助记词test test test ... junk走完 private-key → address → sign → verify 全链路。

浏览器钱包本身依赖浏览器交互(CLI 在等待连接时会输出 "Waiting for browser wallet connection" 提示,见 keychain.rs 的测试断言),因此自动化测试以本地密钥路径验证签名语义;浏览器路径与本地路径共用同一套sign_message/sign_dynamic_typed_data语义与输出格式,保证了两者在签名结果层面的一致性。

使用注意事项小结

  1. --browser--no-hash互斥:浏览器钱包无法签裸哈希,组合使用会直接报错;
  2. --from-file依赖--dataclap层面通过requires = "data"强制约束;
  3. 消息编码规则0x前缀按十六进制解码,否则按 UTF-8 原始字节,两条签名路径(本地/浏览器)规则完全一致;
  4. EIP-712 数据格式:必须是合法的 JSON typed data(含typesprimaryType等字段),可内联传入或从文件读取;
  5. 地址配套使用:签名地址通过cast wallet address --browser获取,或使用 verbose 输出中的address字段,配合cast wallet verify完成离线验证闭环。

本次补丁虽小,却补齐了cast wallet sign在浏览器钱包场景下的完整签名能力——personal-message 与 EIP-712 两条路径都恢复了与本地钱包一致的语义,配合只读命令的地址解析回归,使得"浏览器钱包全程参与、私钥永不接触终端"的工作流在 Cast 中重新变得可行。

【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 21:42:12

智慧医疗预约挂号Android端设计与高并发实践

简介&#xff1a;基于Android的智慧医疗预约挂号系统设计项目&#xff0c;面向毕业设计、课程设计与期末大作业场景&#xff0c;适合Android开发学习者参考&#xff0c;涵盖从需求分析到界面实现与接口交互的完整流程。压缩包共114个文件&#xff0c;大小约4.86MB&#xff0c;包…

作者头像 李华
网站建设 2026/9/16 21:42:01

示波器假故障排查指南:识别触发与探头设置陷阱

上周同事火急火燎地跑来&#xff0c;说实验室那台示波器“坏了&#xff0c;波形缩成一团&#xff0c;还满屏乱跳”。我过去扫了一眼显示界面&#xff0c;探头倍率菜单停在“10X”&#xff0c;探头硬件开关却拨在“1X”——这种“假故障”&#xff0c;我一年能遇上几十次。说句不…

作者头像 李华
网站建设 2026/9/16 21:41:53

华为nova 8 Pro固件zip验证、解包与刷机全流程指南

简介&#xff1a;面向华为nova 8 Pro用户和Android开发者的系统级资源包&#xff0c;内含与设备固件更新、模块化配置及自动化安装相关的核心文件&#xff0c;适合需要刷机、系统优化或定制开发的人群。压缩包共7个文件&#xff0c;以shell脚本&#xff08;3个&#xff09;、pr…

作者头像 李华
网站建设 2026/9/16 21:40:22

开源相册分享小程序配独立后台:从本地复现到上线部署实践

简介&#xff1a;一款开源版酷炫相册分享小程序源码&#xff0c;含独立后台&#xff0c;适合小程序开发者、独立站长和内容运营者使用&#xff0c;可用于搭建个人相册、作品展示、付费分享类小程序。源码在官方开源版基础上进行解密与功能增强&#xff0c;覆盖相册管理、访问密…

作者头像 李华
网站建设 2026/9/16 21:39:34

PHP反序列化漏洞实战:字符串逃逸与session注入绕过过滤

这道题我在BUUCTF上刷的时候卡了挺久&#xff0c;不是因为反序列化本身多难&#xff0c;而是入口藏得比较深&#xff0c;加上filter会把关键词替换成空字符串&#xff0c;导致序列化数据长度对不上&#xff0c;直接unserialize就炸。后来把源码审计思路捋顺之后发现&#xff0c…

作者头像 李华