返回博客
arweave密码学web3钱包确定性
构建确定性 Arweave 钱包:为什么我们超越了现有库
我们如何使用 node-forge 从零开始构建了一个健壮的确定性 Arweave 钱包生成器,实现了流行库无法提供的功能。
Miguel Treviño•

在构建 Zelf 的多链钱包基础设施时,我们面临了一个关键挑战:从单个 BIP39 助记词生成确定性 Arweave 钱包。要求简单但不可妥协:相同的12个单词必须始终生成相同的 Arweave 地址和私钥。
听起来很简单对吧?其实不然。
要点速览:
- **问题:**现有 Arweave 库无法生成确定性钱包(相同短语 = 不同密钥),这对钱包恢复来说是灾难性的。
- **解决方案:**我们使用
node-forge构建了自定义实现来显式控制 PRNG,确保100%确定性。 - **性能:**优化的2048位 RSA 密钥生成时间约2-3秒(相比60秒以上),且不影响安全性。
- **成果:**一个经过严格验证的开源解决方案,保证可靠的自我托管,现已在 Zelf Wallet 中上线。
现有库的问题
我们最初探索了
arweave-mnemonic-keys,一个专门为此设计的流行库。承诺很完美:传入助记词,返回确定性的 Arweave JWK(JSON Web Key)。但它未能通过最基本的测试。
当我们连续两次用相同的助记词运行该库时,得到了不同的地址。不是略有不同——完全不同。这对钱包恢复功能来说是灾难性的。想象一下告诉用户:"你的助记词将恢复你的钱包……也许吧。有时候可以。祝你好运!"
// 我们期望的结果
const wallet1 = await getKeyFromMnemonic(mnemonic);
const wallet2 = await getKeyFromMnemonic(mnemonic);
console.log(wallet1.address === wallet2.address); // 应该为 true
// 我们得到的结果
// false 😱
这是不可接受的。我们需要的是密码学确定性,而非概率性的希望。
构建我们自己的解决方案
我们没有修补一个有缺陷的库或等待修复,而是使用
node-forge 从零开始构建了自己的实现。以下是我们的方法之所以有效的原因:1. 显式 PRNG 控制
大多数库的核心问题是它们依赖系统随机性(
crypto.randomBytes),这在设计上是非确定性的。即使设置了种子,许多库也无法正确隔离其随机数生成器。我们的解决方案:完全控制伪随机数生成器(PRNG)。
const generateWalletFromMnemonic = async (mnemonic) => {
const forge = require("node-forge");
const bip39 = require("bip39");
const crypto = require("crypto");
// 1. 从助记词推导种子
const seed = await bip39.mnemonicToSeed(mnemonic);
// 2. 使用 SHA-256 哈希链创建确定性 PRNG
let state = seed;
const customPrng = {
getBytesSync: (size) => {
let res = "";
while (res.length < size) {
const hasher = crypto.createHash("sha256");
hasher.update(state);
state = hasher.digest();
res += state.toString("binary");
}
return res.substring(0, size);
},
};
// 3. 使用我们的 PRNG 生成 RSA 密钥
const keyPair = forge.pki.rsa.generateKeyPair({
bits: 2048,
prng: customPrng,
workers: -1, // 强制主线程
});
// 4. 转换为 Arweave JWK 格式
// ...(转换逻辑)
};
2. 性能优化
Arweave 通常使用4096位 RSA 密钥,非常安全但在纯 JavaScript 中生成极其缓慢(约60秒以上)。对于钱包导入/恢复流程,这种用户体验是不可接受的。
我们优化为2048位密钥,它们:
- 仍然完全符合 Arweave 协议
- 生成时间约2-3秒(快20倍)
- 保持钱包用例所需的密码学安全性
- 保持完美的确定性
3. 严格验证
我们不仅仅测试地址是否匹配。我们验证了生成密钥的完整加密能力:
// 验证密钥功能
const data = new TextEncoder().encode("Hello Arweave");
const signature = await arweave.crypto.sign(jwk, data);
const isValid = await arweave.crypto.verify(jwk.n, data, signature);
expect(isValid).toBe(true); // ✅ 通过
这证明密钥不仅结构正确——它还是一个功能完整的 Arweave 私钥,能够签署交易。
成果
我们的实现提供:
✅ 100% 确定性:相同助记词 = 相同地址,每一次
✅ 快速生成:约2-3秒 vs 60秒以上
✅ 密码学验证:密钥可以签名和验证消息
✅ 无外部依赖:我们控制整个堆栈
✅ 生产就绪:集成到 Zelf 的钱包基础设施中
为什么这对 Web3 很重要
确定性密钥生成不仅仅是技术上的细节——它是自我托管的基础。当用户写下他们的助记词时,他们正在备份整个数字身份。如果该备份无法可靠地恢复他们的钱包,"成为自己的银行"的整个承诺就会崩塌。
这对 Arweave 尤其重要,因为它是为永久数据存储设计的。如果你在 Arweave 上存储重要文档或 NFT,你需要绝对的信心,仅凭助记词就可以在多年后访问它们。
更广泛的教训
这次经历强化了一个关键原则:不要信任,要验证。
流行的库不一定是正确的。GitHub 星标不能保证正确性。在构建像钱包这样的关键基础设施时,你需要:
- 严格测试(我们运行了数百次确定性测试)
- 理解密码学(不仅仅是复制粘贴代码)
- 愿意从零构建——当现有解决方案失败时
在 Zelf,我们正在构建自主主权身份的未来。这意味着我们不能在确定性密钥生成这样的基础问题上妥协。当工具不存在时,我们自己构建——并且做到最好。
亲自尝试
想看看实际效果?我们的实现已在 Zelf Wallet 中上线:
- 下载 Zelf,访问 zelf.world
- 导入任何12个单词的助记词(或创建一个新的)
- 即时获取你的 Arweave 地址——并知道每次都是相同的
或者如果你是开发者,查看我们的开源实现并为构建更好的 Web3 基础设施做出贡献。
技术说明:我们的实现使用
node-forge 进行 RSA 生成,配合由 BIP39 助记词种子驱动的自定义 SHA-256 PRNG。完整源代码可供审计和贡献。你是否遇到过类似的加密库问题?在下方评论中分享你的经验。