通过Typescript SDK与机密资产(CA)交互
你可以使用Aptos
客户端的confidentialCoin
属性来与CA
进行交互
初始化
CA中的操作需要生成零知识证明(ZKPs),根据你的运行环境,需要定义范围证明
的计算方式。
对于Web环境,可以使用confidential-asset-wasm-bindings/confidential-asset-wasm-bindings
:
让我们准备范围证明生成并配置SDK使用它:
import initWasm, {
batch_range_proof as batchRangeProof,
batch_verify_proof as batchVerifyProof,
range_proof as rangeProof,
verify_proof as verifyProof,
} from '@aptos-labs/confidential-asset-wasm-bindings/range-proofs'
import {
BatchRangeProofInputs,
BatchVerifyRangeProofInputs,
RangeProofInputs,
VerifyRangeProofInputs,
} from '@lukachi/aptos-labs-ts-sdk'
const RANGE_PROOF_WASM_URL =
'https://unpkg.com/@aptos-labs/confidential-asset-wasm-bindings@0.3.16/range-proofs/aptos_rp_wasm_bg.wasm'
export async function genBatchRangeZKP(
opts: BatchRangeProofInputs,
): Promise<{ proof: Uint8Array; commitments: Uint8Array[] }> {
await initWasm({ module_or_path: RANGE_PROOF_WASM_URL })
const proof = batchRangeProof(
new BigUint64Array(opts.v),
opts.rs,
opts.val_base,
opts.rand_base,
opts.num_bits,
)
return {
proof: proof.proof(),
commitments: proof.comms(),
}
}
export async function verifyBatchRangeZKP(
opts: BatchVerifyRangeProofInputs,
): Promise<boolean> {
await initWasm({ module_or_path: RANGE_PROOF_WASM_URL })
return batchVerifyProof(
opts.proof,
opts.comm,
opts.val_base,
opts.rand_base,
opts.num_bits,
)
}
然后,只需将其放在应用的最顶部:
import { RangeProofExecutor } from '@aptos-labs/ts-sdk'
RangeProofExecutor.setGenBatchRangeZKP(genBatchRangeZKP);
RangeProofExecutor.setVerifyBatchRangeZKP(verifyBatchRangeZKP);
RangeProofExecutor.setGenerateRangeZKP(generateRangeZKP);
RangeProofExecutor.setVerifyRangeZKP(verifyRangeZKP);
对于原生应用:
从这里生成android
和ios
绑定,并按需集成到你的应用中。
最后但同样重要的是:
要获取机密资产的”数值”余额,还需要解决离散对数问题(DLP)。 CA实现了Pollard’s Kangaroo方法来解决Ristretto曲线上的DLP。 来源
因此我们还需要初始化解密函数:
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
import initWasm, {
create_kangaroo,
WASMKangaroo,
} from '@aptos-labs/confidential-asset-wasm-bindings/pollard-kangaroo'
import {
ConfidentialAmount,
TwistedEd25519PrivateKey,
TwistedElGamal,
TwistedElGamalCiphertext,
} from '@lukachi/aptos-labs-ts-sdk'
import { bytesToNumberLE } from '@noble/curves/abstract/utils'
const POLLARD_KANGAROO_WASM_URL =
'https://unpkg.com/@aptos-labs/confidential-asset-wasm-bindings@0.3.15/pollard-kangaroo/aptos_pollard_kangaroo_wasm_bg.wasm'
export async function createKangaroo(secret_size: number) {
await initWasm({ module_or_path: POLLARD_KANGAROO_WASM_URL })
return create_kangaroo(secret_size)
}
export const preloadTables = async () => {
const kangaroo16 = await createKangaroo(16)
const kangaroo32 = await createKangaroo(32)
const kangaroo48 = await createKangaroo(48)
TwistedElGamal.setDecryptionFn(async pk => {
if (bytesToNumberLE(pk) === 0n) return 0n
let result = kangaroo16.solve_dlp(pk, 500n)
if (!result) {
result = kangaroo32.solve_dlp(pk, 1500n)
}
if (!result) {
result = kangaroo48.solve_dlp(pk)
}
if (!result) throw new TypeError('Decryption failed')
return result
})
}
现在,将以下代码放在你的应用顶部:
const init = async () => {
await preloadTables();
}
对于原生应用,你可以在此生成android
和ios
绑定来代替WASM使用。
现在我们准备就绪。让我们定义Aptos客户端:
const APTOS_NETWORK: Network = NetworkToNetworkName[Network.TESTNET];
const config = new AptosConfig({ network: APTOS_NETWORK });
export const aptos = new Aptos(config);
创建解密密钥(DK)
要与保密资产交互,首先需要创建一个唯一的密钥对。
生成新密钥:
const dk = TwistedEd25519PrivateKey.generate();
或导入现有密钥:
const dk = new TwistedEd25519PrivateKey("0x...");
你也可以使用签名
派生密钥(仅用于测试目的,勿在生产环境使用):
const user = Account.generate()
const signature = user.sign(TwistedEd25519PrivateKey.decryptionKeyDerivationMessage);
const dk = TwistedEd25519PrivateKey.fromSignature(signature);
注册
接下来,你需要在合约中注册之前生成的加密密钥(EK):
export const registerConfidentialBalance = async (
account: Account,
publicKeyHex: string,
tokenAddress = "0x...",
) => {
const txBody = await aptos.confidentialAsset.deposit({
sender: account.accountAddress,
to: AccountAddress.from(to),
tokenAddress: tokenAddress,
amount: amount,
})
const txResponse = await aptos.signAndSubmitTransaction({ signer: user, transaction: userRegisterCBTxBody });
const txReceipt = await aptos.waitForTransaction({ transactionHash: txResponse.hash });
return txReceipt;
}
检查用户是否已注册特定代币:
export const getIsAccountRegisteredWithToken = async (
account: Account,
tokenAddress = "0x...",
) => {
const isRegistered = await aptos.confidentialAsset.hasUserRegistered({
accountAddress: account.accountAddress,
tokenAddress: tokenAddress,
})
return isRegistered
}
存款
假设你已经拥有代币。
以下代码会将代币存入你的保密余额:
export const depositConfidentialBalance = async (
account: Account,
amount: bigint,
to: string,
tokenAddress = "0x...",
) => {
const txBody = await aptos.confidentialAsset.deposit({
sender: account.accountAddress,
to: AccountAddress.from(to),
tokenAddress: tokenAddress,
amount: amount,
})
// 签名并发送交易
}
获取用户余额
存款后,让我们检查用户的余额。
const userConfidentialBalance = await aptos.confidentialAsset.getBalance({ accountAddress: user.accountAddress, tokenAddress: TOKEN_ADDRESS });
```该方法返回用户的[`pending`和`actual`](confidential-asset.mdx#confidential-asset-store)保密余额,要[解密](confidential-asset.mdx#encryption-and-decryption)这些余额,可以使用`ConfidentialAmount`类
```typescript
export const getConfidentialBalances = async (
account: Account,
decryptionKeyHex: string,
tokenAddress = "0x...",
) => {
const decryptionKey = new TwistedEd25519PrivateKey(decryptionKeyHex)
const { pending, actual } = await aptos.confidentialAsset.getBalance({
accountAddress: account.accountAddress,
tokenAddress,
})
try {
const [confidentialAmountPending, confidentialAmountActual] =
await Promise.all([
ConfidentialAmount.fromEncrypted(pending, decryptionKey),
ConfidentialAmount.fromEncrypted(actual, decryptionKey),
])
return {
pending: confidentialAmountPending,
actual: confidentialAmountActual,
}
} catch (error) {
return {
pending: ConfidentialAmount.fromAmount(0n),
actual: ConfidentialAmount.fromAmount(0n),
}
}
}
余额结转
当您向用户的保密余额存款后,可以看到例如pending
余额中有5n
,而actual
余额为0n
。
用户无法操作pending
余额,因此您可以将其结转到actual
余额。
为此,请使用aptos.confidentialAsset.rolloverPendingBalance
。
重要提示:在rollover
操作前,用户的actual余额需要先进行标准化处理。
要同时处理标准化和rollover
操作,可以使用aptos.confidentialAsset.safeRolloverPendingCB
。
export const safelyRolloverConfidentialBalance = async (
account: Account,
decryptionKeyHex: string,
tokenAddress = "0x...",
) => {
const rolloverTxPayloads = await aptos.confidentialAsset.safeRolloverPendingCB({
sender: account.accountAddress,
tokenAddress,
decryptionKey: new TwistedEd25519PrivateKey(decryptionKeyHex),
})
// 签名并发送批量交易
}
标准化
通常您不需要显式调用标准化操作
如果您需要手动操作:
首先检查保密余额是否已标准化,因为尝试对已标准化的余额再次标准化会抛出异常
export const getIsBalanceNormalized = async (
account: Account,
tokenAddress = "0x...",
) => {
const isNormalized = await aptos.confidentialAsset.isUserBalanceNormalized({
accountAddress: account.accountAddress,
tokenAddress: tokenAddress,
})
return isNormalized
}
获取余额后,最终调用aptos.confidentialAsset.normalizeUserBalance
方法:
export const normalizeConfidentialBalance = async (
account: Account,
decryptionKeyHex: string,
encryptedPendingBalance: TwistedElGamalCiphertext[],
amount: bigint,
tokenAddress = "0x...",
) => {
const normalizeTx = await aptos.confidentialAsset.normalizeUserBalance({
tokenAddress,
decryptionKey: new TwistedEd25519PrivateKey(decryptionKeyHex),
unnormalizedEncryptedBalance: encryptedPendingBalance,
balanceAmount: amount,
sender: account.accountAddress,
})
// 签名并发送交易
}
提现
要将资产从机密余额中提现:
export const withdrawConfidentialBalance = async (
account: Account,
receiver: string,
decryptionKeyHex: string,
withdrawAmount: bigint,
encryptedActualBalance: TwistedElGamalCiphertext[],
tokenAddress = '0x...',
) => {
const withdrawTx = await aptos.confidentialAsset.withdraw({
sender: account.accountAddress,
to: receiver,
tokenAddress,
decryptionKey: decryptionKey,
encryptedActualBalance,
amountToWithdraw: withdrawAmount,
})
// 签名并发送交易
}
转账
进行机密转账需要知道接收方的加密密钥和aptos
账户地址
假设您已有接收方账户地址,让我们获取其加密密钥:
export const getEkByAddr = async (addrHex: string, tokenAddress: string) => {
return aptos.confidentialAsset.getEncryptionByAddr({
accountAddress: AccountAddress.from(addrHex),
tokenAddress,
})
}
现在将所有步骤整合并进行转账:
export const transferConfidentialCoin = async (
account: Account,
decryptionKeyHex: string,
encryptedActualBalance: TwistedElGamalCiphertext[],
amountToTransfer: bigint,
recipientAddressHex: string,
auditorsEncryptionKeyHexList: string[],
tokenAddress = "0x...",
) => {
const decryptionKey = new TwistedEd25519PrivateKey(decryptionKeyHex)
const recipientEncryptionKeyHex = await getEkByAddr(
recipientAddressHex,
tokenAddress,
)
const transferTx = await aptos.confidentialAsset.transferCoin({
senderDecryptionKey: decryptionKey,
recipientEncryptionKey: new TwistedEd25519PublicKey(
recipientEncryptionKeyHex,
),
encryptedActualBalance: encryptedActualBalance,
amountToTransfer,
sender: account.accountAddress,
tokenAddress,
recipientAddress: recipientAddressHex,
auditorEncryptionKeys: auditorsEncryptionKeyHexList.map(
hex => new TwistedEd25519PublicKey(hex),
),
})
// 签名并发送交易
}
密钥轮换
要进行密钥轮换,您需要创建新的解密密钥并使用aptos.confidentialAsset.rotateCBKey
但请注意,key-rotation
会检查待处理余额是否为0。
在这种情况下,我们可以使用带有freeze
选项的rollover
操作,将资产从待处理余额转移到实际余额并锁定余额。
aptos.confidentialAsset.safeRolloverPendingCB({
...,
withFreezeBalance: false,
})
现在让我们创建新的解密密钥并轮换加密密钥:
const balances = await getBalances(user.accountAddress.toString(), myDecryptionKey, TOKEN_ADDRESS);
const NEW_DECRYPTION_KEY = TwistedEd25519PrivateKey.generate();
const keyRotationAndUnfreezeTxResponse = await ConfidentialCoin.safeRotateCBKey(aptos, user, {
sender: user.accountAddress,
currDecryptionKey: currentDecryptionKey,
newDecryptionKey: NEW_DECRYPTION_KEY,
currEncryptedBalance: balances.actual.amountEncrypted,
withUnfreezeBalance: true, // 如果您想在之后解冻余额
tokenAddress: TOKEN_ADDRESS,
});
// 保存:新的解密密钥
console.log(NEW_DECRYPTION_KEY.toString());
// 检查新的余额
const newBalance = await getBalances(user.accountAddress.toString(), NEW_DECRYPTION_KEY, TOKEN_ADDRESS);
console.log(newBalance.pending.amount);
console.log(newBalance.actual.amount);