@aztec/stdlib 5.0.0-nightly.20260530 → 5.0.0-nightly.20260531

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.
@@ -1,13 +1,16 @@
1
1
  import { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import { BufferReader } from '@aztec/foundation/serialize';
3
3
  /**
4
- * Serialization format detection for ChonkProof is size-based:
4
+ * Serialization format detection for ChonkProof is value-based on the leading uint32:
5
+ * - EMPTY: [0: uint32] → total = 4 bytes
5
6
  * - UNCOMPRESSED (legacy): [field_count=1632: uint32] [fields...] → total ≈ 52KB (>= 40KB)
6
7
  * - COMPRESSED: [byte_count: uint32] [compressed_bytes] → total ≈ 35KB (< 40KB)
7
8
  *
8
- * Detection: if the first uint32 equals CHONK_PROOF_LENGTH (1632), it's legacy format
9
- * (field count). Otherwise, it's compressed format (byte count). The old uncompressed
10
- * format is never smaller than 40KB; compressed proofs are always smaller than 40KB.
9
+ * Detection from the first uint32: 0 means an empty proof (no fields); CHONK_PROOF_LENGTH
10
+ * (1632) means legacy format (field count); any other value is the compressed byte count.
11
+ * A real compressed proof always has a non-zero byte count, so 0 is an unambiguous sentinel
12
+ * for the empty case. The old uncompressed format is never smaller than 40KB; compressed
13
+ * proofs are always smaller than 40KB.
11
14
  */
12
15
  export declare class ChonkProof {
13
16
  fields: Fr[];
@@ -28,7 +31,8 @@ export declare class ChonkProof {
28
31
  * Deserialize a ChonkProof from a buffer.
29
32
  * Supports both legacy (field elements) and compressed (chonk compression) formats.
30
33
  *
31
- * Size-based format detection:
34
+ * Value-based format detection on the leading uint32:
35
+ * - First uint32 == 0: empty proof (no fields), total = 4 bytes
32
36
  * - First uint32 == CHONK_PROOF_LENGTH (1632): legacy format, read field elements
33
37
  * Total proof data ≈ 52KB (always >= 40KB)
34
38
  * - Otherwise: compressed format, first uint32 is byte count of compressed data
@@ -69,4 +73,4 @@ export declare class ChonkProofWithPublicInputs {
69
73
  toBuffer(): Buffer<ArrayBufferLike>;
70
74
  static fromBufferArray(fields: Uint8Array[]): ChonkProofWithPublicInputs;
71
75
  }
72
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hvbmtfcHJvb2YuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wcm9vZnMvY2hvbmtfcHJvb2YudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBSUEsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXBELE9BQU8sRUFBRSxZQUFZLEVBQW9DLE1BQU0sNkJBQTZCLENBQUM7QUFFN0Y7Ozs7Ozs7O0dBUUc7QUFHSCxxQkFBYSxVQUFVO0lBWVosTUFBTSxFQUFFLEVBQUUsRUFBRTtJQVhyQjs7OztPQUlHO0lBQ0ksZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDLFlBSVMsTUFBTSxFQUFFLEVBQUUsRUFBRSxFQUNuQixlQUFlLENBQUMsRUFBRSxNQUFNLEVBTXpCO0lBRU0sa0JBQWtCLENBQUMsWUFBWSxFQUFFLEVBQUUsRUFBRSw4QkFFM0M7SUFFTSxPQUFPLFlBRWI7SUFFRCxNQUFNLENBQUMsS0FBSyxlQUVYO0lBRUQsTUFBTSxDQUFDLE1BQU0sZUFZWjtJQUVELE1BQU0sS0FBSyxNQUFNLDJHQUVoQjtJQUdELE1BQU0sNEJBRUw7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSCxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxNQUFNLEdBQUcsWUFBWSxHQUFHLFVBQVUsQ0FnQjNEO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFVBQVUsRUFBRSxNQUFNLEdBQUcsVUFBVSxDQWtCekQ7SUFFRDs7OztPQUlHO0lBQ0ksUUFBUSw0QkFPZDtDQUNGO0FBRUQscUJBQWEsMEJBQTBCO0lBVzVCLHNCQUFzQixFQUFFLEVBQUUsRUFBRTtJQVZyQzs7OztPQUlHO0lBQ0ksZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDLFlBR1Msc0JBQXNCLEVBQUUsRUFBRSxFQUFFLEVBS3BDO0lBRU0sZUFBZSxTQUdyQjtJQUVNLGtCQUFrQixlQUl4QjtJQUVNLE9BQU8sWUFFYjtJQUVELE1BQU0sQ0FBQyxLQUFLLCtCQUVYO0lBRUQsTUFBTSxLQUFLLE1BQU0sMklBRWhCO0lBR0QsTUFBTSw0QkFFTDtJQUVELE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLE1BQU0sR0FBRyxZQUFZLEdBQUcsMEJBQTBCLENBSzNFO0lBRU0sUUFBUSw0QkFFZDtJQUdELE1BQU0sQ0FBQyxlQUFlLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLDBCQUEwQixDQUd2RTtDQUNGIn0=
76
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hvbmtfcHJvb2YuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wcm9vZnMvY2hvbmtfcHJvb2YudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBSUEsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXBELE9BQU8sRUFBRSxZQUFZLEVBQW9DLE1BQU0sNkJBQTZCLENBQUM7QUFFN0Y7Ozs7Ozs7Ozs7O0dBV0c7QUFHSCxxQkFBYSxVQUFVO0lBWVosTUFBTSxFQUFFLEVBQUUsRUFBRTtJQVhyQjs7OztPQUlHO0lBQ0ksZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDLFlBSVMsTUFBTSxFQUFFLEVBQUUsRUFBRSxFQUNuQixlQUFlLENBQUMsRUFBRSxNQUFNLEVBT3pCO0lBRU0sa0JBQWtCLENBQUMsWUFBWSxFQUFFLEVBQUUsRUFBRSw4QkFFM0M7SUFFTSxPQUFPLFlBRWI7SUFFRCxNQUFNLENBQUMsS0FBSyxlQUVYO0lBRUQsTUFBTSxDQUFDLE1BQU0sZUFZWjtJQUVELE1BQU0sS0FBSyxNQUFNLDJHQUVoQjtJQUdELE1BQU0sNEJBRUw7SUFFRDs7Ozs7Ozs7OztPQVVHO0lBQ0gsTUFBTSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxHQUFHLFlBQVksR0FBRyxVQUFVLENBcUIzRDtJQUVEOzs7Ozs7T0FNRztJQUNILE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLEVBQUUsTUFBTSxHQUFHLFVBQVUsQ0FrQnpEO0lBRUQ7Ozs7T0FJRztJQUNJLFFBQVEsNEJBV2Q7Q0FDRjtBQUVELHFCQUFhLDBCQUEwQjtJQVc1QixzQkFBc0IsRUFBRSxFQUFFLEVBQUU7SUFWckM7Ozs7T0FJRztJQUNJLGVBQWUsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUVoQyxZQUdTLHNCQUFzQixFQUFFLEVBQUUsRUFBRSxFQU1wQztJQUVNLGVBQWUsU0FNckI7SUFFTSxrQkFBa0IsZUFPeEI7SUFFTSxPQUFPLFlBRWI7SUFFRCxNQUFNLENBQUMsS0FBSywrQkFFWDtJQUVELE1BQU0sS0FBSyxNQUFNLDJJQUVoQjtJQUdELE1BQU0sNEJBRUw7SUFFRCxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxNQUFNLEdBQUcsWUFBWSxHQUFHLDBCQUEwQixDQUszRTtJQUVNLFFBQVEsNEJBRWQ7SUFHRCxNQUFNLENBQUMsZUFBZSxDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsR0FBRywwQkFBMEIsQ0FHdkU7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"chonk_proof.d.ts","sourceRoot":"","sources":["../../src/proofs/chonk_proof.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAoC,MAAM,6BAA6B,CAAC;AAE7F;;;;;;;;GAQG;AAGH,qBAAa,UAAU;IAYZ,MAAM,EAAE,EAAE,EAAE;IAXrB;;;;OAIG;IACI,eAAe,CAAC,EAAE,MAAM,CAAC;IAEhC,YAIS,MAAM,EAAE,EAAE,EAAE,EACnB,eAAe,CAAC,EAAE,MAAM,EAMzB;IAEM,kBAAkB,CAAC,YAAY,EAAE,EAAE,EAAE,8BAE3C;IAEM,OAAO,YAEb;IAED,MAAM,CAAC,KAAK,eAEX;IAED,MAAM,CAAC,MAAM,eAYZ;IAED,MAAM,KAAK,MAAM,2GAEhB;IAGD,MAAM,4BAEL;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAgB3D;IAED;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAkBzD;IAED;;;;OAIG;IACI,QAAQ,4BAOd;CACF;AAED,qBAAa,0BAA0B;IAW5B,sBAAsB,EAAE,EAAE,EAAE;IAVrC;;;;OAIG;IACI,eAAe,CAAC,EAAE,MAAM,CAAC;IAEhC,YAGS,sBAAsB,EAAE,EAAE,EAAE,EAKpC;IAEM,eAAe,SAGrB;IAEM,kBAAkB,eAIxB;IAEM,OAAO,YAEb;IAED,MAAM,CAAC,KAAK,+BAEX;IAED,MAAM,KAAK,MAAM,2IAEhB;IAGD,MAAM,4BAEL;IAED,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,0BAA0B,CAK3E;IAEM,QAAQ,4BAEd;IAGD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,0BAA0B,CAGvE;CACF"}
1
+ {"version":3,"file":"chonk_proof.d.ts","sourceRoot":"","sources":["../../src/proofs/chonk_proof.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAoC,MAAM,6BAA6B,CAAC;AAE7F;;;;;;;;;;;GAWG;AAGH,qBAAa,UAAU;IAYZ,MAAM,EAAE,EAAE,EAAE;IAXrB;;;;OAIG;IACI,eAAe,CAAC,EAAE,MAAM,CAAC;IAEhC,YAIS,MAAM,EAAE,EAAE,EAAE,EACnB,eAAe,CAAC,EAAE,MAAM,EAOzB;IAEM,kBAAkB,CAAC,YAAY,EAAE,EAAE,EAAE,8BAE3C;IAEM,OAAO,YAEb;IAED,MAAM,CAAC,KAAK,eAEX;IAED,MAAM,CAAC,MAAM,eAYZ;IAED,MAAM,KAAK,MAAM,2GAEhB;IAGD,MAAM,4BAEL;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAqB3D;IAED;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAkBzD;IAED;;;;OAIG;IACI,QAAQ,4BAWd;CACF;AAED,qBAAa,0BAA0B;IAW5B,sBAAsB,EAAE,EAAE,EAAE;IAVrC;;;;OAIG;IACI,eAAe,CAAC,EAAE,MAAM,CAAC;IAEhC,YAGS,sBAAsB,EAAE,EAAE,EAAE,EAMpC;IAEM,eAAe,SAMrB;IAEM,kBAAkB,eAOxB;IAEM,OAAO,YAEb;IAED,MAAM,CAAC,KAAK,+BAEX;IAED,MAAM,KAAK,MAAM,2IAEhB;IAGD,MAAM,4BAEL;IAED,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,0BAA0B,CAK3E;IAEM,QAAQ,4BAEd;IAGD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,0BAA0B,CAGvE;CACF"}
@@ -6,13 +6,16 @@ import { Fr } from '@aztec/foundation/curves/bn254';
6
6
  import { bufferSchemaFor } from '@aztec/foundation/schemas';
7
7
  import { BufferReader, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize';
8
8
  /**
9
- * Serialization format detection for ChonkProof is size-based:
9
+ * Serialization format detection for ChonkProof is value-based on the leading uint32:
10
+ * - EMPTY: [0: uint32] → total = 4 bytes
10
11
  * - UNCOMPRESSED (legacy): [field_count=1632: uint32] [fields...] → total ≈ 52KB (>= 40KB)
11
12
  * - COMPRESSED: [byte_count: uint32] [compressed_bytes] → total ≈ 35KB (< 40KB)
12
13
  *
13
- * Detection: if the first uint32 equals CHONK_PROOF_LENGTH (1632), it's legacy format
14
- * (field count). Otherwise, it's compressed format (byte count). The old uncompressed
15
- * format is never smaller than 40KB; compressed proofs are always smaller than 40KB.
14
+ * Detection from the first uint32: 0 means an empty proof (no fields); CHONK_PROOF_LENGTH
15
+ * (1632) means legacy format (field count); any other value is the compressed byte count.
16
+ * A real compressed proof always has a non-zero byte count, so 0 is an unambiguous sentinel
17
+ * for the empty case. The old uncompressed format is never smaller than 40KB; compressed
18
+ * proofs are always smaller than 40KB.
16
19
  */ // CHONK: "Client Honk" - An UltraHonk variant with incremental folding and delayed non-native arithmetic.
17
20
  export class ChonkProof {
18
21
  fields;
@@ -26,7 +29,8 @@ export class ChonkProof {
26
29
  // Not using Tuple here due to the length being too high.
27
30
  fields, compressedProof){
28
31
  this.fields = fields;
29
- if (fields.length !== CHONK_PROOF_LENGTH) {
32
+ // An empty proof (no fields) is a valid placeholder; see ChonkProof.empty().
33
+ if (fields.length !== 0 && fields.length !== CHONK_PROOF_LENGTH) {
30
34
  throw new Error(`Invalid ChonkProof length: ${fields.length}`);
31
35
  }
32
36
  this.compressedProof = compressedProof;
@@ -41,7 +45,7 @@ export class ChonkProof {
41
45
  return this.fields.every((field)=>field.isZero());
42
46
  }
43
47
  static empty() {
44
- return new ChonkProof(new Array(CHONK_PROOF_LENGTH).fill(Fr.ZERO));
48
+ return new ChonkProof([]);
45
49
  }
46
50
  static random() {
47
51
  // NB: Not using Fr.random here because it slows down some tests that require a large number of txs significantly.
@@ -67,7 +71,8 @@ export class ChonkProof {
67
71
  * Deserialize a ChonkProof from a buffer.
68
72
  * Supports both legacy (field elements) and compressed (chonk compression) formats.
69
73
  *
70
- * Size-based format detection:
74
+ * Value-based format detection on the leading uint32:
75
+ * - First uint32 == 0: empty proof (no fields), total = 4 bytes
71
76
  * - First uint32 == CHONK_PROOF_LENGTH (1632): legacy format, read field elements
72
77
  * Total proof data ≈ 52KB (always >= 40KB)
73
78
  * - Otherwise: compressed format, first uint32 is byte count of compressed data
@@ -75,6 +80,10 @@ export class ChonkProof {
75
80
  */ static fromBuffer(buffer) {
76
81
  const reader = BufferReader.asReader(buffer);
77
82
  const firstUint32 = reader.readNumber();
83
+ if (firstUint32 === 0) {
84
+ // Empty format: a zero length encodes an empty proof.
85
+ return ChonkProof.empty();
86
+ }
78
87
  if (firstUint32 === CHONK_PROOF_LENGTH) {
79
88
  // Legacy format: firstUint32 is the field count (1632)
80
89
  // Widen to `number` to prevent TS from narrowing to literal 1632,
@@ -123,6 +132,10 @@ export class ChonkProof {
123
132
  this.compressedProof
124
133
  ]);
125
134
  }
135
+ if (this.fields.length === 0) {
136
+ // Empty format: a single zero length, no fields.
137
+ return numToUInt32BE(0);
138
+ }
126
139
  // Legacy format: [field_count=1632: uint32] [fields...]
127
140
  return serializeToBuffer(this.fields.length, this.fields);
128
141
  }
@@ -138,15 +151,22 @@ export class ChonkProofWithPublicInputs {
138
151
  // For recursive verification, use without public inputs via `removePublicInputs()`.
139
152
  fieldsWithPublicInputs){
140
153
  this.fieldsWithPublicInputs = fieldsWithPublicInputs;
141
- if (fieldsWithPublicInputs.length < CHONK_PROOF_LENGTH) {
154
+ // An empty proof (no fields) is a valid placeholder; see ChonkProofWithPublicInputs.empty().
155
+ if (fieldsWithPublicInputs.length !== 0 && fieldsWithPublicInputs.length < CHONK_PROOF_LENGTH) {
142
156
  throw new Error(`Invalid ChonkProofWithPublicInputs length: ${fieldsWithPublicInputs.length}`);
143
157
  }
144
158
  }
145
159
  getPublicInputs() {
160
+ if (this.fieldsWithPublicInputs.length === 0) {
161
+ return [];
162
+ }
146
163
  const numPublicInputs = this.fieldsWithPublicInputs.length - CHONK_PROOF_LENGTH;
147
164
  return this.fieldsWithPublicInputs.slice(0, numPublicInputs);
148
165
  }
149
166
  removePublicInputs() {
167
+ if (this.fieldsWithPublicInputs.length === 0) {
168
+ return new ChonkProof([], this.compressedProof);
169
+ }
150
170
  const numPublicInputs = this.fieldsWithPublicInputs.length - CHONK_PROOF_LENGTH;
151
171
  // Flow compressed proof bytes through so the ChonkProof can serialize efficiently
152
172
  return new ChonkProof(this.fieldsWithPublicInputs.slice(numPublicInputs), this.compressedProof);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/stdlib",
3
- "version": "5.0.0-nightly.20260530",
3
+ "version": "5.0.0-nightly.20260531",
4
4
  "type": "module",
5
5
  "inherits": [
6
6
  "../package.common.json",
@@ -92,13 +92,13 @@
92
92
  },
93
93
  "dependencies": {
94
94
  "@aws-sdk/client-s3": "^3.892.0",
95
- "@aztec/bb.js": "5.0.0-nightly.20260530",
96
- "@aztec/blob-lib": "5.0.0-nightly.20260530",
97
- "@aztec/constants": "5.0.0-nightly.20260530",
98
- "@aztec/ethereum": "5.0.0-nightly.20260530",
99
- "@aztec/foundation": "5.0.0-nightly.20260530",
100
- "@aztec/l1-artifacts": "5.0.0-nightly.20260530",
101
- "@aztec/noir-noirc_abi": "5.0.0-nightly.20260530",
95
+ "@aztec/bb.js": "5.0.0-nightly.20260531",
96
+ "@aztec/blob-lib": "5.0.0-nightly.20260531",
97
+ "@aztec/constants": "5.0.0-nightly.20260531",
98
+ "@aztec/ethereum": "5.0.0-nightly.20260531",
99
+ "@aztec/foundation": "5.0.0-nightly.20260531",
100
+ "@aztec/l1-artifacts": "5.0.0-nightly.20260531",
101
+ "@aztec/noir-noirc_abi": "5.0.0-nightly.20260531",
102
102
  "@google-cloud/storage": "^7.15.0",
103
103
  "axios": "^1.15.1",
104
104
  "json-stringify-deterministic": "1.0.12",
@@ -7,13 +7,16 @@ import { bufferSchemaFor } from '@aztec/foundation/schemas';
7
7
  import { BufferReader, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize';
8
8
 
9
9
  /**
10
- * Serialization format detection for ChonkProof is size-based:
10
+ * Serialization format detection for ChonkProof is value-based on the leading uint32:
11
+ * - EMPTY: [0: uint32] → total = 4 bytes
11
12
  * - UNCOMPRESSED (legacy): [field_count=1632: uint32] [fields...] → total ≈ 52KB (>= 40KB)
12
13
  * - COMPRESSED: [byte_count: uint32] [compressed_bytes] → total ≈ 35KB (< 40KB)
13
14
  *
14
- * Detection: if the first uint32 equals CHONK_PROOF_LENGTH (1632), it's legacy format
15
- * (field count). Otherwise, it's compressed format (byte count). The old uncompressed
16
- * format is never smaller than 40KB; compressed proofs are always smaller than 40KB.
15
+ * Detection from the first uint32: 0 means an empty proof (no fields); CHONK_PROOF_LENGTH
16
+ * (1632) means legacy format (field count); any other value is the compressed byte count.
17
+ * A real compressed proof always has a non-zero byte count, so 0 is an unambiguous sentinel
18
+ * for the empty case. The old uncompressed format is never smaller than 40KB; compressed
19
+ * proofs are always smaller than 40KB.
17
20
  */
18
21
 
19
22
  // CHONK: "Client Honk" - An UltraHonk variant with incremental folding and delayed non-native arithmetic.
@@ -32,7 +35,8 @@ export class ChonkProof {
32
35
  public fields: Fr[],
33
36
  compressedProof?: Buffer,
34
37
  ) {
35
- if (fields.length !== CHONK_PROOF_LENGTH) {
38
+ // An empty proof (no fields) is a valid placeholder; see ChonkProof.empty().
39
+ if (fields.length !== 0 && fields.length !== CHONK_PROOF_LENGTH) {
36
40
  throw new Error(`Invalid ChonkProof length: ${fields.length}`);
37
41
  }
38
42
  this.compressedProof = compressedProof;
@@ -47,7 +51,7 @@ export class ChonkProof {
47
51
  }
48
52
 
49
53
  static empty() {
50
- return new ChonkProof(new Array(CHONK_PROOF_LENGTH).fill(Fr.ZERO));
54
+ return new ChonkProof([]);
51
55
  }
52
56
 
53
57
  static random() {
@@ -77,7 +81,8 @@ export class ChonkProof {
77
81
  * Deserialize a ChonkProof from a buffer.
78
82
  * Supports both legacy (field elements) and compressed (chonk compression) formats.
79
83
  *
80
- * Size-based format detection:
84
+ * Value-based format detection on the leading uint32:
85
+ * - First uint32 == 0: empty proof (no fields), total = 4 bytes
81
86
  * - First uint32 == CHONK_PROOF_LENGTH (1632): legacy format, read field elements
82
87
  * Total proof data ≈ 52KB (always >= 40KB)
83
88
  * - Otherwise: compressed format, first uint32 is byte count of compressed data
@@ -87,6 +92,11 @@ export class ChonkProof {
87
92
  const reader = BufferReader.asReader(buffer);
88
93
  const firstUint32 = reader.readNumber();
89
94
 
95
+ if (firstUint32 === 0) {
96
+ // Empty format: a zero length encodes an empty proof.
97
+ return ChonkProof.empty();
98
+ }
99
+
90
100
  if (firstUint32 === CHONK_PROOF_LENGTH) {
91
101
  // Legacy format: firstUint32 is the field count (1632)
92
102
  // Widen to `number` to prevent TS from narrowing to literal 1632,
@@ -138,6 +148,10 @@ export class ChonkProof {
138
148
  // Compressed format: [compressed_byte_count: uint32] [compressed_bytes]
139
149
  return Buffer.concat([numToUInt32BE(this.compressedProof.length), this.compressedProof]);
140
150
  }
151
+ if (this.fields.length === 0) {
152
+ // Empty format: a single zero length, no fields.
153
+ return numToUInt32BE(0);
154
+ }
141
155
  // Legacy format: [field_count=1632: uint32] [fields...]
142
156
  return serializeToBuffer(this.fields.length, this.fields);
143
157
  }
@@ -156,17 +170,24 @@ export class ChonkProofWithPublicInputs {
156
170
  // For recursive verification, use without public inputs via `removePublicInputs()`.
157
171
  public fieldsWithPublicInputs: Fr[],
158
172
  ) {
159
- if (fieldsWithPublicInputs.length < CHONK_PROOF_LENGTH) {
173
+ // An empty proof (no fields) is a valid placeholder; see ChonkProofWithPublicInputs.empty().
174
+ if (fieldsWithPublicInputs.length !== 0 && fieldsWithPublicInputs.length < CHONK_PROOF_LENGTH) {
160
175
  throw new Error(`Invalid ChonkProofWithPublicInputs length: ${fieldsWithPublicInputs.length}`);
161
176
  }
162
177
  }
163
178
 
164
179
  public getPublicInputs() {
180
+ if (this.fieldsWithPublicInputs.length === 0) {
181
+ return [];
182
+ }
165
183
  const numPublicInputs = this.fieldsWithPublicInputs.length - CHONK_PROOF_LENGTH;
166
184
  return this.fieldsWithPublicInputs.slice(0, numPublicInputs);
167
185
  }
168
186
 
169
187
  public removePublicInputs() {
188
+ if (this.fieldsWithPublicInputs.length === 0) {
189
+ return new ChonkProof([], this.compressedProof);
190
+ }
170
191
  const numPublicInputs = this.fieldsWithPublicInputs.length - CHONK_PROOF_LENGTH;
171
192
  // Flow compressed proof bytes through so the ChonkProof can serialize efficiently
172
193
  return new ChonkProof(this.fieldsWithPublicInputs.slice(numPublicInputs), this.compressedProof);