@@ -7,6 +7,7 @@ mod util;
7
7
// The designated caller is ALWAYS used here, and not based on a flag as cross-chain.
8
8
// message hash = H([caller, selector, , ...args])
9
9
// 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.
10
11
11
12
contract Token {
12
13
// Libs
@@ -35,9 +36,7 @@ contract Token {
35
36
36
37
use crate::types:: {AztecAddress , TransparentNote , TransparentNoteMethods , TRANSPARENT_NOTE_LEN };
37
38
use crate::account_interface::AccountContract ;
38
- use crate::util::compute_message_hash ;
39
-
40
-
39
+ use crate::util:: {compute_message_hash };
41
40
42
41
struct Storage {
43
42
admin : PublicState <Field , FIELD_SERIALISED_LEN >,
@@ -169,13 +168,18 @@ contract Token {
169
168
from : AztecAddress ,
170
169
amount : Field ,
171
170
secret_hash : Field ,
171
+ nonce : Field ,
172
172
) -> Field {
173
173
let storage = Storage ::init (Option ::none (), Option ::some (&mut context ));
174
174
175
175
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 ]);
178
179
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" );
179
183
}
180
184
181
185
let amount = SafeU120 ::new (amount );
@@ -194,13 +198,17 @@ contract Token {
194
198
from : AztecAddress ,
195
199
to : AztecAddress ,
196
200
amount : Field ,
201
+ nonce : Field ,
197
202
) -> Field {
198
203
let storage = Storage ::init (Option ::none (), Option ::some (&mut context ));
199
204
200
205
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 ]);
203
208
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" );
204
212
}
205
213
206
214
let amount = SafeU120 ::new (amount );
@@ -232,13 +240,17 @@ contract Token {
232
240
from : AztecAddress ,
233
241
to : AztecAddress ,
234
242
amount : Field ,
243
+ nonce : Field ,
235
244
) -> Field {
236
245
let storage = Storage ::init (Option ::some (&mut context ), Option ::none ());
237
246
238
247
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 ]);
241
250
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" );
242
254
}
243
255
244
256
let from_balance = storage .balances .at (from .address );
@@ -253,13 +265,17 @@ contract Token {
253
265
from : AztecAddress ,
254
266
to : AztecAddress ,
255
267
amount : Field ,
268
+ nonce : Field ,
256
269
) -> Field {
257
270
let storage = Storage ::init (Option ::some (&mut context ), Option ::none ());
258
271
259
272
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 ]);
262
275
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" );
263
279
}
264
280
265
281
let from_balance = storage .balances .at (from .address );
0 commit comments