@aztec/blob-lib 0.0.1-fake-c83136db25 → 0.0.1-fake-ceab37513c

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 (65) hide show
  1. package/dest/blob.d.ts +98 -52
  2. package/dest/blob.d.ts.map +1 -1
  3. package/dest/blob.js +167 -73
  4. package/dest/blob_batching.d.ts +48 -15
  5. package/dest/blob_batching.d.ts.map +1 -1
  6. package/dest/blob_batching.js +120 -81
  7. package/dest/blob_batching_public_inputs.d.ts +71 -0
  8. package/dest/blob_batching_public_inputs.d.ts.map +1 -0
  9. package/dest/blob_batching_public_inputs.js +168 -0
  10. package/dest/encoding.d.ts +62 -22
  11. package/dest/encoding.d.ts.map +1 -1
  12. package/dest/encoding.js +104 -114
  13. package/dest/index.d.ts +2 -5
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +15 -5
  16. package/dest/sponge_blob.d.ts +9 -13
  17. package/dest/sponge_blob.d.ts.map +1 -1
  18. package/dest/sponge_blob.js +17 -28
  19. package/dest/testing.d.ts +12 -7
  20. package/dest/testing.d.ts.map +1 -1
  21. package/dest/testing.js +41 -54
  22. package/dest/types.d.ts +0 -2
  23. package/dest/types.d.ts.map +1 -1
  24. package/dest/types.js +0 -2
  25. package/package.json +4 -5
  26. package/src/blob.ts +198 -76
  27. package/src/blob_batching.ts +137 -109
  28. package/src/blob_batching_public_inputs.ts +252 -0
  29. package/src/encoding.ts +120 -136
  30. package/src/index.ts +18 -5
  31. package/src/sponge_blob.ts +14 -24
  32. package/src/testing.ts +40 -55
  33. package/src/types.ts +2 -2
  34. package/dest/blob_utils.d.ts +0 -30
  35. package/dest/blob_utils.d.ts.map +0 -1
  36. package/dest/blob_utils.js +0 -60
  37. package/dest/circuit_types/blob_accumulator.d.ts +0 -21
  38. package/dest/circuit_types/blob_accumulator.d.ts.map +0 -1
  39. package/dest/circuit_types/blob_accumulator.js +0 -58
  40. package/dest/circuit_types/final_blob_accumulator.d.ts +0 -22
  41. package/dest/circuit_types/final_blob_accumulator.d.ts.map +0 -1
  42. package/dest/circuit_types/final_blob_accumulator.js +0 -63
  43. package/dest/circuit_types/final_blob_batching_challenges.d.ts +0 -15
  44. package/dest/circuit_types/final_blob_batching_challenges.d.ts.map +0 -1
  45. package/dest/circuit_types/final_blob_batching_challenges.js +0 -25
  46. package/dest/circuit_types/index.d.ts +0 -4
  47. package/dest/circuit_types/index.d.ts.map +0 -1
  48. package/dest/circuit_types/index.js +0 -4
  49. package/dest/deserialize.d.ts +0 -14
  50. package/dest/deserialize.d.ts.map +0 -1
  51. package/dest/deserialize.js +0 -33
  52. package/dest/hash.d.ts +0 -35
  53. package/dest/hash.d.ts.map +0 -1
  54. package/dest/hash.js +0 -69
  55. package/dest/kzg_context.d.ts +0 -4
  56. package/dest/kzg_context.d.ts.map +0 -1
  57. package/dest/kzg_context.js +0 -5
  58. package/src/blob_utils.ts +0 -71
  59. package/src/circuit_types/blob_accumulator.ts +0 -84
  60. package/src/circuit_types/final_blob_accumulator.ts +0 -75
  61. package/src/circuit_types/final_blob_batching_challenges.ts +0 -29
  62. package/src/circuit_types/index.ts +0 -4
  63. package/src/deserialize.ts +0 -38
  64. package/src/hash.ts +0 -77
  65. package/src/kzg_context.ts +0 -5
@@ -1,6 +1,6 @@
1
1
  import { BLS12Fr, BLS12Point, Fr } from '@aztec/foundation/fields';
2
+ import { BufferReader } from '@aztec/foundation/serialize';
2
3
  import { Blob } from './blob.js';
3
- import { BlobAccumulator, FinalBlobAccumulator, FinalBlobBatchingChallenges } from './circuit_types/index.js';
4
4
  /**
5
5
  * A class to create, manage, and prove batched EVM blobs.
6
6
  */
@@ -33,13 +33,13 @@ export declare class BatchedBlob {
33
33
  *
34
34
  * @returns A batched blob.
35
35
  */
36
- static batch(blobs: Blob[][]): Promise<BatchedBlob>;
36
+ static batch(blobs: Blob[]): Promise<BatchedBlob>;
37
37
  /**
38
38
  * Returns an empty BatchedBlobAccumulator with precomputed challenges from all blobs in the epoch.
39
39
  * @dev MUST input all blobs to be broadcast. Does not work in multiple calls because z and gamma are calculated
40
40
  * beforehand from ALL blobs.
41
41
  */
42
- static newAccumulator(blobs: Blob[][]): Promise<BatchedBlobAccumulator>;
42
+ static newAccumulator(blobs: Blob[]): Promise<BatchedBlobAccumulator>;
43
43
  /**
44
44
  * Gets the final challenges based on all blobs and their elements to perform a multi opening proof.
45
45
  * Used in BatchedBlobAccumulator as 'finalZ' and finalGamma':
@@ -48,13 +48,12 @@ export declare class BatchedBlob {
48
48
  * - used such that p_i(z) = y_i = Blob.evaluationY for all n blob polynomials p_i().
49
49
  * - gamma = H(H(...H(H(y_0, y_1) y_2)..y_n), z)
50
50
  * - used such that y = sum_i { gamma^i * y_i }, and C = sum_i { gamma^i * C_i }, for all blob evaluations y_i (see above) and commitments C_i.
51
- *
52
- * @param blobs - The blobs to precompute the challenges for. Each sub-array is the blobs for an L1 block.
53
51
  * @returns Challenges z and gamma.
54
52
  */
55
- static precomputeBatchedBlobChallenges(blobs: Blob[][]): Promise<FinalBlobBatchingChallenges>;
56
- verify(): boolean;
53
+ static precomputeBatchedBlobChallenges(blobs: Blob[]): Promise<FinalBlobBatchingChallenges>;
54
+ static precomputeEmptyBatchedBlobChallenges(): Promise<FinalBlobBatchingChallenges>;
57
55
  getEthVersionedBlobHash(): Buffer;
56
+ static getEthVersionedBlobHash(commitment: Buffer): Buffer;
58
57
  /**
59
58
  * Returns a proof of opening of the blobs to verify on L1 using the point evaluation precompile:
60
59
  *
@@ -67,10 +66,33 @@ export declare class BatchedBlob {
67
66
  * See https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile
68
67
  */
69
68
  getEthBlobEvaluationInputs(): `0x${string}`;
70
- toFinalBlobAccumulator(): FinalBlobAccumulator;
71
69
  }
72
70
  /**
73
- * See noir-projects/noir-protocol-circuits/crates/blob/src/abis/blob_accumulator.nr
71
+ * Final values z and gamma are injected into each block root circuit. We ensure they are correct by:
72
+ * - Checking equality in each block merge circuit and propagating up
73
+ * - Checking final z_acc == z in root circuit
74
+ * - Checking final gamma_acc == gamma in root circuit
75
+ *
76
+ * - z = H(...H(H(z_0, z_1) z_2)..z_n)
77
+ * - where z_i = H(H(fields of blob_i), C_i),
78
+ * - used such that p_i(z) = y_i = Blob.evaluationY for all n blob polynomials p_i().
79
+ * - gamma = H(H(...H(H(y_0, y_1) y_2)..y_n), z)
80
+ * - used such that y = sum_i { gamma^i * y_i }, and C = sum_i { gamma^i * C_i }
81
+ * for all blob evaluations y_i (see above) and commitments C_i.
82
+ *
83
+ * Iteratively calculated by BlobAccumulatorPublicInputs.accumulate() in nr. See also precomputeBatchedBlobChallenges() above.
84
+ */
85
+ export declare class FinalBlobBatchingChallenges {
86
+ readonly z: Fr;
87
+ readonly gamma: BLS12Fr;
88
+ constructor(z: Fr, gamma: BLS12Fr);
89
+ equals(other: FinalBlobBatchingChallenges): boolean;
90
+ static empty(): FinalBlobBatchingChallenges;
91
+ static fromBuffer(buffer: Buffer | BufferReader): FinalBlobBatchingChallenges;
92
+ toBuffer(): Buffer<ArrayBufferLike>;
93
+ }
94
+ /**
95
+ * See noir-projects/noir-protocol-circuits/crates/blob/src/blob_batching_public_inputs.nr -> BlobAccumulatorPublicInputs
74
96
  */
75
97
  export declare class BatchedBlobAccumulator {
76
98
  /** Hash of Cs (to link to L1 blob hashes). */
@@ -114,6 +136,21 @@ export declare class BatchedBlobAccumulator {
114
136
  gammaPow: BLS12Fr,
115
137
  /** Final challenge values used in evaluation. Optimistically input and checked in the final acc. */
116
138
  finalBlobChallenges: FinalBlobBatchingChallenges);
139
+ /**
140
+ * Init the first accumulation state of the epoch.
141
+ * We assume the input blob has not been evaluated at z.
142
+ *
143
+ * First state of the accumulator:
144
+ * - v_acc := sha256(C_0)
145
+ * - z_acc := z_0
146
+ * - y_acc := gamma^0 * y_0 = y_0
147
+ * - c_acc := gamma^0 * c_0 = c_0
148
+ * - gamma_acc := poseidon2(y_0.limbs)
149
+ * - gamma^(i + 1) = gamma^1 = gamma // denoted gamma_pow_acc
150
+ *
151
+ * @returns An initial blob accumulator.
152
+ */
153
+ static initialize(blob: Blob, finalBlobChallenges: FinalBlobBatchingChallenges): Promise<BatchedBlobAccumulator>;
117
154
  /**
118
155
  * Create the empty accumulation state of the epoch.
119
156
  * @returns An empty blob accumulator with challenges.
@@ -124,11 +161,10 @@ export declare class BatchedBlobAccumulator {
124
161
  * We assume the input blob has not been evaluated at z.
125
162
  * @returns An updated blob accumulator.
126
163
  */
127
- private accumulate;
164
+ accumulate(blob: Blob): Promise<BatchedBlobAccumulator>;
128
165
  /**
129
166
  * Given blobs, accumulate all state.
130
167
  * We assume the input blobs have not been evaluated at z.
131
- * @param blobs - The blobs to accumulate. They should be in the same L1 block.
132
168
  * @returns An updated blob accumulator.
133
169
  */
134
170
  accumulateBlobs(blobs: Blob[]): Promise<BatchedBlobAccumulator>;
@@ -143,13 +179,10 @@ export declare class BatchedBlobAccumulator {
143
179
  * - c := c_acc (final commitment to be checked on L1)
144
180
  * - gamma := poseidon2(gamma_acc, z) (challenge for linear combination of y and C, above)
145
181
  *
146
- * @param verifyProof - Whether to verify the KZG proof.
147
182
  * @returns A batched blob.
148
183
  */
149
- finalize(verifyProof?: boolean): Promise<BatchedBlob>;
184
+ finalize(): Promise<BatchedBlob>;
150
185
  isEmptyState(): boolean;
151
186
  clone(): BatchedBlobAccumulator;
152
- toBlobAccumulator(): BlobAccumulator;
153
- toFinalBlobAccumulator(): FinalBlobAccumulator;
154
187
  }
155
188
  //# sourceMappingURL=blob_batching.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"blob_batching.d.ts","sourceRoot":"","sources":["../src/blob_batching.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAI9G;;GAEG;AACH,qBAAa,WAAW;IAEpB,8CAA8C;aAC9B,mBAAmB,EAAE,EAAE;IACvC,gDAAgD;aAChC,CAAC,EAAE,EAAE;IACrB,mFAAmF;aACnE,CAAC,EAAE,OAAO;IAC1B,kFAAkF;aAClE,UAAU,EAAE,UAAU;IACtC,4HAA4H;aAC5G,CAAC,EAAE,UAAU;;IAT7B,8CAA8C;IAC9B,mBAAmB,EAAE,EAAE;IACvC,gDAAgD;IAChC,CAAC,EAAE,EAAE;IACrB,mFAAmF;IACnE,CAAC,EAAE,OAAO;IAC1B,kFAAkF;IAClE,UAAU,EAAE,UAAU;IACtC,4HAA4H;IAC5G,CAAC,EAAE,UAAU;IAG/B;;;;;;OAMG;WACU,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBzD;;;;OAIG;WACU,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAK7E;;;;;;;;;;;OAWG;WACU,+BAA+B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAmCnG,MAAM;IAKN,uBAAuB,IAAI,MAAM;IAIjC;;;;;;;;;;OAUG;IACH,0BAA0B,IAAI,KAAK,MAAM,EAAE;IAW3C,sBAAsB;CAGvB;AAED;;GAEG;AACH,qBAAa,sBAAsB;IAE/B,8CAA8C;aAC9B,sBAAsB,EAAE,EAAE;IAC1C,sEAAsE;aACtD,IAAI,EAAE,EAAE;IACxB,yGAAyG;aACzF,IAAI,EAAE,OAAO;IAC7B,qGAAqG;aACrF,IAAI,EAAE,UAAU;IAChC,oGAAoG;aACpF,IAAI,EAAE,UAAU;IAChC;;;;OAIG;aACa,QAAQ,EAAE,EAAE;IAC5B,uGAAuG;aACvF,QAAQ,EAAE,OAAO;IACjC,oGAAoG;aACpF,mBAAmB,EAAE,2BAA2B;;IAnBhE,8CAA8C;IAC9B,sBAAsB,EAAE,EAAE;IAC1C,sEAAsE;IACtD,IAAI,EAAE,EAAE;IACxB,yGAAyG;IACzF,IAAI,EAAE,OAAO;IAC7B,qGAAqG;IACrF,IAAI,EAAE,UAAU;IAChC,oGAAoG;IACpF,IAAI,EAAE,UAAU;IAChC;;;;OAIG;IACa,QAAQ,EAAE,EAAE;IAC5B,uGAAuG;IACvF,QAAQ,EAAE,OAAO;IACjC,oGAAoG;IACpF,mBAAmB,EAAE,2BAA2B;IAGlE;;;OAGG;IACH,MAAM,CAAC,iBAAiB,CAAC,mBAAmB,EAAE,2BAA2B,GAAG,sBAAsB;IAalG;;;;OAIG;YACW,UAAU;IAyCxB;;;;;OAKG;IACG,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE;IAkBnC;;;;;;;;;;;;;OAaG;IACG,QAAQ,CAAC,WAAW,UAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;IAwBzD,YAAY;IAYZ,KAAK;IAaL,iBAAiB;IAWjB,sBAAsB;CAGvB"}
1
+ {"version":3,"file":"blob_batching.d.ts","sourceRoot":"","sources":["../src/blob_batching.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { AZTEC_MAX_EPOCH_DURATION, BLOBS_PER_BLOCK } from '@aztec/constants';
2
- import { poseidon2Hash, sha256ToField } from '@aztec/foundation/crypto';
2
+ import { poseidon2Hash, sha256, sha256ToField } from '@aztec/foundation/crypto';
3
3
  import { BLS12Fr, BLS12Point, Fr } from '@aztec/foundation/fields';
4
- import { computeBlobFieldsHashFromBlobs } from './blob_utils.js';
5
- import { BlobAccumulator, FinalBlobAccumulator, FinalBlobBatchingChallenges } from './circuit_types/index.js';
6
- import { computeEthVersionedBlobHash, hashNoirBigNumLimbs } from './hash.js';
7
- import { kzg } from './kzg_context.js';
4
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
5
+ // Importing directly from 'c-kzg' does not work:
6
+ import cKzg from 'c-kzg';
7
+ import { Blob, VERSIONED_HASH_VERSION_KZG } from './blob.js';
8
+ const { computeKzgProof, verifyKzgProof } = cKzg;
8
9
  /**
9
10
  * A class to create, manage, and prove batched EVM blobs.
10
11
  */ export class BatchedBlob {
@@ -27,15 +28,14 @@ import { kzg } from './kzg_context.js';
27
28
  *
28
29
  * @returns A batched blob.
29
30
  */ static async batch(blobs) {
30
- if (blobs.length > AZTEC_MAX_EPOCH_DURATION) {
31
- throw new Error(`Too many blocks sent to batch(). The maximum is ${AZTEC_MAX_EPOCH_DURATION}. Got ${blobs.length}.`);
31
+ const numBlobs = blobs.length;
32
+ if (numBlobs > BLOBS_PER_BLOCK * AZTEC_MAX_EPOCH_DURATION) {
33
+ throw new Error(`Too many blobs (${numBlobs}) sent to batch(). The maximum is ${BLOBS_PER_BLOCK * AZTEC_MAX_EPOCH_DURATION}.`);
32
34
  }
33
35
  // Precalculate the values (z and gamma) and initialize the accumulator:
34
36
  let acc = await this.newAccumulator(blobs);
35
37
  // Now we can create a multi opening proof of all input blobs:
36
- for (const blockBlobs of blobs){
37
- acc = await acc.accumulateBlobs(blockBlobs);
38
- }
38
+ acc = await acc.accumulateBlobs(blobs);
39
39
  return await acc.finalize();
40
40
  }
41
41
  /**
@@ -54,42 +54,27 @@ import { kzg } from './kzg_context.js';
54
54
  * - used such that p_i(z) = y_i = Blob.evaluationY for all n blob polynomials p_i().
55
55
  * - gamma = H(H(...H(H(y_0, y_1) y_2)..y_n), z)
56
56
  * - used such that y = sum_i { gamma^i * y_i }, and C = sum_i { gamma^i * C_i }, for all blob evaluations y_i (see above) and commitments C_i.
57
- *
58
- * @param blobs - The blobs to precompute the challenges for. Each sub-array is the blobs for an L1 block.
59
57
  * @returns Challenges z and gamma.
60
58
  */ static async precomputeBatchedBlobChallenges(blobs) {
61
- // Compute the final challenge z to evaluate the blobs.
62
- let z;
63
- for (const blockBlobs of blobs){
64
- // Compute the hash of all the fields in the block.
65
- const blobFieldsHash = await computeBlobFieldsHashFromBlobs(blockBlobs);
66
- for (const blob of blockBlobs){
67
- // Compute the challenge z for each blob and accumulate it.
68
- const challengeZ = await blob.computeChallengeZ(blobFieldsHash);
69
- if (!z) {
70
- z = challengeZ;
71
- } else {
72
- z = await poseidon2Hash([
73
- z,
74
- challengeZ
75
- ]);
76
- }
77
- }
78
- }
79
- if (!z) {
80
- throw new Error('No blobs to precompute challenges for.');
59
+ // We need to precompute the final challenge values to evaluate the blobs.
60
+ let z = blobs[0].challengeZ;
61
+ // We start at i = 1, because z is initialized as the first blob's challenge.
62
+ for(let i = 1; i < blobs.length; i++){
63
+ z = await poseidon2Hash([
64
+ z,
65
+ blobs[i].challengeZ
66
+ ]);
81
67
  }
82
68
  // Now we have a shared challenge for all blobs, evaluate them...
83
- const allBlobs = blobs.flat();
84
- const proofObjects = allBlobs.map((b)=>b.evaluate(z));
85
- const evaluations = await Promise.all(proofObjects.map(({ y })=>hashNoirBigNumLimbs(y)));
69
+ const proofObjects = blobs.map((b)=>computeKzgProof(b.data, z.toBuffer()));
70
+ const evaluations = proofObjects.map(([_, evaluation])=>BLS12Fr.fromBuffer(Buffer.from(evaluation)));
86
71
  // ...and find the challenge for the linear combination of blobs.
87
- let gamma = evaluations[0];
72
+ let gamma = await hashNoirBigNumLimbs(evaluations[0]);
88
73
  // We start at i = 1, because gamma is initialized as the first blob's evaluation.
89
- for(let i = 1; i < allBlobs.length; i++){
74
+ for(let i = 1; i < blobs.length; i++){
90
75
  gamma = await poseidon2Hash([
91
76
  gamma,
92
- evaluations[i]
77
+ await hashNoirBigNumLimbs(evaluations[i])
93
78
  ]);
94
79
  }
95
80
  gamma = await poseidon2Hash([
@@ -98,12 +83,33 @@ import { kzg } from './kzg_context.js';
98
83
  ]);
99
84
  return new FinalBlobBatchingChallenges(z, BLS12Fr.fromBN254Fr(gamma));
100
85
  }
101
- verify() {
102
- return kzg.verifyKzgProof(this.commitment.compress(), this.z.toBuffer(), this.y.toBuffer(), this.q.compress());
86
+ static async precomputeEmptyBatchedBlobChallenges() {
87
+ const blobs = [
88
+ await Blob.fromFields([])
89
+ ];
90
+ // We need to precompute the final challenge values to evaluate the blobs.
91
+ const z = blobs[0].challengeZ;
92
+ // Now we have a shared challenge for all blobs, evaluate them...
93
+ const proofObjects = blobs.map((b)=>computeKzgProof(b.data, z.toBuffer()));
94
+ const evaluations = proofObjects.map(([_, evaluation])=>BLS12Fr.fromBuffer(Buffer.from(evaluation)));
95
+ // ...and find the challenge for the linear combination of blobs.
96
+ let gamma = await hashNoirBigNumLimbs(evaluations[0]);
97
+ gamma = await poseidon2Hash([
98
+ gamma,
99
+ z
100
+ ]);
101
+ return new FinalBlobBatchingChallenges(z, BLS12Fr.fromBN254Fr(gamma));
103
102
  }
104
103
  // Returns ethereum's versioned blob hash, following kzg_to_versioned_hash: https://eips.ethereum.org/EIPS/eip-4844#helpers
105
104
  getEthVersionedBlobHash() {
106
- return computeEthVersionedBlobHash(this.commitment.compress());
105
+ const hash = sha256(this.commitment.compress());
106
+ hash[0] = VERSIONED_HASH_VERSION_KZG;
107
+ return hash;
108
+ }
109
+ static getEthVersionedBlobHash(commitment) {
110
+ const hash = sha256(commitment);
111
+ hash[0] = VERSIONED_HASH_VERSION_KZG;
112
+ return hash;
107
113
  }
108
114
  /**
109
115
  * Returns a proof of opening of the blobs to verify on L1 using the point evaluation precompile:
@@ -125,12 +131,44 @@ import { kzg } from './kzg_context.js';
125
131
  ]);
126
132
  return `0x${buf.toString('hex')}`;
127
133
  }
128
- toFinalBlobAccumulator() {
129
- return new FinalBlobAccumulator(this.blobCommitmentsHash, this.z, this.y, this.commitment);
134
+ }
135
+ /**
136
+ * Final values z and gamma are injected into each block root circuit. We ensure they are correct by:
137
+ * - Checking equality in each block merge circuit and propagating up
138
+ * - Checking final z_acc == z in root circuit
139
+ * - Checking final gamma_acc == gamma in root circuit
140
+ *
141
+ * - z = H(...H(H(z_0, z_1) z_2)..z_n)
142
+ * - where z_i = H(H(fields of blob_i), C_i),
143
+ * - used such that p_i(z) = y_i = Blob.evaluationY for all n blob polynomials p_i().
144
+ * - gamma = H(H(...H(H(y_0, y_1) y_2)..y_n), z)
145
+ * - used such that y = sum_i { gamma^i * y_i }, and C = sum_i { gamma^i * C_i }
146
+ * for all blob evaluations y_i (see above) and commitments C_i.
147
+ *
148
+ * Iteratively calculated by BlobAccumulatorPublicInputs.accumulate() in nr. See also precomputeBatchedBlobChallenges() above.
149
+ */ export class FinalBlobBatchingChallenges {
150
+ z;
151
+ gamma;
152
+ constructor(z, gamma){
153
+ this.z = z;
154
+ this.gamma = gamma;
155
+ }
156
+ equals(other) {
157
+ return this.z.equals(other.z) && this.gamma.equals(other.gamma);
158
+ }
159
+ static empty() {
160
+ return new FinalBlobBatchingChallenges(Fr.ZERO, BLS12Fr.ZERO);
161
+ }
162
+ static fromBuffer(buffer) {
163
+ const reader = BufferReader.asReader(buffer);
164
+ return new FinalBlobBatchingChallenges(Fr.fromBuffer(reader), reader.readObject(BLS12Fr));
165
+ }
166
+ toBuffer() {
167
+ return serializeToBuffer(this.z, this.gamma);
130
168
  }
131
169
  }
132
170
  /**
133
- * See noir-projects/noir-protocol-circuits/crates/blob/src/abis/blob_accumulator.nr
171
+ * See noir-projects/noir-protocol-circuits/crates/blob/src/blob_batching_public_inputs.nr -> BlobAccumulatorPublicInputs
134
172
  */ export class BatchedBlobAccumulator {
135
173
  blobCommitmentsHashAcc;
136
174
  zAcc;
@@ -155,6 +193,27 @@ import { kzg } from './kzg_context.js';
155
193
  this.finalBlobChallenges = finalBlobChallenges;
156
194
  }
157
195
  /**
196
+ * Init the first accumulation state of the epoch.
197
+ * We assume the input blob has not been evaluated at z.
198
+ *
199
+ * First state of the accumulator:
200
+ * - v_acc := sha256(C_0)
201
+ * - z_acc := z_0
202
+ * - y_acc := gamma^0 * y_0 = y_0
203
+ * - c_acc := gamma^0 * c_0 = c_0
204
+ * - gamma_acc := poseidon2(y_0.limbs)
205
+ * - gamma^(i + 1) = gamma^1 = gamma // denoted gamma_pow_acc
206
+ *
207
+ * @returns An initial blob accumulator.
208
+ */ static async initialize(blob, finalBlobChallenges) {
209
+ const [q, evaluation] = computeKzgProof(blob.data, finalBlobChallenges.z.toBuffer());
210
+ const firstY = BLS12Fr.fromBuffer(Buffer.from(evaluation));
211
+ // Here, i = 0, so:
212
+ return new BatchedBlobAccumulator(sha256ToField([
213
+ blob.commitment
214
+ ]), blob.challengeZ, firstY, BLS12Point.decompress(blob.commitment), BLS12Point.decompress(Buffer.from(q)), await hashNoirBigNumLimbs(firstY), finalBlobChallenges.gamma, finalBlobChallenges);
215
+ }
216
+ /**
158
217
  * Create the empty accumulation state of the epoch.
159
218
  * @returns An empty blob accumulator with challenges.
160
219
  */ static newWithChallenges(finalBlobChallenges) {
@@ -164,32 +223,20 @@ import { kzg } from './kzg_context.js';
164
223
  * Given blob i, accumulate all state.
165
224
  * We assume the input blob has not been evaluated at z.
166
225
  * @returns An updated blob accumulator.
167
- */ async accumulate(blob, blobFieldsHash) {
168
- const { proof, y: thisY } = blob.evaluate(this.finalBlobChallenges.z);
169
- const thisC = BLS12Point.decompress(blob.commitment);
170
- const thisQ = BLS12Point.decompress(proof);
171
- const blobChallengeZ = await blob.computeChallengeZ(blobFieldsHash);
226
+ */ async accumulate(blob) {
172
227
  if (this.isEmptyState()) {
173
- /**
174
- * Init the first accumulation state of the epoch.
175
- * - v_acc := sha256(C_0)
176
- * - z_acc := z_0
177
- * - y_acc := gamma^0 * y_0 = y_0
178
- * - c_acc := gamma^0 * c_0 = c_0
179
- * - gamma_acc := poseidon2(y_0.limbs)
180
- * - gamma^(i + 1) = gamma^1 = gamma // denoted gamma_pow_acc
181
- */ return new BatchedBlobAccumulator(sha256ToField([
182
- blob.commitment
183
- ]), blobChallengeZ, thisY, thisC, thisQ, await hashNoirBigNumLimbs(thisY), this.finalBlobChallenges.gamma, this.finalBlobChallenges);
228
+ return BatchedBlobAccumulator.initialize(blob, this.finalBlobChallenges);
184
229
  } else {
230
+ const [q, evaluation] = computeKzgProof(blob.data, this.finalBlobChallenges.z.toBuffer());
231
+ const thisY = BLS12Fr.fromBuffer(Buffer.from(evaluation));
185
232
  // Moving from i - 1 to i, so:
186
233
  return new BatchedBlobAccumulator(sha256ToField([
187
234
  this.blobCommitmentsHashAcc,
188
235
  blob.commitment
189
236
  ]), await poseidon2Hash([
190
237
  this.zAcc,
191
- blobChallengeZ
192
- ]), this.yAcc.add(thisY.mul(this.gammaPow)), this.cAcc.add(thisC.mul(this.gammaPow)), this.qAcc.add(thisQ.mul(this.gammaPow)), await poseidon2Hash([
238
+ blob.challengeZ
239
+ ]), this.yAcc.add(thisY.mul(this.gammaPow)), this.cAcc.add(BLS12Point.decompress(blob.commitment).mul(this.gammaPow)), this.qAcc.add(BLS12Point.decompress(Buffer.from(q)).mul(this.gammaPow)), await poseidon2Hash([
193
240
  this.gammaAcc,
194
241
  await hashNoirBigNumLimbs(thisY)
195
242
  ]), this.gammaPow.mul(this.finalBlobChallenges.gamma), this.finalBlobChallenges);
@@ -198,18 +245,12 @@ import { kzg } from './kzg_context.js';
198
245
  /**
199
246
  * Given blobs, accumulate all state.
200
247
  * We assume the input blobs have not been evaluated at z.
201
- * @param blobs - The blobs to accumulate. They should be in the same L1 block.
202
248
  * @returns An updated blob accumulator.
203
249
  */ async accumulateBlobs(blobs) {
204
- if (blobs.length > BLOBS_PER_BLOCK) {
205
- throw new Error(`Too many blobs to accumulate. The maximum is ${BLOBS_PER_BLOCK} per block. Got ${blobs.length}.`);
206
- }
207
- // Compute the hash of all the fields in the block.
208
- const blobFieldsHash = await computeBlobFieldsHashFromBlobs(blobs);
209
250
  // Initialize the acc to iterate over:
210
251
  let acc = this.clone();
211
- for (const blob of blobs){
212
- acc = await acc.accumulate(blob, blobFieldsHash);
252
+ for(let i = 0; i < blobs.length; i++){
253
+ acc = await acc.accumulate(blobs[i]);
213
254
  }
214
255
  return acc;
215
256
  }
@@ -224,9 +265,8 @@ import { kzg } from './kzg_context.js';
224
265
  * - c := c_acc (final commitment to be checked on L1)
225
266
  * - gamma := poseidon2(gamma_acc, z) (challenge for linear combination of y and C, above)
226
267
  *
227
- * @param verifyProof - Whether to verify the KZG proof.
228
268
  * @returns A batched blob.
229
- */ async finalize(verifyProof = false) {
269
+ */ async finalize() {
230
270
  // All values in acc are final, apart from gamma := poseidon2(gammaAcc, z):
231
271
  const calculatedGamma = await poseidon2Hash([
232
272
  this.gammaAcc,
@@ -239,11 +279,10 @@ import { kzg } from './kzg_context.js';
239
279
  if (!calculatedGamma.equals(this.finalBlobChallenges.gamma.toBN254Fr())) {
240
280
  throw new Error(`Blob batching mismatch: accumulated gamma ${calculatedGamma} does not equal injected gamma ${this.finalBlobChallenges.gamma.toBN254Fr()}`);
241
281
  }
242
- const batchedBlob = new BatchedBlob(this.blobCommitmentsHashAcc, this.zAcc, this.yAcc, this.cAcc, this.qAcc);
243
- if (verifyProof && !batchedBlob.verify()) {
282
+ if (!verifyKzgProof(this.cAcc.compress(), this.zAcc.toBuffer(), this.yAcc.toBuffer(), this.qAcc.compress())) {
244
283
  throw new Error(`KZG proof did not verify.`);
245
284
  }
246
- return batchedBlob;
285
+ return new BatchedBlob(this.blobCommitmentsHashAcc, this.zAcc, this.yAcc, this.cAcc, this.qAcc);
247
286
  }
248
287
  isEmptyState() {
249
288
  return this.blobCommitmentsHashAcc.isZero() && this.zAcc.isZero() && this.yAcc.isZero() && this.cAcc.isZero() && this.qAcc.isZero() && this.gammaAcc.isZero() && this.gammaPow.isZero();
@@ -251,10 +290,10 @@ import { kzg } from './kzg_context.js';
251
290
  clone() {
252
291
  return new BatchedBlobAccumulator(Fr.fromBuffer(this.blobCommitmentsHashAcc.toBuffer()), Fr.fromBuffer(this.zAcc.toBuffer()), BLS12Fr.fromBuffer(this.yAcc.toBuffer()), BLS12Point.fromBuffer(this.cAcc.toBuffer()), BLS12Point.fromBuffer(this.qAcc.toBuffer()), Fr.fromBuffer(this.gammaAcc.toBuffer()), BLS12Fr.fromBuffer(this.gammaPow.toBuffer()), FinalBlobBatchingChallenges.fromBuffer(this.finalBlobChallenges.toBuffer()));
253
292
  }
254
- toBlobAccumulator() {
255
- return new BlobAccumulator(this.blobCommitmentsHashAcc, this.zAcc, this.yAcc, this.cAcc, this.gammaAcc, this.gammaPow);
256
- }
257
- toFinalBlobAccumulator() {
258
- return new FinalBlobAccumulator(this.blobCommitmentsHashAcc, this.zAcc, this.yAcc, this.cAcc);
259
- }
293
+ }
294
+ // To mimic the hash accumulation in the rollup circuits, here we hash
295
+ // each u128 limb of the noir bignum struct representing the BLS field.
296
+ async function hashNoirBigNumLimbs(field) {
297
+ const num = field.toNoirBigNum();
298
+ return await poseidon2Hash(num.limbs.map(Fr.fromHexString));
260
299
  }
@@ -0,0 +1,71 @@
1
+ import { BLS12Fr, BLS12Point, Fr } from '@aztec/foundation/fields';
2
+ import { BufferReader, FieldReader } from '@aztec/foundation/serialize';
3
+ import { inspect } from 'util';
4
+ import { Blob } from './blob.js';
5
+ import { BatchedBlob, BatchedBlobAccumulator, FinalBlobBatchingChallenges } from './blob_batching.js';
6
+ /**
7
+ * See nr BlobAccumulatorPublicInputs and ts BatchedBlobAccumulator for documentation.
8
+ */
9
+ export declare class BlobAccumulatorPublicInputs {
10
+ blobCommitmentsHashAcc: Fr;
11
+ zAcc: Fr;
12
+ yAcc: BLS12Fr;
13
+ cAcc: BLS12Point;
14
+ gammaAcc: Fr;
15
+ gammaPowAcc: BLS12Fr;
16
+ constructor(blobCommitmentsHashAcc: Fr, zAcc: Fr, yAcc: BLS12Fr, cAcc: BLS12Point, gammaAcc: Fr, gammaPowAcc: BLS12Fr);
17
+ static empty(): BlobAccumulatorPublicInputs;
18
+ equals(other: BlobAccumulatorPublicInputs): boolean;
19
+ static fromBuffer(buffer: Buffer | BufferReader): BlobAccumulatorPublicInputs;
20
+ toBuffer(): Buffer<ArrayBufferLike>;
21
+ /**
22
+ * Given blobs, accumulate all public inputs state.
23
+ * We assume the input blobs have not been evaluated at z.
24
+ * NOTE: Does NOT accumulate non circuit values including Q. This exists to simulate/check exactly what the circuit is doing
25
+ * and is unsafe for other use. For that reason, a toBatchedBlobAccumulator does not exist. See evaluateBlobs() oracle for usage.
26
+ * @returns An updated blob accumulator.
27
+ */
28
+ accumulateBlobs(blobs: Blob[], finalBlobChallenges: FinalBlobBatchingChallenges): Promise<BlobAccumulatorPublicInputs>;
29
+ toFields(): Fr[];
30
+ static fromFields(fields: Fr[] | FieldReader): BlobAccumulatorPublicInputs;
31
+ /**
32
+ * Converts from an accumulator to a struct for the public inputs of our rollup circuits.
33
+ * @returns A BlobAccumulatorPublicInputs instance.
34
+ */
35
+ static fromBatchedBlobAccumulator(accumulator: BatchedBlobAccumulator): BlobAccumulatorPublicInputs;
36
+ }
37
+ /**
38
+ * See nr FinalBlobAccumulatorPublicInputs and ts BatchedBlobAccumulator for documentation.
39
+ */
40
+ export declare class FinalBlobAccumulatorPublicInputs {
41
+ blobCommitmentsHash: Fr;
42
+ z: Fr;
43
+ y: BLS12Fr;
44
+ c: BLS12Point;
45
+ constructor(blobCommitmentsHash: Fr, z: Fr, y: BLS12Fr, c: BLS12Point);
46
+ static empty(): FinalBlobAccumulatorPublicInputs;
47
+ static fromBuffer(buffer: Buffer | BufferReader): FinalBlobAccumulatorPublicInputs;
48
+ toBuffer(): Buffer<ArrayBufferLike>;
49
+ static fromBatchedBlob(blob: BatchedBlob): FinalBlobAccumulatorPublicInputs;
50
+ toFields(): Fr[];
51
+ toString(): string;
52
+ equals(other: FinalBlobAccumulatorPublicInputs): boolean;
53
+ static random(): FinalBlobAccumulatorPublicInputs;
54
+ static fromBatchedBlobAccumulator(accumulator: BatchedBlobAccumulator): FinalBlobAccumulatorPublicInputs;
55
+ [inspect.custom](): string;
56
+ }
57
+ /**
58
+ * startBlobAccumulator: Accumulated opening proofs for all blobs before this block range.
59
+ * endBlobAccumulator: Accumulated opening proofs for all blobs after adding this block range.
60
+ * finalBlobChallenges: Final values z and gamma, shared across the epoch.
61
+ */
62
+ export declare class BlockBlobPublicInputs {
63
+ startBlobAccumulator: BlobAccumulatorPublicInputs;
64
+ endBlobAccumulator: BlobAccumulatorPublicInputs;
65
+ finalBlobChallenges: FinalBlobBatchingChallenges;
66
+ constructor(startBlobAccumulator: BlobAccumulatorPublicInputs, endBlobAccumulator: BlobAccumulatorPublicInputs, finalBlobChallenges: FinalBlobBatchingChallenges);
67
+ static empty(): BlockBlobPublicInputs;
68
+ static fromBuffer(buffer: Buffer | BufferReader): BlockBlobPublicInputs;
69
+ toBuffer(): Buffer<ArrayBufferLike>;
70
+ }
71
+ //# sourceMappingURL=blob_batching_public_inputs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob_batching_public_inputs.d.ts","sourceRoot":"","sources":["../src/blob_batching_public_inputs.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,6BAA6B,CAAC;AAE3F,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEtG;;GAEG;AACH,qBAAa,2BAA2B;IAE7B,sBAAsB,EAAE,EAAE;IAC1B,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,UAAU;IAChB,QAAQ,EAAE,EAAE;IACZ,WAAW,EAAE,OAAO;gBALpB,sBAAsB,EAAE,EAAE,EAC1B,IAAI,EAAE,EAAE,EACR,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,EAAE,EACZ,WAAW,EAAE,OAAO;IAG7B,MAAM,CAAC,KAAK,IAAI,2BAA2B;IAI3C,MAAM,CAAC,KAAK,EAAE,2BAA2B;IAWzC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,2BAA2B;IAY7E,QAAQ;IAWR;;;;;;OAMG;IACG,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,mBAAmB,EAAE,2BAA2B;IAsBrF,QAAQ;IAaR,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,WAAW,GAAG,2BAA2B;IAgB1E;;;OAGG;IACH,MAAM,CAAC,0BAA0B,CAAC,WAAW,EAAE,sBAAsB;CAUtE;AAED;;GAEG;AACH,qBAAa,gCAAgC;IAElC,mBAAmB,EAAE,EAAE;IACvB,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,UAAU;gBAHb,mBAAmB,EAAE,EAAE,EACvB,CAAC,EAAE,EAAE,EACL,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,UAAU;IAGtB,MAAM,CAAC,KAAK,IAAI,gCAAgC;IAIhD,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,gCAAgC;IAUlF,QAAQ;IAIR,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,WAAW;IAIxC,QAAQ;IAUR,QAAQ;IAQR,MAAM,CAAC,KAAK,EAAE,gCAAgC;IAU9C,MAAM,CAAC,MAAM;IAKb,MAAM,CAAC,0BAA0B,CAAC,WAAW,EAAE,sBAAsB;IASrE,CAAC,OAAO,CAAC,MAAM,CAAC;CAQjB;AAED;;;;GAIG;AACH,qBAAa,qBAAqB;IAEvB,oBAAoB,EAAE,2BAA2B;IACjD,kBAAkB,EAAE,2BAA2B;IAC/C,mBAAmB,EAAE,2BAA2B;gBAFhD,oBAAoB,EAAE,2BAA2B,EACjD,kBAAkB,EAAE,2BAA2B,EAC/C,mBAAmB,EAAE,2BAA2B;IAGzD,MAAM,CAAC,KAAK,IAAI,qBAAqB;IAQrC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,qBAAqB;IASvE,QAAQ;CAGT"}