@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.
- package/dest/automine/delivery/interactive_handshake_responder.d.ts +48 -0
- package/dest/automine/delivery/interactive_handshake_responder.d.ts.map +1 -0
- package/dest/automine/delivery/interactive_handshake_responder.js +76 -0
- package/dest/automine/delivery/onchain_delivery_harness.d.ts +7 -5
- package/dest/automine/delivery/onchain_delivery_harness.d.ts.map +1 -1
- package/dest/automine/delivery/onchain_delivery_harness.js +22 -4
- package/package.json +40 -40
- package/src/automine/delivery/interactive_handshake_responder.ts +125 -0
- package/src/automine/delivery/onchain_delivery_harness.ts +46 -10
|
@@ -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
|
|
2
|
-
import type {
|
|
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
|
|
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:
|
|
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,
|
|
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":"
|
|
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
|
-
|
|
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.
|
|
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.
|
|
33
|
-
"@aztec/archiver": "5.0.0-nightly.
|
|
34
|
-
"@aztec/aztec": "5.0.0-nightly.
|
|
35
|
-
"@aztec/aztec-node": "5.0.0-nightly.
|
|
36
|
-
"@aztec/aztec.js": "5.0.0-nightly.
|
|
37
|
-
"@aztec/bb-prover": "5.0.0-nightly.
|
|
38
|
-
"@aztec/bb.js": "5.0.0-nightly.
|
|
39
|
-
"@aztec/blob-client": "5.0.0-nightly.
|
|
40
|
-
"@aztec/blob-lib": "5.0.0-nightly.
|
|
41
|
-
"@aztec/bot": "5.0.0-nightly.
|
|
42
|
-
"@aztec/cli": "5.0.0-nightly.
|
|
43
|
-
"@aztec/constants": "5.0.0-nightly.
|
|
44
|
-
"@aztec/entrypoints": "5.0.0-nightly.
|
|
45
|
-
"@aztec/epoch-cache": "5.0.0-nightly.
|
|
46
|
-
"@aztec/ethereum": "5.0.0-nightly.
|
|
47
|
-
"@aztec/foundation": "5.0.0-nightly.
|
|
48
|
-
"@aztec/kv-store": "5.0.0-nightly.
|
|
49
|
-
"@aztec/l1-artifacts": "5.0.0-nightly.
|
|
50
|
-
"@aztec/node-keystore": "5.0.0-nightly.
|
|
51
|
-
"@aztec/noir-contracts.js": "5.0.0-nightly.
|
|
52
|
-
"@aztec/noir-noirc_abi": "5.0.0-nightly.
|
|
53
|
-
"@aztec/noir-protocol-circuits-types": "5.0.0-nightly.
|
|
54
|
-
"@aztec/noir-test-contracts.js": "5.0.0-nightly.
|
|
55
|
-
"@aztec/p2p": "5.0.0-nightly.
|
|
56
|
-
"@aztec/protocol-contracts": "5.0.0-nightly.
|
|
57
|
-
"@aztec/prover-client": "5.0.0-nightly.
|
|
58
|
-
"@aztec/prover-node": "5.0.0-nightly.
|
|
59
|
-
"@aztec/pxe": "5.0.0-nightly.
|
|
60
|
-
"@aztec/sequencer-client": "5.0.0-nightly.
|
|
61
|
-
"@aztec/simulator": "5.0.0-nightly.
|
|
62
|
-
"@aztec/slasher": "5.0.0-nightly.
|
|
63
|
-
"@aztec/standard-contracts": "5.0.0-nightly.
|
|
64
|
-
"@aztec/stdlib": "5.0.0-nightly.
|
|
65
|
-
"@aztec/telemetry-client": "5.0.0-nightly.
|
|
66
|
-
"@aztec/validator-client": "5.0.0-nightly.
|
|
67
|
-
"@aztec/validator-ha-signer": "5.0.0-nightly.
|
|
68
|
-
"@aztec/wallet-sdk": "5.0.0-nightly.
|
|
69
|
-
"@aztec/wallets": "5.0.0-nightly.
|
|
70
|
-
"@aztec/world-state": "5.0.0-nightly.
|
|
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 {
|
|
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
|
-
//
|
|
18
|
-
//
|
|
19
|
-
export type
|
|
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:
|
|
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: {
|
|
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
|
-
|
|
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
|
}
|