机密资产(CA)
@aptos-labs/confidential-asset 是 机密资产(CA) 协议的高级 TypeScript 客户端。它会自动处理证明生成、WASM 初始化、余额解密和交易构建,让开发者无需直接操作密码学原语。
npm i @aptos-labs/ts-sdk @aptos-labs/confidential-assetyarn add @aptos-labs/ts-sdk @aptos-labs/confidential-assetpnpm add @aptos-labs/ts-sdk @aptos-labs/confidential-asset该包依赖 @aptos-labs/confidential-asset-bindings 执行密码学操作(Bulletproof 范围证明和离散对数解密)。WASM 二进制文件按需加载:
- 浏览器:自动从 unpkg CDN 获取。
- Node.js:从本地
node_modules加载,无需网络。
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";import { ConfidentialAsset, TwistedEd25519PrivateKey, initializeWasm, isWasmInitialized,} from "@aptos-labs/confidential-asset";
const config = new AptosConfig({ network: Network.TESTNET });const aptos = new Aptos(config);
// CA 模块部署在 0x1(aptos_framework)。const ca = new ConfidentialAsset({ config });
// 在用户首次交互前预加载 WASM,避免首次证明生成时的延迟。await initializeWasm();console.log("WASM 就绪:", isWasmInitialized());ConfidentialAsset 构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config | AptosConfig | 必填 | 网络配置 |
confidentialAssetModuleAddress | string | "0x1" | 模块地址 |
withFeePayer | boolean | false | 是否默认使用代付 Gas 交易 |
CA 操作使用一套独立于 Aptos 账户签名密钥的密钥对:
TwistedEd25519PrivateKey(解密密钥,DK):由用户私密保管,永不离开客户端。TwistedEd25519PublicKey(加密密钥,EK):存储在链上,供他人为该用户加密金额。
import { TwistedEd25519PrivateKey } from "@aptos-labs/confidential-asset";
// 生成新的解密密钥const dk = TwistedEd25519PrivateKey.generate();
// 派生对应的加密密钥(公开)const ek = dk.publicKey();
// 序列化以便安全存储const dkHex = dk.toString();
// 从已保存的十六进制字符串恢复const restoredDk = new TwistedEd25519PrivateKey(dkHex);import { Aptos, AptosConfig, Network, Account } from "@aptos-labs/ts-sdk";import { ConfidentialAsset, TwistedEd25519PrivateKey } from "@aptos-labs/confidential-asset";
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET }));const ca = new ConfidentialAsset({ config: aptos.config });
// APT 同质化资产 metadata 地址const TOKEN = "0xa";
const alice = Account.generate();const bob = Account.generate();// 请先通过测试网水龙头为账户注资...
// 1. 生成解密密钥并注册机密余额const aliceDk = TwistedEd25519PrivateKey.generate();const bobDk = TwistedEd25519PrivateKey.generate();await ca.registerBalance({ signer: alice, tokenAddress: TOKEN, decryptionKey: aliceDk });await ca.registerBalance({ signer: bob, tokenAddress: TOKEN, decryptionKey: bobDk });
// 2. 将 Alice 公开余额中的 1000 个代币存入机密待处理余额await ca.deposit({ signer: alice, tokenAddress: TOKEN, amount: 1000n });
// 3. 将待处理余额 rollover 为可用余额await ca.rolloverPendingBalance({ signer: alice, tokenAddress: TOKEN, senderDecryptionKey: aliceDk,});
// 4. 查询 Alice 的解密后余额const balance = await ca.getBalance({ accountAddress: alice.accountAddress, tokenAddress: TOKEN, decryptionKey: aliceDk,});console.log("可用余额:", balance.availableBalance()); // bigintconsole.log("待处理余额:", balance.pendingBalance()); // bigint
// 5. 向 Bob 发起 300 代币的机密转账(金额在链上隐藏)await ca.transfer({ signer: alice, tokenAddress: TOKEN, recipient: bob.accountAddress, amount: 300n, senderDecryptionKey: aliceDk,});
// 6. 将 200 代币提回 Alice 的公开余额await ca.withdraw({ signer: alice, tokenAddress: TOKEN, senderDecryptionKey: aliceDk, amount: 200n,});API 参考
Section titled “API 参考”registerBalance
Section titled “registerBalance”为 (signer, token) 创建机密存储,并将 EK 发布到链上。
await ca.registerBalance({ signer: alice, tokenAddress: TOKEN, decryptionKey: aliceDk,});
// 注册前建议先检查const registered = await ca.hasUserRegistered({ accountAddress: alice.accountAddress, tokenAddress: TOKEN,});deposit
Section titled “deposit”将代币从 signer 的公开 FA 余额转入 pending_balance。存入金额在链上可见,无需 ZKP。
await ca.deposit({ signer: alice, tokenAddress: TOKEN, amount: 1000n, recipient: alice.accountAddress, // 可选,默认为 signer});getBalance
Section titled “getBalance”从链上获取加密余额,并用 DK 在本地解密。
const balance = await ca.getBalance({ accountAddress: alice.accountAddress, tokenAddress: TOKEN, decryptionKey: aliceDk,});
balance.availableBalance(); // bigint — 可花费金额balance.pendingBalance(); // bigint — 待处理金额(不可花费)解密通过 WASM 模块对每个 16 位块执行 BSGS(Baby-Step Giant-Step)算法。
rolloverPendingBalance
Section titled “rolloverPendingBalance”将 pending_balance 移入 available_balance。
await ca.rolloverPendingBalance({ signer: alice, tokenAddress: TOKEN, senderDecryptionKey: aliceDk, // 需要归一化时必填 withPauseIncoming: false, // 密钥轮换前设为 true});normalizeBalance
Section titled “normalizeBalance”将 available_balance 各块重新压缩到 16 位范围。通常由 rolloverPendingBalance 自动调用。
const normalized = await ca.isBalanceNormalized({ accountAddress: alice.accountAddress, tokenAddress: TOKEN,});if (!normalized) { await ca.normalizeBalance({ signer: alice, tokenAddress: TOKEN, senderDecryptionKey: aliceDk, });}transfer
Section titled “transfer”将隐私金额从发送方 available_balance 转移到接收方 pending_balance,金额在链上永不公开。
await ca.transfer({ signer: alice, tokenAddress: TOKEN, recipient: bob.accountAddress, amount: 300n, senderDecryptionKey: aliceDk, additionalAuditorEncryptionKeys: [], // 可选的自愿审计员 memo: new TextEncoder().encode("发票 #42"), // 可选,最大 256 字节});如需在转账前自动 rollover,使用 transferWithTotalBalance:
await ca.transferWithTotalBalance({ signer: alice, tokenAddress: TOKEN, recipient: bob.accountAddress, amount: 300n, senderDecryptionKey: aliceDk,});withdraw
Section titled “withdraw”将代币从 available_balance 提回公开 FA 存储。
await ca.withdraw({ signer: alice, tokenAddress: TOKEN, senderDecryptionKey: aliceDk, amount: 200n, recipient: bob.accountAddress, // 可选,默认为 signer});如需在提款前自动 rollover,使用 withdrawWithTotalBalance。
rotateEncryptionKey
Section titled “rotateEncryptionKey”替换链上 EK,并用新密钥重新加密 available_balance 的 R 分量。
const newDk = TwistedEd25519PrivateKey.generate();
// SDK 自动处理:rollover + 暂停入账 → 轮换 → 恢复入账await ca.rotateEncryptionKey({ signer: alice, tokenAddress: TOKEN, senderDecryptionKey: aliceDk, // 旧 DK newSenderDecryptionKey: newDk, // 新 DK});
// 请立即保存 newDk——旧 DK 此后对该余额无效。当代币配置了审计员(全局或资产级),SDK 会在每次花费操作时自动包含为该审计员加密的金额密文,无需额外配置。
// 查询资产级审计员密钥(未设置时返回 undefined)const auditorEk = await ca.getAssetAuditorEncryptionKey({ tokenAddress: TOKEN });
// 添加自愿审计员await ca.transfer({ // ...其他参数 additionalAuditorEncryptionKeys: [auditorEk],});代付 Gas
Section titled “代付 Gas”所有写入方法都支持代付 Gas 交易:
// 全局启用const caFeePayer = new ConfidentialAsset({ config, withFeePayer: true });
// 或按次启用await ca.deposit({ signer: alice, tokenAddress: TOKEN, amount: 1000n, withFeePayer: true });| 错误码 | 含义 | 解决方法 |
|---|---|---|
E_CONFIDENTIAL_STORE_NOT_REGISTERED (3) | 该 (用户, 代币) 无机密存储 | 先调用 registerBalance |
E_CONFIDENTIAL_STORE_ALREADY_REGISTERED (2) | 已注册 | 注册前先检查 hasUserRegistered |
E_INCOMING_TRANSFERS_PAUSED (4) | 接收方已暂停入账 | 等待接收方恢复,或换其他接收方 |
E_PENDING_BALANCE_MUST_BE_ROLLED_OVER (6) | 待处理计数达到 65536 上限 | 调用 rolloverPendingBalance |
E_NORMALIZATION_REQUIRED (7) | Rollover 前需要先归一化 | 向 rollover 传入 senderDecryptionKey,或手动调用 normalizeBalance |
E_ALREADY_NORMALIZED (8) | 余额已归一化 | 先检查 isBalanceNormalized |
E_PENDING_BALANCE_NOT_ZERO_BEFORE_KEY_ROTATION (5) | 密钥轮换前待处理余额必须为零 | rotateEncryptionKey 会自动处理 |
E_SELF_TRANSFER (15) | 发送方与接收方相同 | 换一个接收方 |
E_ASSET_TYPE_DISALLOWED (9) | 代币不在白名单中 | 该代币类型尚未启用 CA |
E_EMERGENCY_PAUSED (20) | 协议被治理紧急暂停 | 检查 isEmergencyPaused() 并等待 |
E_UNSAFE_DISPATCHABLE_FA (16) | 不支持 dispatchable FA | 仅支持非 dispatchable FA 类型 |
E_MEMO_TOO_LONG (19) | Memo 超过 256 字节 | 缩短 memo |
预热 WASM。 在用户打开 CA 界面时调用 initializeWasm(),避免首次证明生成时出现明显延迟。
花费前检查是否需要 rollover。 若 pendingBalance() 非零且合并后余额充足,使用 *WithTotalBalance 系列方法一次性完成 rollover 和花费。
验证接收方是否已注册。 转账前调用 hasUserRegistered 确认接收方已注册。
密钥轮换后立即保存新密钥。 rotateEncryptionKey 会使旧 DK 失效,请立即持久化新 DK,并在丢弃旧密钥前验证它能成功解密余额。
| 隐藏 | 不隐藏 |
|---|---|
| 转账金额 | 发送方和接收方地址 |
| 用户余额 | 机密模式下的代币总量(get_total_confidential_supply) |