Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,20 @@ pub(crate) comptime fn create_authorize_once_check(f: FunctionDefinition, is_pri
let nonce_check_quote = f"{nonce_arg_name_quoted} == 0".quoted_contents();

let fn_call = if is_private {
let args_len = f.parameters().len();
quote { aztec::authwit::auth::assert_current_call_valid_authwit::<$args_len> }
let params = f.parameters();
let serialized_len_quote = if params.len() == 0 {
quote { 0 }
} else {
let params_quote_without_parentheses = params
.map(|(_, param_type): (Quoted, Type)| {
quote {
<$param_type as aztec::protocol::traits::Serialize>::N
}
})
.join(quote {+});
quote { ($params_quote_without_parentheses) }
};
quote { aztec::authwit::auth::assert_current_call_valid_authwit::<($serialized_len_quote)> }
} else {
quote { aztec::authwit::auth::assert_current_call_valid_authwit_public }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ use aztec::macros::aztec;
pub contract AuthWitTest {
use aztec::{
authwit::auth::{assert_inner_hash_valid_authwit, assert_inner_hash_valid_authwit_public},
macros::functions::external,
protocol::address::AztecAddress,
macros::functions::{authorize_once, external},
protocol::{address::AztecAddress, traits::{Deserialize, Serialize}},
};

#[derive(Serialize, Deserialize)]
pub struct MultiFieldStruct {
pub a: Field,
pub b: Field,
pub c: Field,
}

#[external("private")]
fn consume(on_behalf_of: AztecAddress, inner_hash: Field) {
assert_inner_hash_valid_authwit(self.context, on_behalf_of, inner_hash);
Expand All @@ -17,4 +24,16 @@ pub contract AuthWitTest {
fn consume_public(on_behalf_of: AztecAddress, inner_hash: Field) {
assert_inner_hash_valid_authwit_public(self.context, on_behalf_of, inner_hash);
}

/// Regression test for #[authorize_once] with a struct parameter that serializes to 3 fields.
/// The macro must emit assert_current_call_valid_authwit::<6>
/// (from:1 + data:3 + amount:1 + nonce:1), not ::<4> (parameter count).
#[authorize_once("from", "authwit_nonce")]
#[external("private")]
fn auth_with_struct(
from: AztecAddress,
_data: MultiFieldStruct,
_amount: Field,
authwit_nonce: Field,
) {}
}
60 changes: 60 additions & 0 deletions yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import type { Logger } from '@aztec/aztec.js/log';
import type { AztecNode } from '@aztec/aztec.js/node';
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
import { type TokenContract, TokenContractArtifact } from '@aztec/noir-contracts.js/Token';
import { AuthWitTestContract, AuthWitTestContractArtifact } from '@aztec/noir-test-contracts.js/AuthWitTest';
import { GenericProxyContract } from '@aztec/noir-test-contracts.js/GenericProxy';
import { PendingNoteHashesContract } from '@aztec/noir-test-contracts.js/PendingNoteHashes';
import { type AbiDecoded, decodeFromAbi, getFunctionArtifact } from '@aztec/stdlib/abi';
import { computeOuterAuthWitHash } from '@aztec/stdlib/auth-witness';

import { jest } from '@jest/globals';

import { simulateThroughAuthwitProxy } from './fixtures/authwit_proxy.js';
import { deployToken, mintTokensToPrivate } from './fixtures/token_utils.js';
import { setup } from './fixtures/utils.js';
import type { TestWallet } from './test-wallet/test_wallet.js';
Expand Down Expand Up @@ -328,6 +331,63 @@ describe('Kernelless simulation', () => {
});
});

describe('authorize_once with multi-field struct parameters', () => {
let authWitTestContract: AuthWitTestContract;
let proxy: GenericProxyContract;

beforeAll(async () => {
[{ contract: authWitTestContract }, { contract: proxy }] = await Promise.all([
AuthWitTestContract.deploy(wallet).send({ from: adminAddress }),
GenericProxyContract.deploy(wallet).send({ from: adminAddress }),
]);
});

it('emits offchain effect with correct serialized args length for struct parameters', async () => {
Comment thread
Thunkar marked this conversation as resolved.
const structData = { a: Fr.random(), b: Fr.random(), c: Fr.random() };
const amount = Fr.random();
const nonce = Fr.random();

// This function uses a struct with 3 fields as parameter, and the #[authorize_once] macro should correctly
// account for this and emit a CallAuthorizationRequest with the correct serialized length, rather than
// just the arguments length
const interaction = authWitTestContract.methods.auth_with_struct(adminAddress, structData, amount, nonce);

wallet.setSimulationMode('kernelless-override');
const { offchainEffects } = await simulateThroughAuthwitProxy(proxy, interaction, {
from: adminAddress,
includeMetadata: true,
});

expect(offchainEffects.length).toBe(1);

const callAuthRequest = await CallAuthorizationRequest.fromFields(offchainEffects[0].data);

expect(offchainEffects[0].contractAddress).toEqual(authWitTestContract.address);

// The macro should emit 6 arguments in total before decoding: from (1) + struct (3) + amount (1) + nonce (1)
expect(callAuthRequest.args).toHaveLength(6);

expect(callAuthRequest.onBehalfOf).toEqual(adminAddress);
expect(callAuthRequest.msgSender).toEqual(proxy.address);

const functionAbi = await getFunctionArtifact(AuthWitTestContractArtifact, callAuthRequest.functionSelector);
const decodedArgs = decodeFromAbi(
functionAbi.parameters.map(param => param.type),
callAuthRequest.args,
) as AbiDecoded[];

expect(decodedArgs).toHaveLength(4);
expect(decodedArgs[0]).toEqual(adminAddress);
expect(decodedArgs[1]).toEqual({
a: structData.a.toBigInt(),
b: structData.b.toBigInt(),
c: structData.c.toBigInt(),
});
expect(decodedArgs[2]).toEqual(amount.toBigInt());
expect(decodedArgs[3]).toEqual(nonce.toBigInt());
});
});

describe('read request verification', () => {
let pendingNoteHashesContract: PendingNoteHashesContract;

Expand Down
4 changes: 4 additions & 0 deletions yarn-project/end-to-end/src/fixtures/authwit_proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ async function buildProxyCall(proxy: GenericProxyContract, action: ContractFunct
return proxy.methods.forward_private_3(call.to, call.selector, call.args);
} else if (argCount === 4) {
return proxy.methods.forward_private_4(call.to, call.selector, call.args);
} else if (argCount === 5) {
return proxy.methods.forward_private_5(call.to, call.selector, call.args);
} else if (argCount === 6) {
return proxy.methods.forward_private_6(call.to, call.selector, call.args);
}
throw new Error(`No forward_private_${argCount} method on proxy`);
}
Expand Down
Loading