Skip to content

Commit 90ef6bb

Browse files
committed
[e2e-tested] add global auditor
1 parent b32bbfa commit 90ef6bb

File tree

10 files changed

+1870
-147
lines changed

10 files changed

+1870
-147
lines changed

confidential-assets/src/api/confidentialAsset.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import {
55
Account,
6+
AccountAddress,
67
AccountAddressInput,
78
AnyNumber,
89
AptosConfig,
@@ -499,9 +500,19 @@ export class ConfidentialAsset {
499500
useCachedValue: true,
500501
});
501502

503+
// Resolve addresses to 32-byte arrays
504+
const senderAddr = AccountAddress.from(signer.accountAddress);
505+
const tokenAddr = AccountAddress.from(tokenAddress);
506+
507+
// Get the auditor public key for the token
508+
const globalAuditorPubKey = await this.getAssetAuditorEncryptionKey({ tokenAddress });
509+
502510
const confidentialNormalization = await ConfidentialNormalization.create({
503511
decryptionKey: senderDecryptionKey,
504512
unnormalizedAvailableBalance: available,
513+
senderAddress: senderAddr.toUint8Array(),
514+
tokenAddress: tokenAddr.toUint8Array(),
515+
auditorEncryptionKey: globalAuditorPubKey,
505516
});
506517

507518
const transaction = await confidentialNormalization.createTransaction({

confidential-assets/src/crypto/confidentialNormalization.ts

Lines changed: 104 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import { ed25519GenListOfRandom } from "../utils";
77
import { EncryptedAmount } from "./encryptedAmount";
88
import { AVAILABLE_BALANCE_CHUNK_COUNT, CHUNK_BITS } from "./chunkedAmount";
99
import { Aptos, SimpleTransaction, AccountAddressInput, InputGenerateTransactionOptions } from "@aptos-labs/ts-sdk";
10-
11-
/** Stub type — sigma proof is not yet implemented. */
12-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
13-
export type ConfidentialNormalizationSigmaProof = {};
10+
import type { SigmaProtocolProof } from "./sigmaProtocol";
11+
import { proveNormalization } from "./sigmaProtocolWithdraw";
1412

1513
export type CreateConfidentialNormalizationOpArgs = {
1614
decryptionKey: TwistedEd25519PrivateKey;
1715
unnormalizedAvailableBalance: EncryptedAmount;
16+
/** 32-byte sender address */
17+
senderAddress: Uint8Array;
18+
/** 32-byte token address */
19+
tokenAddress: Uint8Array;
20+
/** Optional auditor encryption key */
21+
auditorEncryptionKey?: TwistedEd25519PublicKey;
1822
randomness?: bigint[];
1923
};
2024

@@ -25,16 +29,33 @@ export class ConfidentialNormalization {
2529

2630
normalizedEncryptedAvailableBalance: EncryptedAmount;
2731

32+
/** Optional: normalized balance encrypted under auditor key */
33+
auditorEncryptedNormalizedBalance?: EncryptedAmount;
34+
2835
randomness: bigint[];
2936

37+
senderAddress: Uint8Array;
38+
39+
tokenAddress: Uint8Array;
40+
41+
auditorEncryptionKey?: TwistedEd25519PublicKey;
42+
3043
constructor(args: {
3144
decryptionKey: TwistedEd25519PrivateKey;
3245
unnormalizedEncryptedAvailableBalance: EncryptedAmount;
3346
normalizedEncryptedAvailableBalance: EncryptedAmount;
47+
auditorEncryptedNormalizedBalance?: EncryptedAmount;
48+
senderAddress: Uint8Array;
49+
tokenAddress: Uint8Array;
50+
auditorEncryptionKey?: TwistedEd25519PublicKey;
3451
}) {
3552
this.decryptionKey = args.decryptionKey;
3653
this.unnormalizedEncryptedAvailableBalance = args.unnormalizedEncryptedAvailableBalance;
3754
this.normalizedEncryptedAvailableBalance = args.normalizedEncryptedAvailableBalance;
55+
this.auditorEncryptedNormalizedBalance = args.auditorEncryptedNormalizedBalance;
56+
this.senderAddress = args.senderAddress;
57+
this.tokenAddress = args.tokenAddress;
58+
this.auditorEncryptionKey = args.auditorEncryptionKey;
3859
const randomness = this.normalizedEncryptedAvailableBalance.getRandomness();
3960
if (!randomness) {
4061
throw new Error("Randomness is not set");
@@ -43,7 +64,13 @@ export class ConfidentialNormalization {
4364
}
4465

4566
static async create(args: CreateConfidentialNormalizationOpArgs) {
46-
const { decryptionKey, randomness = ed25519GenListOfRandom(AVAILABLE_BALANCE_CHUNK_COUNT) } = args;
67+
const {
68+
decryptionKey,
69+
randomness = ed25519GenListOfRandom(AVAILABLE_BALANCE_CHUNK_COUNT),
70+
senderAddress,
71+
tokenAddress,
72+
auditorEncryptionKey,
73+
} = args;
4774

4875
const unnormalizedEncryptedAvailableBalance = args.unnormalizedAvailableBalance;
4976

@@ -52,31 +79,62 @@ export class ConfidentialNormalization {
5279
publicKey: decryptionKey.publicKey(),
5380
randomness,
5481
});
82+
83+
// If auditor is set, encrypt the normalized balance under the auditor key with the same randomness
84+
let auditorEncryptedNormalizedBalance: EncryptedAmount | undefined;
85+
if (auditorEncryptionKey) {
86+
auditorEncryptedNormalizedBalance = EncryptedAmount.fromAmountAndPublicKey({
87+
amount: unnormalizedEncryptedAvailableBalance.getAmount(),
88+
publicKey: auditorEncryptionKey,
89+
randomness,
90+
});
91+
}
92+
5593
return new ConfidentialNormalization({
5694
decryptionKey,
5795
unnormalizedEncryptedAvailableBalance,
5896
normalizedEncryptedAvailableBalance,
97+
auditorEncryptedNormalizedBalance,
98+
senderAddress,
99+
tokenAddress,
100+
auditorEncryptionKey,
59101
});
60102
}
61103

62-
/** Returns an empty sigma proof (stub — sigma proof is not yet implemented). */
63-
static serializeSigmaProof(): Uint8Array {
64-
return new Uint8Array(0);
65-
}
66-
67-
/** Stub — always returns an empty sigma proof. */
68-
async genSigmaProof(): Promise<ConfidentialNormalizationSigmaProof> {
69-
return {} as ConfidentialNormalizationSigmaProof;
70-
}
104+
/**
105+
* Generate the sigma protocol proof for normalization.
106+
* Normalization is the same as withdrawal with v = 0.
107+
*/
108+
genSigmaProof(): SigmaProtocolProof {
109+
const oldCipherTexts = this.unnormalizedEncryptedAvailableBalance.getCipherText();
110+
const newCipherTexts = this.normalizedEncryptedAvailableBalance.getCipherText();
111+
112+
const oldBalanceC = oldCipherTexts.map((ct) => ct.C);
113+
const oldBalanceD = oldCipherTexts.map((ct) => ct.D);
114+
const newBalanceC = newCipherTexts.map((ct) => ct.C);
115+
const newBalanceD = newCipherTexts.map((ct) => ct.D);
116+
117+
let auditorEncryptionKey: TwistedEd25519PublicKey | undefined;
118+
let newBalanceDAud: import(".").RistPoint[] | undefined;
119+
if (this.auditorEncryptionKey && this.auditorEncryptedNormalizedBalance) {
120+
auditorEncryptionKey = this.auditorEncryptionKey;
121+
newBalanceDAud = this.auditorEncryptedNormalizedBalance.getCipherText().map((ct) => ct.D);
122+
}
71123

72-
/** Stub — always returns true. */
73-
static verifySigmaProof(_opts: {
74-
publicKey: TwistedEd25519PublicKey;
75-
sigmaProof: ConfidentialNormalizationSigmaProof;
76-
unnormalizedEncryptedBalance: EncryptedAmount;
77-
normalizedEncryptedBalance: EncryptedAmount;
78-
}): boolean {
79-
return true;
124+
return proveNormalization({
125+
dk: this.decryptionKey,
126+
senderAddress: this.senderAddress,
127+
tokenAddress: this.tokenAddress,
128+
amount: 0n,
129+
oldBalanceC,
130+
oldBalanceD,
131+
newBalanceC,
132+
newBalanceD,
133+
newAmountChunks: this.normalizedEncryptedAvailableBalance.getAmountChunks(),
134+
newRandomness: this.randomness,
135+
auditorEncryptionKey,
136+
newBalanceDAud,
137+
});
80138
}
81139

82140
async genRangeProof(): Promise<Uint8Array> {
@@ -104,11 +162,21 @@ export class ConfidentialNormalization {
104162
});
105163
}
106164

107-
async authorizeNormalization(): Promise<[{ sigmaProof: Uint8Array; rangeProof: Uint8Array }, EncryptedAmount]> {
108-
const sigmaProof = ConfidentialNormalization.serializeSigmaProof();
165+
async authorizeNormalization(): Promise<
166+
[
167+
{ sigmaProof: SigmaProtocolProof; rangeProof: Uint8Array },
168+
EncryptedAmount,
169+
EncryptedAmount | undefined,
170+
]
171+
> {
172+
const sigmaProof = this.genSigmaProof();
109173
const rangeProof = await this.genRangeProof();
110174

111-
return [{ sigmaProof, rangeProof }, this.normalizedEncryptedAvailableBalance];
175+
return [
176+
{ sigmaProof, rangeProof },
177+
this.normalizedEncryptedAvailableBalance,
178+
this.auditorEncryptedNormalizedBalance,
179+
];
112180
}
113181

114182
async createTransaction(args: {
@@ -119,18 +187,25 @@ export class ConfidentialNormalization {
119187
withFeePayer?: boolean;
120188
options?: InputGenerateTransactionOptions;
121189
}): Promise<SimpleTransaction> {
122-
const [{ rangeProof }, normalizedCB] = await this.authorizeNormalization();
190+
const [{ sigmaProof, rangeProof }, normalizedCB, auditorCB] = await this.authorizeNormalization();
191+
192+
// Build auditor A components (D points encrypted under auditor key)
193+
const newBalanceA = auditorCB
194+
? auditorCB.getCipherText().map((ct) => ct.D.toRawBytes())
195+
: ([] as Uint8Array[]);
123196

124197
return args.client.transaction.build.simple({
125198
...args,
126199
data: {
127200
function: `${args.confidentialAssetModuleAddress}::${MODULE_NAME}::normalize_raw`,
128201
functionArguments: [
129202
args.tokenAddress,
130-
normalizedCB.getCipherTextBytes(),
203+
normalizedCB.getCipherText().map((ct) => ct.C.toRawBytes()), // new_balance_C
204+
normalizedCB.getCipherText().map((ct) => ct.D.toRawBytes()), // new_balance_D
205+
newBalanceA, // new_balance_A
131206
rangeProof,
132-
[] as Uint8Array[], // sigma_proto_comm (stub)
133-
[] as Uint8Array[], // sigma_proto_resp (stub)
207+
sigmaProof.commitment,
208+
sigmaProof.response,
134209
],
135210
},
136211
options: args.options,

0 commit comments

Comments
 (0)