Skip to content

Commit eb42205

Browse files
committed
Merge branch 'main' into dark-pool
2 parents 327139d + 4892a57 commit eb42205

File tree

16 files changed

+99
-150
lines changed

16 files changed

+99
-150
lines changed

apps/interface/src/lib/components/SendForm.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { lib } from "$lib";
33
import { requestRollup } from "$lib/utils";
44
import { sdk } from "@repo/contracts/sdk";
5+
import { TokenAmount } from "@repo/contracts/sdk/RollupService";
56
import { Ui } from "@repo/ui";
67
import { utils } from "@repo/utils";
78
import { assert } from "ts-essentials";
@@ -38,7 +39,10 @@
3839
secretKey,
3940
fromNote: note,
4041
to,
41-
amount: BigInt(amount.quotient.toString()),
42+
amount: await TokenAmount.from({
43+
token: amount.currency.address,
44+
amount: BigInt(amount.quotient.toString()),
45+
}),
4246
});
4347
await requestRollup();
4448
}

apps/interface/src/lib/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ const contract = PoolERC20__factory.connect(
3838
const coreSdk = sdk.createCoreSdk(contract);
3939
const trees = new sdk.RemoteTreesService(route("POST /api/trees"));
4040
const interfaceSdk = sdk.createInterfaceSdk(coreSdk, trees, {
41-
shield: import("@repo/contracts/noir/target/shield.json"),
42-
unshield: import("@repo/contracts/noir/target/unshield.json"),
43-
join: import("@repo/contracts/noir/target/join.json"),
44-
transfer: import("@repo/contracts/noir/target/transfer.json"),
41+
shield: import("@repo/contracts/noir/target/erc20_shield.json"),
42+
unshield: import("@repo/contracts/noir/target/erc20_unshield.json"),
43+
join: import("@repo/contracts/noir/target/erc20_join.json"),
44+
transfer: import("@repo/contracts/noir/target/erc20_transfer.json"),
4545
});
4646
const reown = new ReownService(contract);
4747
const evm = new EvmAccountService();

packages/contracts/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ node_modules
2020
.DS_Store
2121

2222
Prover.toml
23+
24+
data
25+
configs
26+
bn254_g*.dat

packages/contracts/contracts/PoolERC20.sol

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ contract PoolERC20 is PoolGeneric {
4646
) external {
4747
token.safeTransferFrom(msg.sender, address(this), amount);
4848

49-
PublicInputs.Type memory pi = PublicInputs.create(2 + 2 + 1);
49+
PublicInputs.Type memory pi = PublicInputs.create(1 + 2 + 1);
5050
pi.push(getNoteHashTree().root);
51-
pi.push(getNullifierTree().root);
5251
pi.push(address(token));
5352
pi.pushUint256Limbs(amount);
53+
// TODO(security): ensure noteHash does not already exist in the noteHashTree. If it exists, the tx will never be rolled up and the money will be lost.
5454
pi.push(note.noteHash);
5555
require(
5656
_poolErc20Storage().shieldVerifier.verify(proof, pi.finish()),
@@ -73,6 +73,9 @@ contract PoolERC20 is PoolGeneric {
7373
bytes32 nullifier,
7474
NoteInput calldata changeNote
7575
) external {
76+
// TODO(security): bring back unshield. It was removed because nullifiers are no longer checked on tx level. Only when the tx is rolled up.
77+
require(false, "not implemented");
78+
7679
PublicInputs.Type memory pi = PublicInputs.create(6 + 1);
7780
// params
7881
pi.push(getNoteHashTree().root);
@@ -106,10 +109,9 @@ contract PoolERC20 is PoolGeneric {
106109
NoteInput calldata joinNote
107110
) external {
108111
PublicInputs.Type memory pi = PublicInputs.create(
109-
2 + MAX_NOTES_TO_JOIN + 1
112+
1 + MAX_NOTES_TO_JOIN + 1
110113
);
111114
pi.push(getNoteHashTree().root);
112-
pi.push(getNullifierTree().root);
113115
pi.push(joinNote.noteHash);
114116
for (uint256 i = 0; i < MAX_NOTES_TO_JOIN; i++) {
115117
pi.push(nullifiers[i]);
@@ -136,9 +138,8 @@ contract PoolERC20 is PoolGeneric {
136138
NoteInput calldata changeNote,
137139
NoteInput calldata toNote
138140
) external {
139-
PublicInputs.Type memory pi = PublicInputs.create(5);
141+
PublicInputs.Type memory pi = PublicInputs.create(4);
140142
pi.push(getNoteHashTree().root);
141-
pi.push(getNullifierTree().root);
142143
pi.push(changeNote.noteHash);
143144
pi.push(toNote.noteHash);
144145
pi.push(nullifier);
@@ -163,9 +164,8 @@ contract PoolERC20 is PoolGeneric {
163164
NoteInput[4] calldata notes,
164165
bytes32[2] calldata nullifiers
165166
) external {
166-
PublicInputs.Type memory pi = PublicInputs.create(8);
167+
PublicInputs.Type memory pi = PublicInputs.create(1 + 6);
167168
pi.push(getNoteHashTree().root);
168-
pi.push(getNullifierTree().root);
169169
pi.push(notes[0].noteHash);
170170
pi.push(notes[1].noteHash);
171171
pi.push(notes[2].noteHash);

packages/contracts/contracts/PoolGeneric.sol

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,9 @@ uint32 constant MAX_NOTES_PER_ROLLUP = 64;
99
// Note: keep in sync with other languages
1010
uint32 constant MAX_NULLIFIERS_PER_ROLLUP = 64;
1111

12-
// Note: keep in sync with other languages
13-
uint256 constant NOTE_HASH_OR_NULLIFIER_STATE_NOT_EXISTS = 0;
14-
// Note: keep in sync with other languages
15-
uint256 constant NOTE_HASH_OR_NULLIFIER_STATE_PENDING = 1;
16-
// Note: keep in sync with other languages
17-
uint256 constant NOTE_HASH_OR_NULLIFIER_STATE_ROLLED_UP = 2;
18-
1912
struct PendingTx {
2013
bool rolledUp;
14+
// TODO(perf): store a hash of the noteHashes and nullifiers and check when rolling up
2115
Fr[] noteHashes;
2216
Fr[] nullifiers;
2317
}
@@ -32,10 +26,8 @@ contract PoolGeneric {
3226
IVerifier rollupVerifier;
3327
PendingTx[] allPendingTxs;
3428
AppendOnlyTreeSnapshot noteHashTree;
35-
mapping(Fr => uint256) noteHashState; // TODO(perf): nuke this
3629
uint256 noteHashBatchIndex;
3730
AppendOnlyTreeSnapshot nullifierTree;
38-
mapping(Fr => uint256) nullifierState; // TODO(perf): nuke this
3931
uint256 nullifierBatchIndex;
4032
}
4133

@@ -47,14 +39,12 @@ contract PoolGeneric {
4739
uint256 indexed index,
4840
Fr[MAX_NOTES_PER_ROLLUP] noteHashes
4941
);
50-
error NoteHashExists(Fr noteHash);
5142

5243
// TODO(perf): use dynamic array to save on gas costs
5344
event Nullifiers(
5445
uint256 indexed index,
5546
Fr[MAX_NULLIFIERS_PER_ROLLUP] nullifiers
5647
);
57-
error NullifierExists(Fr nullifier);
5848

5949
constructor(IVerifier rollupVerifier_) {
6050
_poolGenericStorage().rollupVerifier = rollupVerifier_;
@@ -147,20 +137,12 @@ contract PoolGeneric {
147137
);
148138
_poolGenericStorage().noteHashTree = newNoteHashTree;
149139
_poolGenericStorage().nullifierTree = newNullifierTree;
150-
// TODO(perf): remove this disgusting gas waste
151-
for (uint256 i = 0; i < pendingNoteHashes.length; i++) {
152-
_poolGenericStorage().noteHashState[
153-
pendingNoteHashes[i]
154-
] = NOTE_HASH_OR_NULLIFIER_STATE_ROLLED_UP;
155-
}
156-
// TODO(perf): remove this disgusting gas waste
157-
for (uint256 i = 0; i < pendingNullifiers.length; i++) {
158-
_poolGenericStorage().nullifierState[
159-
pendingNullifiers[i]
160-
] = NOTE_HASH_OR_NULLIFIER_STATE_ROLLED_UP;
161-
}
162140
}
163141

142+
/**
143+
* @dev REQUIREMENT: noteHashes do not exist in the noteHashTree and nullifiers do not exist in the nullifierTree.
144+
* If they do, the tx will never be rolled up.
145+
*/
164146
function _PoolGeneric_addPendingTx(
165147
NoteInput[] memory noteInputs,
166148
bytes32[] memory nullifiers
@@ -178,28 +160,12 @@ contract PoolGeneric {
178160

179161
for (uint256 i = 0; i < noteInputs.length; i++) {
180162
Fr noteHash = FrLib.tryFrom(noteInputs[i].noteHash);
181-
require(
182-
_poolGenericStorage().noteHashState[noteHash] ==
183-
NOTE_HASH_OR_NULLIFIER_STATE_NOT_EXISTS,
184-
NoteHashExists(noteHash)
185-
);
186-
_poolGenericStorage().noteHashState[
187-
noteHash
188-
] = NOTE_HASH_OR_NULLIFIER_STATE_PENDING;
189163
// TODO(perf): this is a waste of gas
190164
pendingTx.noteHashes.push(noteHash);
191165
}
192166

193167
for (uint256 i = 0; i < nullifiers.length; i++) {
194168
Fr nullifier = FrLib.tryFrom(nullifiers[i]);
195-
require(
196-
_poolGenericStorage().nullifierState[nullifier] ==
197-
NOTE_HASH_OR_NULLIFIER_STATE_NOT_EXISTS,
198-
NullifierExists(nullifier)
199-
);
200-
_poolGenericStorage().nullifierState[
201-
nullifier
202-
] = NOTE_HASH_OR_NULLIFIER_STATE_PENDING;
203169
// TODO(perf): this is a waste of gas
204170
pendingTx.nullifiers.push(nullifier);
205171
}
@@ -227,14 +193,6 @@ contract PoolGeneric {
227193
return _poolGenericStorage().nullifierTree;
228194
}
229195

230-
function noteHashState(bytes32 noteHash) external view returns (uint256) {
231-
return _poolGenericStorage().noteHashState[FrLib.tryFrom(noteHash)];
232-
}
233-
234-
function nullifierState(bytes32 nullifier) external view returns (uint256) {
235-
return _poolGenericStorage().nullifierState[FrLib.tryFrom(nullifier)];
236-
}
237-
238196
function _poolGenericStorage()
239197
private
240198
pure

packages/contracts/noir/.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
11
target
2-
data
3-
configs
4-
bn254_g*.dat

packages/contracts/noir/common/src/lib.nr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,4 @@ impl std::cmp::Ord for TokenAmount {
104104

105105
pub struct TreeRoots {
106106
pub note_hash_root: Field,
107-
pub nullifier_root: Field,
108107
}

packages/contracts/noir/common/src/owned_note.nr

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@ where
3838
context.tree_roots().note_hash_root,
3939
);
4040
let nullifier = compute_nullifier_of_owned_note(self.note, secret_key);
41-
// TODO(perf): optimize consume by checking nullifiers only during rollup
42-
merkle_tree::assert_check_non_membership(
43-
nullifier,
44-
self.nullifier_low_leaf_preimage,
45-
self.nullifier_low_leaf_membership_witness,
46-
context.tree_roots().nullifier_root,
47-
);
4841
context.push_nullifier(nullifier);
4942
}
5043
}

packages/contracts/noir/erc20_unshield/src/main.nr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ fn main(
1212
) -> pub common::Result<1, 1> {
1313
let mut context = common::Context::from(tree_roots);
1414

15+
// disabled because nullifiers are no longer checked on tx level
16+
panic(f"not implemented");
17+
1518
erc20::Token::burn(
1619
&mut context,
1720
from_secret_key,

packages/contracts/sdk/NativeUltraPlonkBackend.ts renamed to packages/contracts/sdk/NativeUltraHonkBackend.ts

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import type { ProofData } from "@aztec/bb.js";
12
import type { CompiledCircuit } from "@noir-lang/noir_js";
23
import { spawn } from "node:child_process";
34
import fs from "node:fs";
45
import path from "node:path";
6+
import { decodeNativeHonkProof } from "./utils.js";
57

6-
export class NativeUltraPlonkBackend {
8+
export class NativeUltraHonkBackend {
79
constructor(
810
readonly bbPath: string,
911
readonly circuit: CompiledCircuit,
@@ -30,7 +32,7 @@ export class NativeUltraPlonkBackend {
3032
fs.writeFileSync(circuitJsonPath, JSON.stringify(this.circuit));
3133
fs.writeFileSync(witnessOutputPath, witness);
3234
const args = [
33-
"prove", // ultraplonk
35+
"prove_ultra_keccak_honk",
3436
"-b",
3537
circuitJsonPath,
3638
"-w",
@@ -48,15 +50,17 @@ export class NativeUltraPlonkBackend {
4850
console.error(`stderr: ${data}`);
4951
});
5052

51-
return await new Promise<{ proof: Uint8Array }>((resolve, reject) => {
53+
return await new Promise<ProofData>((resolve, reject) => {
5254
bbProcess.on("close", (code: number) => {
5355
if (code !== 0) {
5456
reject(new Error(`Process exited with code ${code}`));
5557
return;
5658
}
5759

58-
const proof = fs.readFileSync(proofOutputPath);
59-
resolve(splitUltraPlonkProof(proof));
60+
const proofData = decodeNativeHonkProof(
61+
fs.readFileSync(proofOutputPath),
62+
);
63+
resolve(proofData);
6064
});
6165

6266
bbProcess.on("error", (err) => {
@@ -89,36 +93,3 @@ export class NativeUltraPlonkBackend {
8993
return targetDir;
9094
}
9195
}
92-
93-
async function splitUltraPlonkProof(proofData: Uint8Array) {
94-
const proof = proofData.slice(-2144);
95-
const publicInputsFlat = proofData.slice(0, proofData.length - 2144);
96-
const publicInputs = deflattenFields(publicInputsFlat);
97-
return { proof, publicInputs };
98-
}
99-
100-
export function deflattenFields(flattenedFields: Uint8Array): string[] {
101-
const publicInputSize = 32;
102-
const chunkedFlattenedPublicInputs: Uint8Array[] = [];
103-
104-
for (let i = 0; i < flattenedFields.length; i += publicInputSize) {
105-
const publicInput = flattenedFields.slice(i, i + publicInputSize);
106-
chunkedFlattenedPublicInputs.push(publicInput);
107-
}
108-
109-
return chunkedFlattenedPublicInputs.map(uint8ArrayToHex);
110-
}
111-
112-
function uint8ArrayToHex(buffer: Uint8Array): string {
113-
const hex: string[] = [];
114-
115-
buffer.forEach(function (i) {
116-
let h = i.toString(16);
117-
if (h.length % 2) {
118-
h = "0" + h;
119-
}
120-
hex.push(h);
121-
});
122-
123-
return "0x" + hex.join("");
124-
}

0 commit comments

Comments
 (0)