@aztec-labs/stdlib 6.0.0-nightly.20260922 → 6.0.0-rc.1

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.
Files changed (44) hide show
  1. package/dest/block/proposal/committee_attestation.d.ts +1 -1
  2. package/dest/block/proposal/committee_attestation.d.ts.map +1 -1
  3. package/dest/block/proposal/committee_attestation.js +5 -9
  4. package/dest/block/test/l2_tips_store_test_suite.d.ts +1 -1
  5. package/dest/block/test/l2_tips_store_test_suite.d.ts.map +1 -1
  6. package/dest/block/test/l2_tips_store_test_suite.js +2 -2
  7. package/dest/block/validate_block_result.d.ts +2 -2
  8. package/dest/block/validate_block_result.d.ts.map +1 -1
  9. package/dest/block/validate_block_result.js +1 -4
  10. package/dest/checkpoint/checkpoint_data.d.ts +9 -1
  11. package/dest/checkpoint/checkpoint_data.d.ts.map +1 -1
  12. package/dest/checkpoint/checkpoint_data.js +3 -0
  13. package/dest/checkpoint/published_checkpoint.d.ts +22 -3
  14. package/dest/checkpoint/published_checkpoint.d.ts.map +1 -1
  15. package/dest/checkpoint/published_checkpoint.js +23 -5
  16. package/dest/hash/hash.d.ts +2 -2
  17. package/dest/hash/hash.d.ts.map +1 -1
  18. package/dest/hash/hash.js +2 -2
  19. package/dest/interfaces/proving-job.d.ts +70 -70
  20. package/dest/kernel/private_circuit_public_inputs.d.ts +11 -7
  21. package/dest/kernel/private_circuit_public_inputs.d.ts.map +1 -1
  22. package/dest/kernel/private_circuit_public_inputs.js +10 -8
  23. package/dest/kernel/private_context_inputs.d.ts +3 -3
  24. package/dest/kernel/private_context_inputs.d.ts.map +1 -1
  25. package/dest/kernel/private_context_inputs.js +4 -4
  26. package/dest/tests/factories.js +1 -1
  27. package/dest/tx/private_tx_constant_data.d.ts +9 -9
  28. package/dest/tx/private_tx_constant_data.d.ts.map +1 -1
  29. package/dest/tx/private_tx_constant_data.js +8 -8
  30. package/dest/tx/tx_request.d.ts +24 -4
  31. package/dest/tx/tx_request.d.ts.map +1 -1
  32. package/dest/tx/tx_request.js +25 -4
  33. package/package.json +8 -8
  34. package/src/block/proposal/committee_attestation.ts +5 -5
  35. package/src/block/test/l2_tips_store_test_suite.ts +7 -1
  36. package/src/block/validate_block_result.ts +4 -7
  37. package/src/checkpoint/checkpoint_data.ts +11 -0
  38. package/src/checkpoint/published_checkpoint.ts +26 -1
  39. package/src/hash/hash.ts +2 -2
  40. package/src/kernel/private_circuit_public_inputs.ts +8 -6
  41. package/src/kernel/private_context_inputs.ts +2 -2
  42. package/src/tests/factories.ts +1 -1
  43. package/src/tx/private_tx_constant_data.ts +6 -6
  44. package/src/tx/tx_request.ts +27 -5
@@ -16,7 +16,12 @@ export declare class TxRequest {
16
16
  txContext: TxContext;
17
17
  /** Function data representing the function to call. */
18
18
  functionData: FunctionData;
19
- /** A salt to make the hash difficult to predict. The hash is used as the first nullifier if there is no nullifier emitted throughout the tx. */
19
+ /**
20
+ * A fresh random field drawn by the wallet, acting as the tx request's nonce. With `origin`, `chainId` and `version`
21
+ * it is the whole preimage of the protocol nullifier, so it keeps that nullifier unpredictable and unique. The kernel
22
+ * cannot check that it is random; draw it fresh for every transaction, since two requests with the same origin and
23
+ * salt are mutually exclusive.
24
+ */
20
25
  salt: Fr;
21
26
  constructor(
22
27
  /** Sender. */
@@ -27,7 +32,12 @@ export declare class TxRequest {
27
32
  txContext: TxContext,
28
33
  /** Function data representing the function to call. */
29
34
  functionData: FunctionData,
30
- /** A salt to make the hash difficult to predict. The hash is used as the first nullifier if there is no nullifier emitted throughout the tx. */
35
+ /**
36
+ * A fresh random field drawn by the wallet, acting as the tx request's nonce. With `origin`, `chainId` and `version`
37
+ * it is the whole preimage of the protocol nullifier, so it keeps that nullifier unpredictable and unique. The kernel
38
+ * cannot check that it is random; draw it fresh for every transaction, since two requests with the same origin and
39
+ * salt are mutually exclusive.
40
+ */
31
41
  salt: Fr);
32
42
  static getFields(fields: FieldsOf<TxRequest>): readonly [AztecAddress, Fr, TxContext, FunctionData, Fr];
33
43
  static from(fields: FieldsOf<TxRequest>): TxRequest;
@@ -36,6 +46,17 @@ export declare class TxRequest {
36
46
  * @returns The buffer.
37
47
  */
38
48
  toBuffer(): Buffer<ArrayBufferLike>;
49
+ /**
50
+ * The unsiloed value of the protocol nullifier: the transaction's nonce commitment.
51
+ * The preimage is only `origin`, `chainId`, `version` and `salt`. Gas settings and the first call's arguments are
52
+ * left out so that a fee bump or a cancellation that reuses the salt produces the same nullifier.
53
+ */
54
+ computeProtocolNullifierValue(): Promise<Fr>;
55
+ /**
56
+ * The protocol nullifier as inserted into the nullifier tree, and as every private call receives it in
57
+ * `PrivateCircuitPublicInputs.protocolNullifier`: the value above siloed under `NULL_MSG_SENDER`.
58
+ */
59
+ computeProtocolNullifier(): Promise<Fr>;
39
60
  toFields(): Fr[];
40
61
  /**
41
62
  * Deserializes from a buffer or reader, corresponding to a write in cpp.
@@ -43,8 +64,7 @@ export declare class TxRequest {
43
64
  * @returns The deserialized TxRequest object.
44
65
  */
45
66
  static fromBuffer(buffer: Buffer | BufferReader): TxRequest;
46
- hash(): Promise<Fr>;
47
67
  static empty(): TxRequest;
48
68
  isEmpty(): boolean;
49
69
  }
50
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfcmVxdWVzdC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3R4L3R4X3JlcXVlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxZQUFZLEVBQXdDLE1BQU0sa0NBQWtDLENBQUM7QUFDdEcsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUM7O0dBRUc7QUFDSCxxQkFBYSxTQUFTO0lBR2xCLGNBQWM7SUFDUCxNQUFNLEVBQUUsWUFBWTtJQUMzQiwyQ0FBMkM7SUFDcEMsUUFBUSxFQUFFLEVBQUU7SUFDbkIsMkJBQTJCO0lBQ3BCLFNBQVMsRUFBRSxTQUFTO0lBQzNCLHVEQUF1RDtJQUNoRCxZQUFZLEVBQUUsWUFBWTtJQUNqQyxnSkFBZ0o7SUFDekksSUFBSSxFQUFFLEVBQUU7SUFWakI7SUFDRSxjQUFjO0lBQ1AsTUFBTSxFQUFFLFlBQVk7SUFDM0IsMkNBQTJDO0lBQ3BDLFFBQVEsRUFBRSxFQUFFO0lBQ25CLDJCQUEyQjtJQUNwQixTQUFTLEVBQUUsU0FBUztJQUMzQix1REFBdUQ7SUFDaEQsWUFBWSxFQUFFLFlBQVk7SUFDakMsZ0pBQWdKO0lBQ3pJLElBQUksRUFBRSxFQUFFLEVBQ2I7SUFHSixNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsU0FBUyxDQUFDLDREQUUzQztJQUVELE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxTQUFTLENBQUMsR0FBRyxTQUFTLENBRWxEO0lBRUQ7OztPQUdHO0lBQ0gsUUFBUSw0QkFFUDtJQUVELFFBQVEsSUFBSSxFQUFFLEVBQUUsQ0FNZjtJQUVEOzs7O09BSUc7SUFDSCxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxNQUFNLEdBQUcsWUFBWSxHQUFHLFNBQVMsQ0FTMUQ7SUFFRCxJQUFJLGdCQUVIO0lBRUQsTUFBTSxDQUFDLEtBQUssY0FFWDtJQUVELE9BQU8sWUFRTjtDQUNGIn0=
70
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfcmVxdWVzdC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3R4L3R4X3JlcXVlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxZQUFZLEVBQXdDLE1BQU0sa0NBQWtDLENBQUM7QUFDdEcsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRXpELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUM7O0dBRUc7QUFDSCxxQkFBYSxTQUFTO0lBR2xCLGNBQWM7SUFDUCxNQUFNLEVBQUUsWUFBWTtJQUMzQiwyQ0FBMkM7SUFDcEMsUUFBUSxFQUFFLEVBQUU7SUFDbkIsMkJBQTJCO0lBQ3BCLFNBQVMsRUFBRSxTQUFTO0lBQzNCLHVEQUF1RDtJQUNoRCxZQUFZLEVBQUUsWUFBWTtJQUNqQzs7Ozs7T0FLRztJQUNJLElBQUksRUFBRSxFQUFFO0lBZmpCO0lBQ0UsY0FBYztJQUNQLE1BQU0sRUFBRSxZQUFZO0lBQzNCLDJDQUEyQztJQUNwQyxRQUFRLEVBQUUsRUFBRTtJQUNuQiwyQkFBMkI7SUFDcEIsU0FBUyxFQUFFLFNBQVM7SUFDM0IsdURBQXVEO0lBQ2hELFlBQVksRUFBRSxZQUFZO0lBQ2pDOzs7OztPQUtHO0lBQ0ksSUFBSSxFQUFFLEVBQUUsRUFDYjtJQUdKLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxTQUFTLENBQUMsNERBRTNDO0lBRUQsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxHQUFHLFNBQVMsQ0FFbEQ7SUFFRDs7O09BR0c7SUFDSCxRQUFRLDRCQUVQO0lBRUQ7Ozs7T0FJRztJQUNILDZCQUE2QixJQUFJLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FLM0M7SUFFRDs7O09BR0c7SUFDRyx3QkFBd0IsSUFBSSxPQUFPLENBQUMsRUFBRSxDQUFDLENBRTVDO0lBRUQsUUFBUSxJQUFJLEVBQUUsRUFBRSxDQU1mO0lBRUQ7Ozs7T0FJRztJQUNILE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLE1BQU0sR0FBRyxZQUFZLEdBQUcsU0FBUyxDQVMxRDtJQUVELE1BQU0sQ0FBQyxLQUFLLGNBRVg7SUFFRCxPQUFPLFlBUU47Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"tx_request.d.ts","sourceRoot":"","sources":["../../src/tx/tx_request.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,qCAAqC,CAAC;AACzD,OAAO,EAAE,YAAY,EAAwC,MAAM,kCAAkC,CAAC;AACtG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,qBAAa,SAAS;IAGlB,cAAc;IACP,MAAM,EAAE,YAAY;IAC3B,2CAA2C;IACpC,QAAQ,EAAE,EAAE;IACnB,2BAA2B;IACpB,SAAS,EAAE,SAAS;IAC3B,uDAAuD;IAChD,YAAY,EAAE,YAAY;IACjC,gJAAgJ;IACzI,IAAI,EAAE,EAAE;IAVjB;IACE,cAAc;IACP,MAAM,EAAE,YAAY;IAC3B,2CAA2C;IACpC,QAAQ,EAAE,EAAE;IACnB,2BAA2B;IACpB,SAAS,EAAE,SAAS;IAC3B,uDAAuD;IAChD,YAAY,EAAE,YAAY;IACjC,gJAAgJ;IACzI,IAAI,EAAE,EAAE,EACb;IAGJ,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,4DAE3C;IAED,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,SAAS,CAElD;IAED;;;OAGG;IACH,QAAQ,4BAEP;IAED,QAAQ,IAAI,EAAE,EAAE,CAMf;IAED;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAS1D;IAED,IAAI,gBAEH;IAED,MAAM,CAAC,KAAK,cAEX;IAED,OAAO,YAQN;CACF"}
1
+ {"version":3,"file":"tx_request.d.ts","sourceRoot":"","sources":["../../src/tx/tx_request.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,qCAAqC,CAAC;AACzD,OAAO,EAAE,YAAY,EAAwC,MAAM,kCAAkC,CAAC;AACtG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,qBAAa,SAAS;IAGlB,cAAc;IACP,MAAM,EAAE,YAAY;IAC3B,2CAA2C;IACpC,QAAQ,EAAE,EAAE;IACnB,2BAA2B;IACpB,SAAS,EAAE,SAAS;IAC3B,uDAAuD;IAChD,YAAY,EAAE,YAAY;IACjC;;;;;OAKG;IACI,IAAI,EAAE,EAAE;IAfjB;IACE,cAAc;IACP,MAAM,EAAE,YAAY;IAC3B,2CAA2C;IACpC,QAAQ,EAAE,EAAE;IACnB,2BAA2B;IACpB,SAAS,EAAE,SAAS;IAC3B,uDAAuD;IAChD,YAAY,EAAE,YAAY;IACjC;;;;;OAKG;IACI,IAAI,EAAE,EAAE,EACb;IAGJ,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,4DAE3C;IAED,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,SAAS,CAElD;IAED;;;OAGG;IACH,QAAQ,4BAEP;IAED;;;;OAIG;IACH,6BAA6B,IAAI,OAAO,CAAC,EAAE,CAAC,CAK3C;IAED;;;OAGG;IACG,wBAAwB,IAAI,OAAO,CAAC,EAAE,CAAC,CAE5C;IAED,QAAQ,IAAI,EAAE,EAAE,CAMf;IAED;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAS1D;IAED,MAAM,CAAC,KAAK,cAEX;IAED,OAAO,YAQN;CACF"}
@@ -3,6 +3,7 @@ import { poseidon2HashWithSeparator } from '@aztec-labs/foundation/crypto/poseid
3
3
  import { Fr } from '@aztec-labs/foundation/curves/bn254';
4
4
  import { BufferReader, serializeToBuffer, serializeToFields } from '@aztec-labs/foundation/serialize';
5
5
  import { AztecAddress } from '../aztec-address/index.js';
6
+ import { computeProtocolNullifier } from '../hash/hash.js';
6
7
  import { FunctionData } from './function_data.js';
7
8
  import { TxContext } from './tx_context.js';
8
9
  /**
@@ -14,7 +15,12 @@ import { TxContext } from './tx_context.js';
14
15
  functionData;
15
16
  salt;
16
17
  // docs:start:constructor
17
- constructor(/** Sender. */ origin, /** Pedersen hash of function arguments. */ argsHash, /** Transaction context. */ txContext, /** Function data representing the function to call. */ functionData, /** A salt to make the hash difficult to predict. The hash is used as the first nullifier if there is no nullifier emitted throughout the tx. */ salt){
18
+ constructor(/** Sender. */ origin, /** Pedersen hash of function arguments. */ argsHash, /** Transaction context. */ txContext, /** Function data representing the function to call. */ functionData, /**
19
+ * A fresh random field drawn by the wallet, acting as the tx request's nonce. With `origin`, `chainId` and `version`
20
+ * it is the whole preimage of the protocol nullifier, so it keeps that nullifier unpredictable and unique. The kernel
21
+ * cannot check that it is random; draw it fresh for every transaction, since two requests with the same origin and
22
+ * salt are mutually exclusive.
23
+ */ salt){
18
24
  this.origin = origin;
19
25
  this.argsHash = argsHash;
20
26
  this.txContext = txContext;
@@ -42,6 +48,24 @@ import { TxContext } from './tx_context.js';
42
48
  ...TxRequest.getFields(this)
43
49
  ]);
44
50
  }
51
+ /**
52
+ * The unsiloed value of the protocol nullifier: the transaction's nonce commitment.
53
+ * The preimage is only `origin`, `chainId`, `version` and `salt`. Gas settings and the first call's arguments are
54
+ * left out so that a fee bump or a cancellation that reuses the salt produces the same nullifier.
55
+ */ computeProtocolNullifierValue() {
56
+ return poseidon2HashWithSeparator([
57
+ this.origin.toField(),
58
+ this.txContext.chainId,
59
+ this.txContext.version,
60
+ this.salt
61
+ ], DomainSeparator.PROTOCOL_NULLIFIER);
62
+ }
63
+ /**
64
+ * The protocol nullifier as inserted into the nullifier tree, and as every private call receives it in
65
+ * `PrivateCircuitPublicInputs.protocolNullifier`: the value above siloed under `NULL_MSG_SENDER`.
66
+ */ async computeProtocolNullifier() {
67
+ return computeProtocolNullifier(await this.computeProtocolNullifierValue());
68
+ }
45
69
  toFields() {
46
70
  const fields = serializeToFields(...TxRequest.getFields(this));
47
71
  if (fields.length !== TX_REQUEST_LENGTH) {
@@ -57,9 +81,6 @@ import { TxContext } from './tx_context.js';
57
81
  const reader = BufferReader.asReader(buffer);
58
82
  return new TxRequest(reader.readObject(AztecAddress), Fr.fromBuffer(reader), reader.readObject(TxContext), reader.readObject(FunctionData), Fr.fromBuffer(reader));
59
83
  }
60
- hash() {
61
- return poseidon2HashWithSeparator(this.toFields(), DomainSeparator.TX_REQUEST);
62
- }
63
84
  static empty() {
64
85
  return new TxRequest(AztecAddress.ZERO, Fr.zero(), TxContext.empty(), FunctionData.empty(), Fr.zero());
65
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec-labs/stdlib",
3
- "version": "6.0.0-nightly.20260922",
3
+ "version": "6.0.0-rc.1",
4
4
  "type": "module",
5
5
  "inherits": [
6
6
  "../package.common.json",
@@ -92,12 +92,12 @@
92
92
  },
93
93
  "dependencies": {
94
94
  "@aws-sdk/client-s3": "^3.892.0",
95
- "@aztec-foundation/bb.js": "6.0.0-nightly.20260921",
96
- "@aztec-foundation/noir-noirc_abi": "6.0.0-nightly.20260921",
97
- "@aztec-labs/blob-lib": "6.0.0-nightly.20260922",
98
- "@aztec-labs/constants": "6.0.0-nightly.20260922",
99
- "@aztec-labs/ethereum": "6.0.0-nightly.20260922",
100
- "@aztec-labs/foundation": "6.0.0-nightly.20260922",
95
+ "@aztec-foundation/bb.js": "6.0.0-rc.1",
96
+ "@aztec-foundation/noir-noirc_abi": "6.0.0-rc.1",
97
+ "@aztec-labs/blob-lib": "6.0.0-rc.1",
98
+ "@aztec-labs/constants": "6.0.0-rc.1",
99
+ "@aztec-labs/ethereum": "6.0.0-rc.1",
100
+ "@aztec-labs/foundation": "6.0.0-rc.1",
101
101
  "@google-cloud/storage": "^7.15.0",
102
102
  "axios": "^1.15.1",
103
103
  "json-stringify-deterministic": "1.0.12",
@@ -108,7 +108,7 @@
108
108
  "msgpackr": "^1.11.2",
109
109
  "pako": "^2.1.0",
110
110
  "tslib": "^2.4.0",
111
- "viem": "npm:@aztec/viem@2.38.2",
111
+ "viem": "npm:@aztec/viem@2.38.3",
112
112
  "zod": "~4.4.3"
113
113
  },
114
114
  "devDependencies": {
@@ -1,4 +1,5 @@
1
1
  import type { ViemCommitteeAttestation, ViemCommitteeAttestations } from '@aztec-labs/ethereum/contracts';
2
+ import { Buffer32 } from '@aztec-labs/foundation/buffer';
2
3
  import { EthAddress } from '@aztec-labs/foundation/eth-address';
3
4
  import { Signature } from '@aztec-labs/foundation/eth-signature';
4
5
  import { BufferReader, serializeToBuffer } from '@aztec-labs/foundation/serialize';
@@ -76,10 +77,10 @@ export class CommitteeAttestation {
76
77
  }
77
78
 
78
79
  const v = dataBuffer[dataIndex];
79
- const r = `0x${dataBuffer.subarray(dataIndex + 1, dataIndex + 33).toString('hex')}` as const;
80
- const s = `0x${dataBuffer.subarray(dataIndex + 33, dataIndex + 65).toString('hex')}` as const;
80
+ const r = Buffer32.fromBuffer(dataBuffer.subarray(dataIndex + 1, dataIndex + 33));
81
+ const s = Buffer32.fromBuffer(dataBuffer.subarray(dataIndex + 33, dataIndex + 65));
81
82
 
82
- const signature = Signature.fromViemSignature({ r, s, v });
83
+ const signature = new Signature(r, s, v);
83
84
 
84
85
  // For signed attestations, we use a zero address as the address is recovered from the signature
85
86
  attestations.push(new CommitteeAttestation(EthAddress.ZERO, signature));
@@ -90,8 +91,7 @@ export class CommitteeAttestation {
90
91
  throw new Error(`Insufficient data for address at position ${i}`);
91
92
  }
92
93
 
93
- const addressBytes = dataBuffer.subarray(dataIndex, dataIndex + 20);
94
- const address = EthAddress.fromString(`0x${addressBytes.toString('hex')}`);
94
+ const address = EthAddress.fromBuffer(dataBuffer.subarray(dataIndex, dataIndex + 20));
95
95
 
96
96
  // For address-only attestations, use empty signature
97
97
  attestations.push(new CommitteeAttestation(address, Signature.empty()));
@@ -3,6 +3,7 @@ import { times } from '@aztec-labs/foundation/collection';
3
3
  import { Fr } from '@aztec-labs/foundation/curves/bn254';
4
4
  import {
5
5
  type CheckpointId,
6
+ CommitteeAttestationsAndSigners,
6
7
  GENESIS_CHECKPOINT_HEADER_HASH,
7
8
  L2Block,
8
9
  type L2BlockId,
@@ -91,7 +92,12 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
91
92
  blockToCheckpoint.set(block.number, checkpointNumber);
92
93
  }
93
94
 
94
- return new PublishedCheckpoint(checkpoint, L1PublishedData.random(), []);
95
+ return new PublishedCheckpoint(
96
+ checkpoint,
97
+ L1PublishedData.random(),
98
+ [],
99
+ CommitteeAttestationsAndSigners.packAttestations([]),
100
+ );
95
101
  };
96
102
 
97
103
  /** Creates a thin chain-checkpointed event carrying the block + checkpoint ids of the checkpoint's last block. */
@@ -1,4 +1,7 @@
1
- import type { ViemCommitteeAttestations } from '@aztec-labs/ethereum/contracts';
1
+ import {
2
+ type ViemCommitteeAttestations,
3
+ ViemCommitteeAttestationsSchema,
4
+ } from '@aztec-labs/ethereum/contracts/committee-attestations';
2
5
  import { EpochNumber, EpochNumberSchema } from '@aztec-labs/foundation/branded-types';
3
6
  import { EthAddress } from '@aztec-labs/foundation/eth-address';
4
7
  import { type ZodFor, schemas } from '@aztec-labs/foundation/schemas';
@@ -71,12 +74,6 @@ export type ValidateCheckpointNegativeResult =
71
74
  /** Result type for validating checkpoint attestations */
72
75
  export type ValidateCheckpointResult = { valid: true } | ValidateCheckpointNegativeResult;
73
76
 
74
- /** Zod schema for the raw packed `CommitteeAttestations` viem tuple (two 0x-prefixed hex strings). */
75
- const ViemCommitteeAttestationsSchema: ZodFor<ViemCommitteeAttestations> = z.object({
76
- signatureIndices: schemas.HexStringWith0x,
77
- signaturesOrAddresses: schemas.HexStringWith0x,
78
- });
79
-
80
77
  export const ValidateCheckpointResultSchema: ZodFor<ValidateCheckpointResult> = z.union([
81
78
  z.object({ valid: z.literal(true) }),
82
79
  z.object({
@@ -1,3 +1,7 @@
1
+ import {
2
+ type ViemCommitteeAttestations,
3
+ ViemCommitteeAttestationsSchema,
4
+ } from '@aztec-labs/ethereum/contracts/committee-attestations';
1
5
  import {
2
6
  BlockNumber,
3
7
  BlockNumberSchema,
@@ -24,6 +28,11 @@ export type CommonCheckpointData = {
24
28
  /** Data stored with checkpoint data after publishing on l1 */
25
29
  export type L1EnrichedCheckpointData = {
26
30
  attestations: CommitteeAttestation[];
31
+ /**
32
+ * The packed `CommitteeAttestations` tuple exactly as posted to L1. {@link attestations} is what this tuple
33
+ * decodes to; only the tuple reproduces the `attestationsHash` the rollup stored at propose time.
34
+ */
35
+ verbatimAttestations: ViemCommitteeAttestations;
27
36
  l1: L1PublishedData;
28
37
  };
29
38
 
@@ -86,6 +95,7 @@ export const CheckpointDataSchema = z
86
95
  blockCount: schemas.Integer,
87
96
  feeAssetPriceModifier: schemas.BigInt,
88
97
  attestations: z.array(CommitteeAttestation.schema),
98
+ verbatimAttestations: ViemCommitteeAttestationsSchema,
89
99
  l1: L1PublishedData.schema,
90
100
  })
91
101
  .transform(
@@ -98,6 +108,7 @@ export const CheckpointDataSchema = z
98
108
  blockCount: obj.blockCount,
99
109
  feeAssetPriceModifier: obj.feeAssetPriceModifier,
100
110
  attestations: obj.attestations,
111
+ verbatimAttestations: obj.verbatimAttestations,
101
112
  l1: obj.l1,
102
113
  }),
103
114
  );
@@ -1,8 +1,13 @@
1
1
  // Ignoring import issue to fix portable inferred type issue in zod schema
2
+ import {
3
+ type ViemCommitteeAttestations,
4
+ ViemCommitteeAttestationsSchema,
5
+ } from '@aztec-labs/ethereum/contracts/committee-attestations';
2
6
  import { Buffer32 } from '@aztec-labs/foundation/buffer';
3
7
  import { randomBigInt } from '@aztec-labs/foundation/crypto/random';
4
8
  import { schemas } from '@aztec-labs/foundation/schemas';
5
9
  import { BufferReader, serializeToBuffer } from '@aztec-labs/foundation/serialize';
10
+ import { bufferToHex, hexToBuffer } from '@aztec-labs/foundation/string';
6
11
  import type { FieldsOf } from '@aztec-labs/foundation/types';
7
12
  import { z } from 'zod';
8
13
 
@@ -60,6 +65,14 @@ export class PublishedCheckpoint {
60
65
  public l1: L1PublishedData,
61
66
  /** The attestations for the last block in the checkpoint. */
62
67
  public attestations: CommitteeAttestation[],
68
+ /**
69
+ * The packed `CommitteeAttestations` tuple exactly as it appeared in the propose calldata. The rollup
70
+ * stores `keccak256(abi.encode(tuple))` and re-checks it when an epoch proof or an invalidation is
71
+ * submitted, so the tuple can only be reproduced from these bytes: repacking the decoded
72
+ * {@link attestations} zeroes bitmap bits past the committee size and canonicalizes recovery bytes,
73
+ * neither of which any decoder reads but both of which the stored hash covers.
74
+ */
75
+ public verbatimAttestations: ViemCommitteeAttestations,
63
76
  ) {}
64
77
 
65
78
  static get schema() {
@@ -68,12 +81,13 @@ export class PublishedCheckpoint {
68
81
  checkpoint: Checkpoint.schema,
69
82
  l1: L1PublishedData.schema,
70
83
  attestations: z.array(CommitteeAttestation.schema),
84
+ verbatimAttestations: ViemCommitteeAttestationsSchema,
71
85
  })
72
86
  .transform(obj => PublishedCheckpoint.from(obj));
73
87
  }
74
88
 
75
89
  static getFields(fields: FieldsOf<PublishedCheckpoint>) {
76
- return [fields.checkpoint, fields.l1, fields.attestations] as const;
90
+ return [fields.checkpoint, fields.l1, fields.attestations, fields.verbatimAttestations] as const;
77
91
  }
78
92
 
79
93
  static from(fields: FieldsOf<PublishedCheckpoint>) {
@@ -87,14 +101,21 @@ export class PublishedCheckpoint {
87
101
  const l1BlockHash = reader.readString(MAX_BLOCK_HASH_STRING_LENGTH);
88
102
  const l1Timestamp = reader.readBigInt();
89
103
  const attestations = reader.readVector(CommitteeAttestation, MAX_COMMITTEE_SIZE);
104
+ const verbatimAttestations: ViemCommitteeAttestations = {
105
+ signatureIndices: bufferToHex(reader.readBuffer()),
106
+ signaturesOrAddresses: bufferToHex(reader.readBuffer()),
107
+ };
90
108
  return new PublishedCheckpoint(
91
109
  checkpoint,
92
110
  new L1PublishedData(l1BlockNumber, l1Timestamp, l1BlockHash),
93
111
  attestations,
112
+ verbatimAttestations,
94
113
  );
95
114
  }
96
115
 
97
116
  public toBuffer(): Buffer {
117
+ const signatureIndices = hexToBuffer(this.verbatimAttestations.signatureIndices);
118
+ const signaturesOrAddresses = hexToBuffer(this.verbatimAttestations.signaturesOrAddresses);
98
119
  return serializeToBuffer(
99
120
  this.checkpoint,
100
121
  this.l1.blockNumber,
@@ -102,6 +123,10 @@ export class PublishedCheckpoint {
102
123
  this.l1.timestamp,
103
124
  this.attestations.length,
104
125
  this.attestations,
126
+ signatureIndices.length,
127
+ signatureIndices,
128
+ signaturesOrAddresses.length,
129
+ signaturesOrAddresses,
105
130
  );
106
131
  }
107
132
  }
package/src/hash/hash.ts CHANGED
@@ -109,8 +109,8 @@ export async function computeSiloedPublicInitializationNullifier(contract: Aztec
109
109
  *
110
110
  * @dev Must match the implementation in noir-protocol-circuits/crates/types/src/hash.nr > compute_protocol_nullifier
111
111
  */
112
- export function computeProtocolNullifier(txRequestHash: Fr): Promise<Fr> {
113
- return siloNullifier(AztecAddress.NULL_MSG_SENDER, txRequestHash);
112
+ export function computeProtocolNullifier(protocolNullifierValue: Fr): Promise<Fr> {
113
+ return siloNullifier(AztecAddress.NULL_MSG_SENDER, protocolNullifierValue);
114
114
  }
115
115
 
116
116
  /** Domain-separates a raw log tag with the given domain separator. */
@@ -145,10 +145,12 @@ export class PrivateCircuitPublicInputs {
145
145
  */
146
146
  public contractClassLogsHashes: ClaimedLengthArray<CountedLogHash, typeof MAX_CONTRACT_CLASS_LOGS_PER_CALL>,
147
147
  /**
148
- * The tx request's salt. Keeps `tx_request.hash()` (the protocol nullifier) unpredictable, and lets the init
149
- * kernel bind this proof to a specific tx request (it asserts this equals `tx_request.salt`).
148
+ * The transaction's siloed protocol nullifier: the value the init kernel inserts at index 0 of the transaction's
149
+ * nullifiers, identical on every private call. The init kernel asserts the first call's value equals the one
150
+ * derived from the tx request, binding this proof to that request; every later kernel asserts each nested call's
151
+ * value equals the transaction constant.
150
152
  */
151
- public txRequestSalt: Fr,
153
+ public protocolNullifier: Fr,
152
154
  ) {}
153
155
 
154
156
  /**
@@ -287,7 +289,7 @@ export class PrivateCircuitPublicInputs {
287
289
  this.l2ToL1Msgs.isEmpty() &&
288
290
  this.privateLogs.isEmpty() &&
289
291
  this.contractClassLogsHashes.isEmpty() &&
290
- this.txRequestSalt.isZero()
292
+ this.protocolNullifier.isZero()
291
293
  );
292
294
  }
293
295
 
@@ -321,7 +323,7 @@ export class PrivateCircuitPublicInputs {
321
323
  fields.l2ToL1Msgs,
322
324
  fields.privateLogs,
323
325
  fields.contractClassLogsHashes,
324
- fields.txRequestSalt,
326
+ fields.protocolNullifier,
325
327
  ] as const;
326
328
  }
327
329
 
@@ -356,7 +358,7 @@ export class PrivateCircuitPublicInputs {
356
358
  this.l2ToL1Msgs,
357
359
  this.privateLogs,
358
360
  this.contractClassLogsHashes,
359
- this.txRequestSalt,
361
+ this.protocolNullifier,
360
362
  ]);
361
363
  }
362
364
 
@@ -11,7 +11,7 @@ export class PrivateContextInputs {
11
11
  public anchorBlockHeader: BlockHeader,
12
12
  public txContext: TxContext,
13
13
  public startSideEffectCounter: number,
14
- public txRequestSalt: Fr,
14
+ public protocolNullifier: Fr,
15
15
  ) {}
16
16
 
17
17
  public static empty(): PrivateContextInputs {
@@ -24,7 +24,7 @@ export class PrivateContextInputs {
24
24
  this.anchorBlockHeader,
25
25
  this.txContext,
26
26
  this.startSideEffectCounter,
27
- this.txRequestSalt,
27
+ this.protocolNullifier,
28
28
  ]);
29
29
  }
30
30
  }
@@ -653,7 +653,7 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn
653
653
  anchorBlockHeader: makeBlockHeader(seed + 0xd00),
654
654
  txContext: makeTxContext(seed + 0x1400),
655
655
  isFeePayer: false,
656
- txRequestSalt: fr(seed + 0x1500),
656
+ protocolNullifier: fr(seed + 0x1500),
657
657
  });
658
658
  }
659
659
 
@@ -24,13 +24,13 @@ export class PrivateTxConstantData {
24
24
  */
25
25
  public txContext: TxContext,
26
26
  /**
27
- * Salt of the transaction request (`TxRequest.salt`).
27
+ * The transaction's siloed protocol nullifier.
28
28
  *
29
29
  * Bound to the user's `tx_request` by the Init circuit and kept constant by every subsequent kernel so that each
30
- * app circuit's `txRequestSalt` public input can be checked against it. It is intentionally not carried into
31
- * `TxConstantData`: it is dropped by the Tail circuits rather than being exposed by the final (hiding) kernel.
30
+ * app circuit's `protocolNullifier` public input can be checked against it. Not carried into `TxConstantData`:
31
+ * the value is already published as the transaction's first nullifier.
32
32
  */
33
- public txRequestSalt: Fr,
33
+ public protocolNullifier: Fr,
34
34
  /**
35
35
  * Root of the vk tree for the protocol circuits.
36
36
  */
@@ -49,7 +49,7 @@ export class PrivateTxConstantData {
49
49
  return [
50
50
  fields.anchorBlockHeader,
51
51
  fields.txContext,
52
- fields.txRequestSalt,
52
+ fields.protocolNullifier,
53
53
  fields.vkTreeRoot,
54
54
  fields.protocolContracts,
55
55
  ] as const;
@@ -105,7 +105,7 @@ export class PrivateTxConstantData {
105
105
  return (
106
106
  this.anchorBlockHeader.getSize() +
107
107
  this.txContext.getSize() +
108
- this.txRequestSalt.size +
108
+ this.protocolNullifier.size +
109
109
  this.vkTreeRoot.size +
110
110
  this.protocolContracts.getSize()
111
111
  );
@@ -5,6 +5,7 @@ import { BufferReader, serializeToBuffer, serializeToFields } from '@aztec-labs/
5
5
  import type { FieldsOf } from '@aztec-labs/foundation/types';
6
6
 
7
7
  import { AztecAddress } from '../aztec-address/index.js';
8
+ import { computeProtocolNullifier } from '../hash/hash.js';
8
9
  import { FunctionData } from './function_data.js';
9
10
  import { TxContext } from './tx_context.js';
10
11
 
@@ -22,7 +23,12 @@ export class TxRequest {
22
23
  public txContext: TxContext,
23
24
  /** Function data representing the function to call. */
24
25
  public functionData: FunctionData,
25
- /** A salt to make the hash difficult to predict. The hash is used as the first nullifier if there is no nullifier emitted throughout the tx. */
26
+ /**
27
+ * A fresh random field drawn by the wallet, acting as the tx request's nonce. With `origin`, `chainId` and `version`
28
+ * it is the whole preimage of the protocol nullifier, so it keeps that nullifier unpredictable and unique. The kernel
29
+ * cannot check that it is random; draw it fresh for every transaction, since two requests with the same origin and
30
+ * salt are mutually exclusive.
31
+ */
26
32
  public salt: Fr,
27
33
  ) {}
28
34
  // docs:end:constructor
@@ -43,6 +49,26 @@ export class TxRequest {
43
49
  return serializeToBuffer([...TxRequest.getFields(this)]);
44
50
  }
45
51
 
52
+ /**
53
+ * The unsiloed value of the protocol nullifier: the transaction's nonce commitment.
54
+ * The preimage is only `origin`, `chainId`, `version` and `salt`. Gas settings and the first call's arguments are
55
+ * left out so that a fee bump or a cancellation that reuses the salt produces the same nullifier.
56
+ */
57
+ computeProtocolNullifierValue(): Promise<Fr> {
58
+ return poseidon2HashWithSeparator(
59
+ [this.origin.toField(), this.txContext.chainId, this.txContext.version, this.salt],
60
+ DomainSeparator.PROTOCOL_NULLIFIER,
61
+ );
62
+ }
63
+
64
+ /**
65
+ * The protocol nullifier as inserted into the nullifier tree, and as every private call receives it in
66
+ * `PrivateCircuitPublicInputs.protocolNullifier`: the value above siloed under `NULL_MSG_SENDER`.
67
+ */
68
+ async computeProtocolNullifier(): Promise<Fr> {
69
+ return computeProtocolNullifier(await this.computeProtocolNullifierValue());
70
+ }
71
+
46
72
  toFields(): Fr[] {
47
73
  const fields = serializeToFields(...TxRequest.getFields(this));
48
74
  if (fields.length !== TX_REQUEST_LENGTH) {
@@ -67,10 +93,6 @@ export class TxRequest {
67
93
  );
68
94
  }
69
95
 
70
- hash() {
71
- return poseidon2HashWithSeparator(this.toFields(), DomainSeparator.TX_REQUEST);
72
- }
73
-
74
96
  static empty() {
75
97
  return new TxRequest(AztecAddress.ZERO, Fr.zero(), TxContext.empty(), FunctionData.empty(), Fr.zero());
76
98
  }