@fatsolutions/privacy-pools-core-starknet-sdk 0.0.42 → 0.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/auditor.ts DELETED
@@ -1,223 +0,0 @@
1
- import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
2
- import { hkdf } from "@noble/hashes/hkdf.js";
3
- import { sha3_256 as sha256 } from "@noble/hashes/sha3.js";
4
- import { bytesToNumberBE, numberToBytesBE } from "@noble/ciphers/utils.js";
5
- import { Secret, getCommitment, AccountCommitment, Hash, PoolInfo } from "@0xbow/privacy-pools-core-sdk";
6
- import { CairoOption, CairoOptionVariant } from "starknet";
7
- import { poseidonHashMany } from "@scure/starknet";
8
- import { StarknetDataService } from "./data.service.js";
9
- import { base64 } from "@scure/base";
10
- import { mnemonicToEntropy } from "@scure/bip39";
11
- import { wordlist } from '@scure/bip39/wordlists/english';
12
-
13
- export interface AuditEvent {
14
- tag: Hash;
15
- ciphertext: string;
16
- prevNullifierHash: Hash;
17
- newCommitment: Hash;
18
- blockNumber: bigint;
19
- transactionHash: string;
20
- }
21
-
22
- //TODO: The first time you will need to generate a viewing key is in the first withdraw
23
- // of a deposited note. At that point you allready have a label. I suggest for the viewKey to be derivable
24
- // from the account secret + deposit label. There is no need to store another secret for each deposit.
25
- // viewKey =~ Hash(AccountSecret, Label);
26
- export function deriveKey(secret: Uint8Array, label: bigint): Uint8Array {
27
- const info = new TextEncoder().encode("xchacha-key");
28
- const salt = new TextEncoder().encode(String(label));
29
- const viewKey = hkdf(sha256, secret, salt, info, 32);
30
- return viewKey;
31
- }
32
-
33
- export function deriveKeyFromMnemonic(mnemonic: string, label: bigint): Uint8Array {
34
- const secret = mnemonicToEntropy(mnemonic, wordlist);
35
- return deriveKey(secret, label);
36
- }
37
-
38
- export function encrypt(plaintext: Uint8Array, viewKey: Uint8Array) {
39
- const nonce = crypto.getRandomValues(new Uint8Array(24));
40
-
41
- const chacha = xchacha20poly1305(viewKey, nonce);
42
- const ciphertext = chacha.encrypt(plaintext);
43
-
44
- // Prepend the nonce
45
- const output = new Uint8Array(24 + ciphertext.length);
46
- output.set(nonce, 0);
47
- output.set(ciphertext, 24);
48
-
49
- return output;
50
- }
51
-
52
- export function decrypt(input: Uint8Array, viewKey: Uint8Array): Uint8Array {
53
- const nonce = input.slice(0, 24);
54
- const chacha = xchacha20poly1305(viewKey, nonce);
55
-
56
- const ciphertext = input.slice(24);
57
- //TODO: What happens if AE fails?
58
- const decrypted = chacha.decrypt(ciphertext);
59
-
60
- return decrypted;
61
- }
62
-
63
- export function packAuditorData({
64
- label: declaredLabel,
65
- value: declaredValue,
66
- nullifier: declaredNullifier,
67
- secret: declaredSecret
68
- }: Pick<AccountCommitment, 'label' | 'value' | 'nullifier' | 'secret'>,
69
- ): Uint8Array {
70
-
71
- //TODO: Add assertions about lenght of bigint
72
- const output = new Uint8Array(128);
73
- output.set(numberToBytesBE(declaredLabel, 32), 0);
74
- output.set(numberToBytesBE(declaredValue, 32), 32);
75
- output.set(numberToBytesBE(declaredNullifier, 32), 64);
76
- output.set(numberToBytesBE(declaredSecret, 32), 96);
77
-
78
- return output;
79
- }
80
-
81
- export function unpackAuditorData(packedData: Uint8Array)
82
- : {
83
- declaredLabel: bigint,
84
- declaredValue: bigint,
85
- declaredNullifier: bigint,
86
- declaredSecret: bigint;
87
- } {
88
- const declaredLabel = bytesToNumberBE(packedData.slice(0, 32));
89
- const declaredValue = bytesToNumberBE(packedData.slice(32, 64));
90
- const declaredNullifier = bytesToNumberBE(packedData.slice(64, 96));
91
- const declaredSecret = bytesToNumberBE(packedData.slice(96, 128));
92
-
93
- return { declaredLabel, declaredValue, declaredNullifier, declaredSecret };
94
- }
95
-
96
-
97
- export function reconstructAccountCommitment(event: AuditEvent, viewKey: Uint8Array): AccountCommitment {
98
- const { ciphertext, blockNumber, transactionHash, prevNullifierHash } = event;
99
-
100
- const auditorData = decrypt(base64.decode(ciphertext), viewKey);
101
- const { declaredLabel, declaredValue, declaredNullifier, declaredSecret } = unpackAuditorData(auditorData);
102
-
103
- const {
104
- hash,
105
- nullifierHash,
106
- preimage: { value: _value, label, precommitment: _precommitment }
107
- } = getCommitment(declaredValue, declaredLabel, declaredNullifier as Secret, declaredSecret as Secret);
108
-
109
- if (nullifierHash != prevNullifierHash) {
110
- throw new Error("Inconsistency in emitted nullifier hash");
111
- }
112
-
113
- return {
114
- hash,
115
- value: declaredValue,
116
- label: label as Hash,
117
- nullifier: declaredNullifier as Secret,
118
- secret: declaredSecret as Secret,
119
- blockNumber,
120
- txHash: transactionHash as `0x{string}`,
121
- };
122
- }
123
-
124
- export function computeTag(viewKey: Uint8Array, withdraw_number: number): bigint {
125
- const viewKeyBigInt = bytesToNumberBE(viewKey);
126
- const tag = poseidonHashMany([viewKeyBigInt, BigInt(withdraw_number)]);
127
- return tag;
128
- };
129
-
130
-
131
- async function fetchAuditEventsForViewKey(
132
- viewKey: Uint8Array,
133
- serviceData: StarknetDataService,
134
- pool: PoolInfo,
135
- ): Promise<[AuditEvent, number][]> {
136
- let out: [AuditEvent, number][] = [];
137
- let withdraw_number = 0;
138
- while (true) {
139
- let tag = computeTag(viewKey, withdraw_number);
140
- let events = await serviceData.getAuditEventForTag(tag, pool);
141
- if (!events) { break; };
142
-
143
- //It should be only ONE event with this tag. But it is not eforced. A troll could make a withdraw with
144
- //the tag of a previous withraw from another label.
145
- events.forEach(event => out.push([event, withdraw_number]));
146
- withdraw_number++;
147
- }
148
- return out;
149
- }
150
-
151
- export async function constructSequence(
152
- viewKey: Uint8Array,
153
- serviceData: StarknetDataService,
154
- pool: PoolInfo
155
- ): Promise<AccountCommitment[]> {
156
- const commitments: AccountCommitment[] = [];
157
- const events = await fetchAuditEventsForViewKey(viewKey, serviceData, pool);
158
-
159
- const events_for_index = events.filter(([_, b]) => b === 0);
160
- if (events_for_index.length === 0) {
161
- throw new Error("No withdraws for this key");
162
- }
163
-
164
- let declaredCommitment: AccountCommitment | undefined;
165
-
166
- for (const [event, _withdraw_number] of events_for_index) {
167
- try {
168
- declaredCommitment = reconstructAccountCommitment(event, viewKey);
169
- } catch {
170
- //What should we do here?
171
- }
172
- }
173
-
174
- if (!declaredCommitment) {
175
- throw new Error("No event could be decrypted");
176
- }
177
-
178
-
179
- let initialLabel = declaredCommitment.label;
180
-
181
- let deposit = await serviceData.getDeposits(pool, pool.deploymentBlock, initialLabel);
182
- if (deposit.length != 1) {
183
- throw new Error("No deposit found for this label");
184
- }
185
-
186
- let initialCommitment = deposit[0]!.commitment;
187
- if (initialCommitment != declaredCommitment.hash) {
188
- throw new Error("mismatch");
189
- }
190
- commitments.push(declaredCommitment);
191
-
192
- for (let i = 1; i < events.length; i++) {
193
- let events_for_index = events.filter(([_, b]) => b === i);
194
-
195
- for (const [event, _withdraw_number] of events_for_index) {
196
- try {
197
- declaredCommitment = reconstructAccountCommitment(event, viewKey);
198
- } catch {
199
- //What should we do here?
200
- }
201
- }
202
-
203
- if (initialLabel != declaredCommitment.label) {
204
- throw new Error("mismatch");
205
- }
206
-
207
- if (commitments[i - 1]!.hash != declaredCommitment.hash) {
208
- throw new Error("mismatch");
209
- }
210
-
211
- commitments.push(declaredCommitment);
212
- }
213
-
214
- return commitments;
215
- }
216
-
217
- /**
218
- * Represents a AuditEvent event from a privacy pool
219
- */
220
- export interface AuditorData {
221
- tag: bigint;
222
- ciphertext: string;
223
- }