@aztec/end-to-end 5.0.0-nightly.20260708 → 5.0.0-nightly.20260709

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.
@@ -0,0 +1,48 @@
1
+ import { AztecAddress, type CompleteAddress } from '@aztec/aztec.js/addresses';
2
+ import { type SchnorrSignature } from '@aztec/foundation/crypto/schnorr';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
+ import type { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
5
+ import type { CustomRequest } from '@aztec/pxe/config';
6
+ import { type PublicKeys } from '@aztec/stdlib/keys';
7
+ /**
8
+ * The decoded payload of the registry's interactive-handshake signature request. Note it never carries the sender,
9
+ * so the recipient authorizes the handshake without learning who initiated it.
10
+ */
11
+ export type InteractiveHandshakeRequest = {
12
+ /** The account whose authorization is being requested. */
13
+ recipient: AztecAddress;
14
+ chainId: Fr;
15
+ version: Fr;
16
+ /** The x-coordinate of the handshake's ephemeral public key (its y-coordinate is positive by construction). */
17
+ ephPkX: Fr;
18
+ };
19
+ /**
20
+ * The recipient's signed authorization for an interactive handshake. Mirrors the registry contract's
21
+ * `RecipientSignature` struct field for field.
22
+ */
23
+ export type RecipientSignature = {
24
+ /** The recipient's master public keys, bound in-circuit to the recipient's address via `partialAddress`. */
25
+ publicKeys: PublicKeys;
26
+ partialAddress: Fr;
27
+ /** The x-coordinate of the recipient's master message-signing public key. */
28
+ mspkX: Fr;
29
+ /** Whether that key's y-coordinate is positive, so the circuit can reconstruct the point from `mspkX`. */
30
+ mspkYIsPositive: boolean;
31
+ /** The schnorr signature over the handshake message. */
32
+ signature: SchnorrSignature;
33
+ };
34
+ /**
35
+ * Parses and validates the registry's interactive-handshake signature request.
36
+ *
37
+ * @throws If the request kind is not {@link INTERACTIVE_HANDSHAKE_REQUEST_KIND}, the issuing contract is not the
38
+ * standard HandshakeRegistry, or the payload does not have the expected shape.
39
+ */
40
+ export declare function parseInteractiveHandshakeRequest(request: CustomRequest): InteractiveHandshakeRequest;
41
+ /**
42
+ * Produces the recipient's signed authorization for an interactive handshake, signing with the master
43
+ * message-signing secret key.
44
+ */
45
+ export declare function signInteractiveHandshake(request: InteractiveHandshakeRequest, completeAddress: CompleteAddress, masterMessageSigningSecretKey: GrumpkinScalar): Promise<RecipientSignature>;
46
+ /** Serializes a {@link RecipientSignature} to the field layout the registry's in-circuit deserialization expects. */
47
+ export declare function recipientSignatureToFields(recipientSignature: RecipientSignature): Fr[];
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJhY3RpdmVfaGFuZHNoYWtlX3Jlc3BvbmRlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2F1dG9taW5lL2RlbGl2ZXJ5L2ludGVyYWN0aXZlX2hhbmRzaGFrZV9yZXNwb25kZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxLQUFLLGVBQWUsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRy9FLE9BQU8sRUFBVyxLQUFLLGdCQUFnQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDbEYsT0FBTyxFQUFNLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3hELE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3hFLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBS3ZELE9BQU8sRUFBRSxLQUFLLFVBQVUsRUFBZ0MsTUFBTSxvQkFBb0IsQ0FBQztBQUVuRjs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sMkJBQTJCLEdBQUc7SUFDeEMsMERBQTBEO0lBQzFELFNBQVMsRUFBRSxZQUFZLENBQUM7SUFDeEIsT0FBTyxFQUFFLEVBQUUsQ0FBQztJQUNaLE9BQU8sRUFBRSxFQUFFLENBQUM7SUFDWiwrR0FBK0c7SUFDL0csTUFBTSxFQUFFLEVBQUUsQ0FBQztDQUNaLENBQUM7QUFFRjs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sa0JBQWtCLEdBQUc7SUFDL0IsNEdBQTRHO0lBQzVHLFVBQVUsRUFBRSxVQUFVLENBQUM7SUFDdkIsY0FBYyxFQUFFLEVBQUUsQ0FBQztJQUNuQiw2RUFBNkU7SUFDN0UsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUNWLDBHQUEwRztJQUMxRyxlQUFlLEVBQUUsT0FBTyxDQUFDO0lBQ3pCLHdEQUF3RDtJQUN4RCxTQUFTLEVBQUUsZ0JBQWdCLENBQUM7Q0FDN0IsQ0FBQztBQUVGOzs7OztHQUtHO0FBQ0gsd0JBQWdCLGdDQUFnQyxDQUFDLE9BQU8sRUFBRSxhQUFhLEdBQUcsMkJBQTJCLENBZXBHO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQXNCLHdCQUF3QixDQUM1QyxPQUFPLEVBQUUsMkJBQTJCLEVBQ3BDLGVBQWUsRUFBRSxlQUFlLEVBQ2hDLDZCQUE2QixFQUFFLGNBQWMsR0FDNUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBbUI3QjtBQUVELHFIQUFxSDtBQUNySCx3QkFBZ0IsMEJBQTBCLENBQUMsa0JBQWtCLEVBQUUsa0JBQWtCLEdBQUcsRUFBRSxFQUFFLENBYXZGIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive_handshake_responder.d.ts","sourceRoot":"","sources":["../../../src/automine/delivery/interactive_handshake_responder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG/E,OAAO,EAAW,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAClF,OAAO,EAAM,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKvD,OAAO,EAAE,KAAK,UAAU,EAAgC,MAAM,oBAAoB,CAAC;AAEnF;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,0DAA0D;IAC1D,SAAS,EAAE,YAAY,CAAC;IACxB,OAAO,EAAE,EAAE,CAAC;IACZ,OAAO,EAAE,EAAE,CAAC;IACZ,+GAA+G;IAC/G,MAAM,EAAE,EAAE,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,4GAA4G;IAC5G,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,EAAE,CAAC;IACnB,6EAA6E;IAC7E,KAAK,EAAE,EAAE,CAAC;IACV,0GAA0G;IAC1G,eAAe,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,SAAS,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,aAAa,GAAG,2BAA2B,CAepG;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,2BAA2B,EACpC,eAAe,EAAE,eAAe,EAChC,6BAA6B,EAAE,cAAc,GAC5C,OAAO,CAAC,kBAAkB,CAAC,CAmB7B;AAED,qHAAqH;AACrH,wBAAgB,0BAA0B,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,EAAE,EAAE,CAavF"}
@@ -0,0 +1,76 @@
1
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
2
+ import { DomainSeparator } from '@aztec/constants';
3
+ import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
4
+ import { Schnorr } from '@aztec/foundation/crypto/schnorr';
5
+ import { Fq, Fr } from '@aztec/foundation/curves/bn254';
6
+ import { INTERACTIVE_HANDSHAKE_REQUEST_KIND, STANDARD_HANDSHAKE_REGISTRY_ADDRESS } from '@aztec/standard-contracts/handshake-registry/constants';
7
+ import { derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
8
+ /**
9
+ * Parses and validates the registry's interactive-handshake signature request.
10
+ *
11
+ * @throws If the request kind is not {@link INTERACTIVE_HANDSHAKE_REQUEST_KIND}, the issuing contract is not the
12
+ * standard HandshakeRegistry, or the payload does not have the expected shape.
13
+ */ export function parseInteractiveHandshakeRequest(request) {
14
+ if (!request.kind.equals(INTERACTIVE_HANDSHAKE_REQUEST_KIND)) {
15
+ throw new Error(`Not an interactive-handshake signature request: unexpected kind ${request.kind}`);
16
+ }
17
+ if (!request.contractAddress.equals(STANDARD_HANDSHAKE_REGISTRY_ADDRESS)) {
18
+ throw new Error(`Interactive-handshake signature request issued by ${request.contractAddress}, expected the standard HandshakeRegistry at ${STANDARD_HANDSHAKE_REGISTRY_ADDRESS}`);
19
+ }
20
+ if (request.payload.length !== 4) {
21
+ throw new Error(`Interactive-handshake signature request payload has ${request.payload.length} fields, expected 4`);
22
+ }
23
+ const [recipient, chainId, version, ephPkX] = request.payload;
24
+ return {
25
+ recipient: new AztecAddress(recipient),
26
+ chainId,
27
+ version,
28
+ ephPkX
29
+ };
30
+ }
31
+ /**
32
+ * Produces the recipient's signed authorization for an interactive handshake, signing with the master
33
+ * message-signing secret key.
34
+ */ export async function signInteractiveHandshake(request, completeAddress, masterMessageSigningSecretKey) {
35
+ const mspk = await derivePublicKeyFromSecretKey(masterMessageSigningSecretKey);
36
+ const [mspkX, mspkYIsPositive] = mspk.toXAndSign();
37
+ const message = await computeInteractiveHandshakeSignatureMessage({
38
+ chainId: request.chainId,
39
+ version: request.version,
40
+ registry: STANDARD_HANDSHAKE_REGISTRY_ADDRESS,
41
+ ephPkX: request.ephPkX
42
+ });
43
+ const signature = await new Schnorr().constructSignature(message, masterMessageSigningSecretKey);
44
+ return {
45
+ publicKeys: completeAddress.publicKeys,
46
+ partialAddress: completeAddress.partialAddress,
47
+ mspkX,
48
+ mspkYIsPositive,
49
+ signature
50
+ };
51
+ }
52
+ /** Serializes a {@link RecipientSignature} to the field layout the registry's in-circuit deserialization expects. */ export function recipientSignatureToFields(recipientSignature) {
53
+ const s = Fq.fromBuffer(recipientSignature.signature.s);
54
+ const e = Fq.fromBuffer(recipientSignature.signature.e);
55
+ return [
56
+ ...recipientSignature.publicKeys.toFields(),
57
+ recipientSignature.partialAddress,
58
+ recipientSignature.mspkX,
59
+ new Fr(recipientSignature.mspkYIsPositive),
60
+ s.lo,
61
+ s.hi,
62
+ e.lo,
63
+ e.hi
64
+ ];
65
+ }
66
+ /**
67
+ * The message an interactive-handshake authorization signs: the handshake's ephemeral key and chain context under
68
+ * `DomainSeparator.INTERACTIVE_HANDSHAKE_SIGNATURE`, exactly as the registry recomputes it in-circuit.
69
+ */ function computeInteractiveHandshakeSignatureMessage(args) {
70
+ return poseidon2HashWithSeparator([
71
+ args.chainId,
72
+ args.version,
73
+ args.registry,
74
+ args.ephPkX
75
+ ], DomainSeparator.INTERACTIVE_HANDSHAKE_SIGNATURE);
76
+ }
@@ -1,7 +1,8 @@
1
- import type { AztecAddress } from '@aztec/aztec.js/addresses';
2
- import type { PXECreationOptions } from '@aztec/pxe/server';
1
+ import { type InitialAccountData } from '@aztec/accounts/testing';
2
+ import type { AztecAddress, CompleteAddress } from '@aztec/aztec.js/addresses';
3
+ import type { ResolveCustomRequest, ResolveTaggingSecretStrategy } from '@aztec/pxe/config';
3
4
  import { TestWallet } from '../../test-wallet/test_wallet.js';
4
- export type SenderHook = NonNullable<NonNullable<PXECreationOptions['hooks']>['resolveTaggingSecretStrategy']>;
5
+ export type CustomRequestResponder = (recipient: TestWallet, recipientAccount: InitialAccountData, recipientCompleteAddress: CompleteAddress) => ResolveCustomRequest;
5
6
  export type Mode = 'constrained' | 'unconstrained';
6
7
  export type DeliveryMode = Mode | {
7
8
  events: Mode;
@@ -10,8 +11,9 @@ export type DeliveryMode = Mode | {
10
11
  export declare function buildMessageDeliveryTest(opts: {
11
12
  strategy: string;
12
13
  mode: DeliveryMode;
13
- senderHook: SenderHook;
14
+ senderHook: ResolveTaggingSecretStrategy;
14
15
  recipientRegistration?: (recipient: TestWallet, recipientAddress: AztecAddress, sender: AztecAddress) => Promise<void>;
16
+ customRequestResponder?: CustomRequestResponder;
15
17
  additionalTests?: () => void;
16
18
  }): void;
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib25jaGFpbl9kZWxpdmVyeV9oYXJuZXNzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXV0b21pbmUvZGVsaXZlcnkvb25jaGFpbl9kZWxpdmVyeV9oYXJuZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBSTlELE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFPNUQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBSTlELE1BQU0sTUFBTSxVQUFVLEdBQUcsV0FBVyxDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQztBQUUvRyxNQUFNLE1BQU0sSUFBSSxHQUFHLGFBQWEsR0FBRyxlQUFlLENBQUM7QUFJbkQsTUFBTSxNQUFNLFlBQVksR0FBRyxJQUFJLEdBQUc7SUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDO0lBQUMsS0FBSyxFQUFFLElBQUksQ0FBQTtDQUFFLENBQUM7QUFZaEUsd0JBQWdCLHdCQUF3QixDQUFDLElBQUksRUFBRTtJQUc3QyxRQUFRLEVBQUUsTUFBTSxDQUFDO0lBQ2pCLElBQUksRUFBRSxZQUFZLENBQUM7SUFFbkIsVUFBVSxFQUFFLFVBQVUsQ0FBQztJQUV2QixxQkFBcUIsQ0FBQyxFQUFFLENBQ3RCLFNBQVMsRUFBRSxVQUFVLEVBQ3JCLGdCQUFnQixFQUFFLFlBQVksRUFDOUIsTUFBTSxFQUFFLFlBQVksS0FDakIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBSW5CLGVBQWUsQ0FBQyxFQUFFLE1BQU0sSUFBSSxDQUFDO0NBQzlCLFFBMkhBIn0=
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib25jaGFpbl9kZWxpdmVyeV9oYXJuZXNzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXV0b21pbmUvZGVsaXZlcnkvb25jaGFpbl9kZWxpdmVyeV9oYXJuZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGtCQUFrQixFQUEyQixNQUFNLHlCQUF5QixDQUFDO0FBRzNGLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxlQUFlLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUsvRSxPQUFPLEtBQUssRUFBaUIsb0JBQW9CLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQU8zRyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFJOUQsTUFBTSxNQUFNLHNCQUFzQixHQUFHLENBQ25DLFNBQVMsRUFBRSxVQUFVLEVBQ3JCLGdCQUFnQixFQUFFLGtCQUFrQixFQUNwQyx3QkFBd0IsRUFBRSxlQUFlLEtBQ3RDLG9CQUFvQixDQUFDO0FBRTFCLE1BQU0sTUFBTSxJQUFJLEdBQUcsYUFBYSxHQUFHLGVBQWUsQ0FBQztBQUluRCxNQUFNLE1BQU0sWUFBWSxHQUFHLElBQUksR0FBRztJQUFFLE1BQU0sRUFBRSxJQUFJLENBQUM7SUFBQyxLQUFLLEVBQUUsSUFBSSxDQUFBO0NBQUUsQ0FBQztBQVloRSx3QkFBZ0Isd0JBQXdCLENBQUMsSUFBSSxFQUFFO0lBRzdDLFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsSUFBSSxFQUFFLFlBQVksQ0FBQztJQUVuQixVQUFVLEVBQUUsNEJBQTRCLENBQUM7SUFFekMscUJBQXFCLENBQUMsRUFBRSxDQUN0QixTQUFTLEVBQUUsVUFBVSxFQUNyQixnQkFBZ0IsRUFBRSxZQUFZLEVBQzlCLE1BQU0sRUFBRSxZQUFZLEtBQ2pCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUduQixzQkFBc0IsQ0FBQyxFQUFFLHNCQUFzQixDQUFDO0lBSWhELGVBQWUsQ0FBQyxFQUFFLE1BQU0sSUFBSSxDQUFDO0NBQzlCLFFBdUpBIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"onchain_delivery_harness.d.ts","sourceRoot":"","sources":["../../../src/automine/delivery/onchain_delivery_harness.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAI9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAO5D,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAI9D,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC;AAE/G,MAAM,MAAM,IAAI,GAAG,aAAa,GAAG,eAAe,CAAC;AAInD,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,CAAC;AAYhE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAG7C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IAEnB,UAAU,EAAE,UAAU,CAAC;IAEvB,qBAAqB,CAAC,EAAE,CACtB,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,YAAY,EAC9B,MAAM,EAAE,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CAAC;IAInB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B,QA2HA"}
1
+ {"version":3,"file":"onchain_delivery_harness.d.ts","sourceRoot":"","sources":["../../../src/automine/delivery/onchain_delivery_harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAA2B,MAAM,yBAAyB,CAAC;AAG3F,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAK/E,OAAO,KAAK,EAAiB,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AAO3G,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAI9D,MAAM,MAAM,sBAAsB,GAAG,CACnC,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,kBAAkB,EACpC,wBAAwB,EAAE,eAAe,KACtC,oBAAoB,CAAC;AAE1B,MAAM,MAAM,IAAI,GAAG,aAAa,GAAG,eAAe,CAAC;AAInD,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,CAAC;AAYhE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAG7C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IAEnB,UAAU,EAAE,4BAA4B,CAAC;IAEzC,qBAAqB,CAAC,EAAE,CACtB,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,YAAY,EAC9B,MAAM,EAAE,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CAAC;IAGnB,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAIhD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B,QAuJA"}
@@ -15,7 +15,7 @@ const formatMode = (mode)=>typeof mode === 'string' ? mode : `${mode.events}->${
15
15
  // Cross-PXE is the meaningful setup: the recipient PXE holds no sender state, so a cell only "discovers" a message if
16
16
  // the source truly reached it.
17
17
  export function buildMessageDeliveryTest(opts) {
18
- const { strategy, mode, senderHook, recipientRegistration, additionalTests } = opts;
18
+ const { strategy, mode, senderHook, recipientRegistration, customRequestResponder, additionalTests } = opts;
19
19
  const description = `${strategy} x ${formatMode(mode)}`;
20
20
  describe(description, ()=>{
21
21
  jest.setTimeout(300_000);
@@ -47,22 +47,35 @@ export function buildMessageDeliveryTest(opts) {
47
47
  } : mode;
48
48
  const sendEvent = (value)=>eventMode === 'constrained' ? contractSender.methods.emit_event(recipient, value) : contractSender.methods.emit_event_unconstrained(recipient, value);
49
49
  const sendNote = (value)=>noteMode === 'constrained' ? contractSender.methods.emit_note(recipient, value) : contractSender.methods.emit_note_unconstrained(recipient, value);
50
+ let additionallyFundedAccounts;
51
+ let recipientAccount;
52
+ let customRequestCount = 0;
50
53
  beforeAll(async ()=>{
51
54
  // The sender PXE holds the sender and carries this cell's tagging-secret-strategy hook. The recipient is funded
52
55
  // at genesis here but created and deployed on the isolated recipient PXE below, so it carries no sender state
53
56
  // from other cells.
54
- let additionallyFundedAccounts;
55
57
  ({ aztecNode, additionallyFundedAccounts, wallet: walletSender, accounts: [sender], teardown: teardownSender } = await setup(1, {
56
58
  ...AUTOMINE_E2E_OPTS,
57
59
  additionallyFundedAccounts: await generateSchnorrAccounts(1, 'schnorr'),
58
60
  pxeCreationOptions: {
59
61
  hooks: {
60
- resolveTaggingSecretStrategy: senderHook
62
+ resolveTaggingSecretStrategy: senderHook,
63
+ resolveCustomRequest: async (request)=>{
64
+ if (!customRequestResponder) {
65
+ throw new Error('A custom request arrived but this test cell has no customRequestResponder configured');
66
+ }
67
+ if (!recipientAccount) {
68
+ throw new Error('A custom request arrived before the recipient wallet was created');
69
+ }
70
+ customRequestCount++;
71
+ const respond = customRequestResponder(walletRecipient, additionallyFundedAccounts[0], await recipientAccount.getCompleteAddress());
72
+ return respond(request);
73
+ }
61
74
  }
62
75
  }
63
76
  }));
64
77
  ({ wallet: walletRecipient, teardown: teardownRecipient } = await setupPXEAndGetWallet(aztecNode, aztecNode, {}, undefined, 'pxe-recipient'));
65
- const recipientAccount = await walletRecipient.createSchnorrAccount(additionallyFundedAccounts[0].secret, additionallyFundedAccounts[0].salt, additionallyFundedAccounts[0].signingKey);
78
+ recipientAccount = await walletRecipient.createSchnorrAccount(additionallyFundedAccounts[0].secret, additionallyFundedAccounts[0].salt, additionallyFundedAccounts[0].signingKey);
66
79
  await (await recipientAccount.getDeployMethod()).send({
67
80
  from: NO_FROM
68
81
  });
@@ -118,6 +131,11 @@ export function buildMessageDeliveryTest(opts) {
118
131
  it('the recipient PXE reads back the notes delivered by the sender PXE', ()=>{
119
132
  expect(readNotes).toEqual(noteValues);
120
133
  });
134
+ if (customRequestResponder) {
135
+ it('the custom request hook fires exactly once, on the send that bootstraps the tagging secret', ()=>{
136
+ expect(customRequestCount).toBe(1);
137
+ });
138
+ }
121
139
  additionalTests?.();
122
140
  });
123
141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/end-to-end",
3
- "version": "5.0.0-nightly.20260708",
3
+ "version": "5.0.0-nightly.20260709",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -29,45 +29,45 @@
29
29
  "formatting": "run -T prettier --check ./src && run -T eslint ./src"
30
30
  },
31
31
  "dependencies": {
32
- "@aztec/accounts": "5.0.0-nightly.20260708",
33
- "@aztec/archiver": "5.0.0-nightly.20260708",
34
- "@aztec/aztec": "5.0.0-nightly.20260708",
35
- "@aztec/aztec-node": "5.0.0-nightly.20260708",
36
- "@aztec/aztec.js": "5.0.0-nightly.20260708",
37
- "@aztec/bb-prover": "5.0.0-nightly.20260708",
38
- "@aztec/bb.js": "5.0.0-nightly.20260708",
39
- "@aztec/blob-client": "5.0.0-nightly.20260708",
40
- "@aztec/blob-lib": "5.0.0-nightly.20260708",
41
- "@aztec/bot": "5.0.0-nightly.20260708",
42
- "@aztec/cli": "5.0.0-nightly.20260708",
43
- "@aztec/constants": "5.0.0-nightly.20260708",
44
- "@aztec/entrypoints": "5.0.0-nightly.20260708",
45
- "@aztec/epoch-cache": "5.0.0-nightly.20260708",
46
- "@aztec/ethereum": "5.0.0-nightly.20260708",
47
- "@aztec/foundation": "5.0.0-nightly.20260708",
48
- "@aztec/kv-store": "5.0.0-nightly.20260708",
49
- "@aztec/l1-artifacts": "5.0.0-nightly.20260708",
50
- "@aztec/node-keystore": "5.0.0-nightly.20260708",
51
- "@aztec/noir-contracts.js": "5.0.0-nightly.20260708",
52
- "@aztec/noir-noirc_abi": "5.0.0-nightly.20260708",
53
- "@aztec/noir-protocol-circuits-types": "5.0.0-nightly.20260708",
54
- "@aztec/noir-test-contracts.js": "5.0.0-nightly.20260708",
55
- "@aztec/p2p": "5.0.0-nightly.20260708",
56
- "@aztec/protocol-contracts": "5.0.0-nightly.20260708",
57
- "@aztec/prover-client": "5.0.0-nightly.20260708",
58
- "@aztec/prover-node": "5.0.0-nightly.20260708",
59
- "@aztec/pxe": "5.0.0-nightly.20260708",
60
- "@aztec/sequencer-client": "5.0.0-nightly.20260708",
61
- "@aztec/simulator": "5.0.0-nightly.20260708",
62
- "@aztec/slasher": "5.0.0-nightly.20260708",
63
- "@aztec/standard-contracts": "5.0.0-nightly.20260708",
64
- "@aztec/stdlib": "5.0.0-nightly.20260708",
65
- "@aztec/telemetry-client": "5.0.0-nightly.20260708",
66
- "@aztec/validator-client": "5.0.0-nightly.20260708",
67
- "@aztec/validator-ha-signer": "5.0.0-nightly.20260708",
68
- "@aztec/wallet-sdk": "5.0.0-nightly.20260708",
69
- "@aztec/wallets": "5.0.0-nightly.20260708",
70
- "@aztec/world-state": "5.0.0-nightly.20260708",
32
+ "@aztec/accounts": "5.0.0-nightly.20260709",
33
+ "@aztec/archiver": "5.0.0-nightly.20260709",
34
+ "@aztec/aztec": "5.0.0-nightly.20260709",
35
+ "@aztec/aztec-node": "5.0.0-nightly.20260709",
36
+ "@aztec/aztec.js": "5.0.0-nightly.20260709",
37
+ "@aztec/bb-prover": "5.0.0-nightly.20260709",
38
+ "@aztec/bb.js": "5.0.0-nightly.20260709",
39
+ "@aztec/blob-client": "5.0.0-nightly.20260709",
40
+ "@aztec/blob-lib": "5.0.0-nightly.20260709",
41
+ "@aztec/bot": "5.0.0-nightly.20260709",
42
+ "@aztec/cli": "5.0.0-nightly.20260709",
43
+ "@aztec/constants": "5.0.0-nightly.20260709",
44
+ "@aztec/entrypoints": "5.0.0-nightly.20260709",
45
+ "@aztec/epoch-cache": "5.0.0-nightly.20260709",
46
+ "@aztec/ethereum": "5.0.0-nightly.20260709",
47
+ "@aztec/foundation": "5.0.0-nightly.20260709",
48
+ "@aztec/kv-store": "5.0.0-nightly.20260709",
49
+ "@aztec/l1-artifacts": "5.0.0-nightly.20260709",
50
+ "@aztec/node-keystore": "5.0.0-nightly.20260709",
51
+ "@aztec/noir-contracts.js": "5.0.0-nightly.20260709",
52
+ "@aztec/noir-noirc_abi": "5.0.0-nightly.20260709",
53
+ "@aztec/noir-protocol-circuits-types": "5.0.0-nightly.20260709",
54
+ "@aztec/noir-test-contracts.js": "5.0.0-nightly.20260709",
55
+ "@aztec/p2p": "5.0.0-nightly.20260709",
56
+ "@aztec/protocol-contracts": "5.0.0-nightly.20260709",
57
+ "@aztec/prover-client": "5.0.0-nightly.20260709",
58
+ "@aztec/prover-node": "5.0.0-nightly.20260709",
59
+ "@aztec/pxe": "5.0.0-nightly.20260709",
60
+ "@aztec/sequencer-client": "5.0.0-nightly.20260709",
61
+ "@aztec/simulator": "5.0.0-nightly.20260709",
62
+ "@aztec/slasher": "5.0.0-nightly.20260709",
63
+ "@aztec/standard-contracts": "5.0.0-nightly.20260709",
64
+ "@aztec/stdlib": "5.0.0-nightly.20260709",
65
+ "@aztec/telemetry-client": "5.0.0-nightly.20260709",
66
+ "@aztec/validator-client": "5.0.0-nightly.20260709",
67
+ "@aztec/validator-ha-signer": "5.0.0-nightly.20260709",
68
+ "@aztec/wallet-sdk": "5.0.0-nightly.20260709",
69
+ "@aztec/wallets": "5.0.0-nightly.20260709",
70
+ "@aztec/world-state": "5.0.0-nightly.20260709",
71
71
  "@iarna/toml": "^2.2.5",
72
72
  "@jest/globals": "^30.0.0",
73
73
  "@noble/curves": "=1.0.0",
@@ -0,0 +1,125 @@
1
+ import { AztecAddress, type CompleteAddress } from '@aztec/aztec.js/addresses';
2
+ import { DomainSeparator } from '@aztec/constants';
3
+ import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
4
+ import { Schnorr, type SchnorrSignature } from '@aztec/foundation/crypto/schnorr';
5
+ import { Fq, Fr } from '@aztec/foundation/curves/bn254';
6
+ import type { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
7
+ import type { CustomRequest } from '@aztec/pxe/config';
8
+ import {
9
+ INTERACTIVE_HANDSHAKE_REQUEST_KIND,
10
+ STANDARD_HANDSHAKE_REGISTRY_ADDRESS,
11
+ } from '@aztec/standard-contracts/handshake-registry/constants';
12
+ import { type PublicKeys, derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
13
+
14
+ /**
15
+ * The decoded payload of the registry's interactive-handshake signature request. Note it never carries the sender,
16
+ * so the recipient authorizes the handshake without learning who initiated it.
17
+ */
18
+ export type InteractiveHandshakeRequest = {
19
+ /** The account whose authorization is being requested. */
20
+ recipient: AztecAddress;
21
+ chainId: Fr;
22
+ version: Fr;
23
+ /** The x-coordinate of the handshake's ephemeral public key (its y-coordinate is positive by construction). */
24
+ ephPkX: Fr;
25
+ };
26
+
27
+ /**
28
+ * The recipient's signed authorization for an interactive handshake. Mirrors the registry contract's
29
+ * `RecipientSignature` struct field for field.
30
+ */
31
+ export type RecipientSignature = {
32
+ /** The recipient's master public keys, bound in-circuit to the recipient's address via `partialAddress`. */
33
+ publicKeys: PublicKeys;
34
+ partialAddress: Fr;
35
+ /** The x-coordinate of the recipient's master message-signing public key. */
36
+ mspkX: Fr;
37
+ /** Whether that key's y-coordinate is positive, so the circuit can reconstruct the point from `mspkX`. */
38
+ mspkYIsPositive: boolean;
39
+ /** The schnorr signature over the handshake message. */
40
+ signature: SchnorrSignature;
41
+ };
42
+
43
+ /**
44
+ * Parses and validates the registry's interactive-handshake signature request.
45
+ *
46
+ * @throws If the request kind is not {@link INTERACTIVE_HANDSHAKE_REQUEST_KIND}, the issuing contract is not the
47
+ * standard HandshakeRegistry, or the payload does not have the expected shape.
48
+ */
49
+ export function parseInteractiveHandshakeRequest(request: CustomRequest): InteractiveHandshakeRequest {
50
+ if (!request.kind.equals(INTERACTIVE_HANDSHAKE_REQUEST_KIND)) {
51
+ throw new Error(`Not an interactive-handshake signature request: unexpected kind ${request.kind}`);
52
+ }
53
+ if (!request.contractAddress.equals(STANDARD_HANDSHAKE_REGISTRY_ADDRESS)) {
54
+ throw new Error(
55
+ `Interactive-handshake signature request issued by ${request.contractAddress}, expected the standard HandshakeRegistry at ${STANDARD_HANDSHAKE_REGISTRY_ADDRESS}`,
56
+ );
57
+ }
58
+ if (request.payload.length !== 4) {
59
+ throw new Error(`Interactive-handshake signature request payload has ${request.payload.length} fields, expected 4`);
60
+ }
61
+
62
+ const [recipient, chainId, version, ephPkX] = request.payload;
63
+ return { recipient: new AztecAddress(recipient), chainId, version, ephPkX };
64
+ }
65
+
66
+ /**
67
+ * Produces the recipient's signed authorization for an interactive handshake, signing with the master
68
+ * message-signing secret key.
69
+ */
70
+ export async function signInteractiveHandshake(
71
+ request: InteractiveHandshakeRequest,
72
+ completeAddress: CompleteAddress,
73
+ masterMessageSigningSecretKey: GrumpkinScalar,
74
+ ): Promise<RecipientSignature> {
75
+ const mspk = await derivePublicKeyFromSecretKey(masterMessageSigningSecretKey);
76
+ const [mspkX, mspkYIsPositive] = mspk.toXAndSign();
77
+
78
+ const message = await computeInteractiveHandshakeSignatureMessage({
79
+ chainId: request.chainId,
80
+ version: request.version,
81
+ registry: STANDARD_HANDSHAKE_REGISTRY_ADDRESS,
82
+ ephPkX: request.ephPkX,
83
+ });
84
+ const signature = await new Schnorr().constructSignature(message, masterMessageSigningSecretKey);
85
+
86
+ return {
87
+ publicKeys: completeAddress.publicKeys,
88
+ partialAddress: completeAddress.partialAddress,
89
+ mspkX,
90
+ mspkYIsPositive,
91
+ signature,
92
+ };
93
+ }
94
+
95
+ /** Serializes a {@link RecipientSignature} to the field layout the registry's in-circuit deserialization expects. */
96
+ export function recipientSignatureToFields(recipientSignature: RecipientSignature): Fr[] {
97
+ const s = Fq.fromBuffer(recipientSignature.signature.s);
98
+ const e = Fq.fromBuffer(recipientSignature.signature.e);
99
+ return [
100
+ ...recipientSignature.publicKeys.toFields(),
101
+ recipientSignature.partialAddress,
102
+ recipientSignature.mspkX,
103
+ new Fr(recipientSignature.mspkYIsPositive),
104
+ s.lo,
105
+ s.hi,
106
+ e.lo,
107
+ e.hi,
108
+ ];
109
+ }
110
+
111
+ /**
112
+ * The message an interactive-handshake authorization signs: the handshake's ephemeral key and chain context under
113
+ * `DomainSeparator.INTERACTIVE_HANDSHAKE_SIGNATURE`, exactly as the registry recomputes it in-circuit.
114
+ */
115
+ function computeInteractiveHandshakeSignatureMessage(args: {
116
+ chainId: Fr;
117
+ version: Fr;
118
+ registry: AztecAddress;
119
+ ephPkX: Fr;
120
+ }): Promise<Fr> {
121
+ return poseidon2HashWithSeparator(
122
+ [args.chainId, args.version, args.registry, args.ephPkX],
123
+ DomainSeparator.INTERACTIVE_HANDSHAKE_SIGNATURE,
124
+ );
125
+ }
@@ -1,11 +1,12 @@
1
1
  import { type InitialAccountData, generateSchnorrAccounts } from '@aztec/accounts/testing';
2
2
  import type { FieldLike } from '@aztec/aztec.js/abi';
3
3
  import { NO_FROM } from '@aztec/aztec.js/account';
4
- import type { AztecAddress } from '@aztec/aztec.js/addresses';
4
+ import type { AztecAddress, CompleteAddress } from '@aztec/aztec.js/addresses';
5
5
  import type { AztecNode } from '@aztec/aztec.js/node';
6
+ import type { AccountManager } from '@aztec/aztec.js/wallet';
6
7
  import { BlockNumber } from '@aztec/foundation/branded-types';
7
8
  import { type DeliveryEvent, OnchainDeliveryTestContract } from '@aztec/noir-test-contracts.js/OnchainDeliveryTest';
8
- import type { PXECreationOptions } from '@aztec/pxe/server';
9
+ import type { CustomRequest, ResolveCustomRequest, ResolveTaggingSecretStrategy } from '@aztec/pxe/config';
9
10
  import type { AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
10
11
 
11
12
  import { jest } from '@jest/globals';
@@ -14,9 +15,13 @@ import { AUTOMINE_E2E_OPTS } from '../../fixtures/fixtures.js';
14
15
  import { ensureHandshakeRegistryPublished, setup, setupPXEAndGetWallet } from '../../fixtures/setup.js';
15
16
  import { TestWallet } from '../../test-wallet/test_wallet.js';
16
17
 
17
- // The wallet hook that selects a message's tagging-secret source. Derived from the exported PXE options
18
- // rather than importing the hook type, which `@aztec/pxe/server` does not re-export.
19
- export type SenderHook = NonNullable<NonNullable<PXECreationOptions['hooks']>['resolveTaggingSecretStrategy']>;
18
+ // Builds the hook serving custom requests issued during the sender's simulations. Installed on the sender PXE at
19
+ // creation but built only once the recipient exists, since serving typically needs the recipient's wallet and keys.
20
+ export type CustomRequestResponder = (
21
+ recipient: TestWallet,
22
+ recipientAccount: InitialAccountData,
23
+ recipientCompleteAddress: CompleteAddress,
24
+ ) => ResolveCustomRequest;
20
25
 
21
26
  export type Mode = 'constrained' | 'unconstrained';
22
27
 
@@ -40,19 +45,22 @@ export function buildMessageDeliveryTest(opts: {
40
45
  strategy: string;
41
46
  mode: DeliveryMode;
42
47
  // Required: every cell states its source explicitly rather than leaning on the PXE default (covered by unit tests).
43
- senderHook: SenderHook;
48
+ senderHook: ResolveTaggingSecretStrategy;
44
49
  // Recipient-side setup the source requires (e.g. registering a raw arbitrary secret); runs once after deployment.
45
50
  recipientRegistration?: (
46
51
  recipient: TestWallet,
47
52
  recipientAddress: AztecAddress,
48
53
  sender: AztecAddress,
49
54
  ) => Promise<void>;
55
+ // Serves the custom requests issued during the sender's simulations (e.g. the registry's interactive-handshake
56
+ // signature request).
57
+ customRequestResponder?: CustomRequestResponder;
50
58
  // Extra `it()`s to register in this cell's suite, e.g. assertions against state a custom `senderHook` recorded.
51
59
  // Called inside the same `describe`, after the two baseline assertions below, so it shares their `beforeAll`
52
60
  // instead of depending on Jest's cross-`describe` execution order.
53
61
  additionalTests?: () => void;
54
62
  }) {
55
- const { strategy, mode, senderHook, recipientRegistration, additionalTests } = opts;
63
+ const { strategy, mode, senderHook, recipientRegistration, customRequestResponder, additionalTests } = opts;
56
64
  const description = `${strategy} x ${formatMode(mode)}`;
57
65
 
58
66
  describe(description, () => {
@@ -84,11 +92,14 @@ export function buildMessageDeliveryTest(opts: {
84
92
  ? contractSender.methods.emit_note(recipient, value)
85
93
  : contractSender.methods.emit_note_unconstrained(recipient, value);
86
94
 
95
+ let additionallyFundedAccounts: InitialAccountData[];
96
+ let recipientAccount: AccountManager | undefined;
97
+ let customRequestCount = 0;
98
+
87
99
  beforeAll(async () => {
88
100
  // The sender PXE holds the sender and carries this cell's tagging-secret-strategy hook. The recipient is funded
89
101
  // at genesis here but created and deployed on the isolated recipient PXE below, so it carries no sender state
90
102
  // from other cells.
91
- let additionallyFundedAccounts: InitialAccountData[];
92
103
  ({
93
104
  aztecNode,
94
105
  additionallyFundedAccounts,
@@ -98,7 +109,26 @@ export function buildMessageDeliveryTest(opts: {
98
109
  } = await setup(1, {
99
110
  ...AUTOMINE_E2E_OPTS,
100
111
  additionallyFundedAccounts: await generateSchnorrAccounts(1, 'schnorr'),
101
- pxeCreationOptions: { hooks: { resolveTaggingSecretStrategy: senderHook } },
112
+ pxeCreationOptions: {
113
+ hooks: {
114
+ resolveTaggingSecretStrategy: senderHook,
115
+ resolveCustomRequest: async (request: CustomRequest) => {
116
+ if (!customRequestResponder) {
117
+ throw new Error('A custom request arrived but this test cell has no customRequestResponder configured');
118
+ }
119
+ if (!recipientAccount) {
120
+ throw new Error('A custom request arrived before the recipient wallet was created');
121
+ }
122
+ customRequestCount++;
123
+ const respond = customRequestResponder(
124
+ walletRecipient,
125
+ additionallyFundedAccounts[0],
126
+ await recipientAccount.getCompleteAddress(),
127
+ );
128
+ return respond(request);
129
+ },
130
+ },
131
+ },
102
132
  }));
103
133
 
104
134
  ({ wallet: walletRecipient, teardown: teardownRecipient } = await setupPXEAndGetWallet(
@@ -108,7 +138,7 @@ export function buildMessageDeliveryTest(opts: {
108
138
  undefined,
109
139
  'pxe-recipient',
110
140
  ));
111
- const recipientAccount = await walletRecipient.createSchnorrAccount(
141
+ recipientAccount = await walletRecipient.createSchnorrAccount(
112
142
  additionallyFundedAccounts[0].secret,
113
143
  additionallyFundedAccounts[0].salt,
114
144
  additionallyFundedAccounts[0].signingKey,
@@ -172,6 +202,12 @@ export function buildMessageDeliveryTest(opts: {
172
202
  expect(readNotes).toEqual(noteValues);
173
203
  });
174
204
 
205
+ if (customRequestResponder) {
206
+ it('the custom request hook fires exactly once, on the send that bootstraps the tagging secret', () => {
207
+ expect(customRequestCount).toBe(1);
208
+ });
209
+ }
210
+
175
211
  additionalTests?.();
176
212
  });
177
213
  }