Skip to content

Commit de9ec7f

Browse files
committed
feat: add nonces
1 parent dac4a7d commit de9ec7f

File tree

8 files changed

+203
-102
lines changed

8 files changed

+203
-102
lines changed

yarn-project/acir-simulator/src/client/private_execution.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,16 @@ export class PrivateFunctionExecution {
161161
return Promise.resolve(ZERO_ACVM_FIELD);
162162
},
163163
enqueuePublicFunctionCall: async ([acvmContractAddress], [acvmFunctionSelector], [acvmArgsHash]) => {
164+
const selector = frToAztecAddress(fromACVMField(acvmContractAddress));
164165
const enqueuedRequest = await this.enqueuePublicFunctionCall(
165-
frToAztecAddress(fromACVMField(acvmContractAddress)),
166+
selector,
166167
FunctionSelector.fromField(fromACVMField(acvmFunctionSelector)),
167168
this.context.packedArgsCache.unpack(fromACVMField(acvmArgsHash)),
168169
this.callContext,
169170
);
170171

171172
this.log(
172-
`Enqueued call to public function (with side-effect counter #${enqueuedRequest.sideEffectCounter}) ${acvmContractAddress}:${acvmFunctionSelector}`,
173+
`Enqueued call to public function (with side-effect counter #${enqueuedRequest.sideEffectCounter}) ${acvmContractAddress}:${selector}`,
173174
);
174175
enqueuedPublicFunctionCalls.push(enqueuedRequest);
175176
return toAcvmEnqueuePublicFunctionResult(enqueuedRequest);

yarn-project/acir-simulator/src/public/executor.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,11 @@ export class PublicExecutor {
126126
},
127127
callPublicFunction: async ([address], [functionSelector], [argsHash]) => {
128128
const args = packedArgs.unpack(fromACVMField(argsHash));
129-
this.log(`Public function call: addr=${address} selector=${functionSelector} args=${args.join(',')}`);
129+
const selector = FunctionSelector.fromField(fromACVMField(functionSelector));
130+
this.log(`Public function call: addr=${address} selector=${selector} args=${args.join(',')}`);
130131
const childExecutionResult = await this.callPublicFunction(
131132
frToAztecAddress(fromACVMField(address)),
132-
FunctionSelector.fromField(fromACVMField(functionSelector)),
133+
selector,
133134
args,
134135
execution.callContext,
135136
globalVariables,

yarn-project/aztec.js/src/abis/ecdsa_account_contract.json

+1-5
Large diffs are not rendered by default.

yarn-project/aztec.js/src/abis/schnorr_account_contract.json

+1-5
Large diffs are not rendered by default.

yarn-project/aztec.js/src/abis/schnorr_auth_witness_account_contract.json

+9-29
Large diffs are not rendered by default.

yarn-project/end-to-end/src/e2e_token_contract.test.ts

+149-46
Large diffs are not rendered by default.

yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr

+27-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod util;
77
// The designated caller is ALWAYS used here, and not based on a flag as cross-chain.
88
// message hash = H([caller, selector, , ...args])
99
// To be read as `caller` calls function defined by `selector` with `args`
10+
// Including a nonce in the message hash ensures that the message can only be used once.
1011

1112
contract Token {
1213
// Libs
@@ -35,9 +36,7 @@ contract Token {
3536

3637
use crate::types::{AztecAddress, TransparentNote, TransparentNoteMethods, TRANSPARENT_NOTE_LEN};
3738
use crate::account_interface::AccountContract;
38-
use crate::util::compute_message_hash;
39-
40-
39+
use crate::util::{compute_message_hash};
4140

4241
struct Storage {
4342
admin: PublicState<Field, FIELD_SERIALISED_LEN>,
@@ -169,13 +168,18 @@ contract Token {
169168
from: AztecAddress,
170169
amount: Field,
171170
secret_hash: Field,
171+
nonce: Field,
172172
) -> Field {
173173
let storage = Storage::init(Option::none(), Option::some(&mut context));
174174

175175
if (from.address != context.msg_sender()) {
176-
let selector = compute_selector("shield((Field),Field,Field)");
177-
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, amount, secret_hash]);
176+
// The redeem is only spendable once, so we need to ensure that you cannot insert multiple shields from the same message.
177+
let selector = compute_selector("shield((Field),Field,Field,Field)");
178+
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, amount, secret_hash, nonce]);
178179
assert(AccountContract::at(from.address).is_valid(Option::none(), Option::some(context), message_field) == 0xe86ab4ff, "invalid call");
180+
context.push_new_nullifier(message_field, 0);
181+
} else {
182+
assert(nonce == 0, "invalid nonce");
179183
}
180184

181185
let amount = SafeU120::new(amount);
@@ -194,13 +198,17 @@ contract Token {
194198
from: AztecAddress,
195199
to: AztecAddress,
196200
amount: Field,
201+
nonce: Field,
197202
) -> Field {
198203
let storage = Storage::init(Option::none(), Option::some(&mut context));
199204

200205
if (from.address != context.msg_sender()) {
201-
let selector = compute_selector("transfer_public((Field),(Field),Field)");
202-
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, to.address, amount]);
206+
let selector = compute_selector("transfer_public((Field),(Field),Field,Field)");
207+
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, to.address, amount, nonce]);
203208
assert(AccountContract::at(from.address).is_valid(Option::none(), Option::some(context), message_field) == 0xe86ab4ff, "invalid call");
209+
context.push_new_nullifier(message_field, 0);
210+
} else {
211+
assert(nonce == 0, "invalid nonce");
204212
}
205213

206214
let amount = SafeU120::new(amount);
@@ -232,13 +240,17 @@ contract Token {
232240
from: AztecAddress,
233241
to: AztecAddress,
234242
amount: Field,
243+
nonce: Field,
235244
) -> Field {
236245
let storage = Storage::init(Option::some(&mut context), Option::none());
237246

238247
if (from.address != context.msg_sender()) {
239-
let selector = compute_selector("unshield((Field),(Field),Field)");
240-
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, to.address, amount]);
248+
let selector = compute_selector("unshield((Field),(Field),Field,Field)");
249+
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, to.address, amount, nonce]);
241250
assert(AccountContract::at(from.address).is_valid(Option::some(&mut context), Option::none(), message_field) == 0xe86ab4ff, "invalid call");
251+
context.push_new_nullifier(message_field, 0);
252+
} else {
253+
assert(nonce == 0, "invalid nonce");
242254
}
243255

244256
let from_balance = storage.balances.at(from.address);
@@ -253,13 +265,17 @@ contract Token {
253265
from: AztecAddress,
254266
to: AztecAddress,
255267
amount: Field,
268+
nonce: Field,
256269
) -> Field {
257270
let storage = Storage::init(Option::some(&mut context), Option::none());
258271

259272
if (from.address != context.msg_sender()) {
260-
let selector = compute_selector("transfer((Field),(Field),Field)");
261-
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, to.address, amount]);
273+
let selector = compute_selector("transfer((Field),(Field),Field,Field)");
274+
let message_field = compute_message_hash([context.msg_sender(), selector, from.address, to.address, amount, nonce]);
262275
assert(AccountContract::at(from.address).is_valid(Option::some(&mut context), Option::none(), message_field) == 0xe86ab4ff, "invalid call");
276+
context.push_new_nullifier(message_field, 0);
277+
} else {
278+
assert(nonce == 0, "invalid nonce");
263279
}
264280

265281
let from_balance = storage.balances.at(from.address);

yarn-project/sequencer-client/src/sequencer/sequencer.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,16 @@ export class Sequencer {
282282
const allTxs = [...txs, ...times(emptyTxCount, () => emptyTx)];
283283
this.log(`Building block ${globalVariables.blockNumber}`);
284284

285-
const [block] = await this.blockBuilder.buildL2Block(globalVariables, allTxs, newL1ToL2Messages);
286-
return block;
285+
// If the block is invalid, drop the transactions
286+
try {
287+
const [block] = await this.blockBuilder.buildL2Block(globalVariables, allTxs, newL1ToL2Messages);
288+
return block;
289+
} catch (err) {
290+
const hashes = txs.map(tx => tx.hash);
291+
this.log.error(`Dropping failed txs ${hashes.join(', ')}`);
292+
await this.p2pClient.deleteTxs(hashes);
293+
throw err;
294+
}
287295
}
288296

289297
/**

0 commit comments

Comments
 (0)