@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.
- package/dest/blob.d.ts +98 -52
- package/dest/blob.d.ts.map +1 -1
- package/dest/blob.js +167 -73
- package/dest/blob_batching.d.ts +48 -15
- package/dest/blob_batching.d.ts.map +1 -1
- package/dest/blob_batching.js +120 -81
- package/dest/blob_batching_public_inputs.d.ts +71 -0
- package/dest/blob_batching_public_inputs.d.ts.map +1 -0
- package/dest/blob_batching_public_inputs.js +168 -0
- package/dest/encoding.d.ts +62 -22
- package/dest/encoding.d.ts.map +1 -1
- package/dest/encoding.js +104 -114
- package/dest/index.d.ts +2 -5
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +15 -5
- package/dest/sponge_blob.d.ts +9 -13
- package/dest/sponge_blob.d.ts.map +1 -1
- package/dest/sponge_blob.js +17 -28
- package/dest/testing.d.ts +12 -7
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +41 -54
- package/dest/types.d.ts +0 -2
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +0 -2
- package/package.json +4 -5
- package/src/blob.ts +198 -76
- package/src/blob_batching.ts +137 -109
- package/src/blob_batching_public_inputs.ts +252 -0
- package/src/encoding.ts +120 -136
- package/src/index.ts +18 -5
- package/src/sponge_blob.ts +14 -24
- package/src/testing.ts +40 -55
- package/src/types.ts +2 -2
- package/dest/blob_utils.d.ts +0 -30
- package/dest/blob_utils.d.ts.map +0 -1
- package/dest/blob_utils.js +0 -60
- package/dest/circuit_types/blob_accumulator.d.ts +0 -21
- package/dest/circuit_types/blob_accumulator.d.ts.map +0 -1
- package/dest/circuit_types/blob_accumulator.js +0 -58
- package/dest/circuit_types/final_blob_accumulator.d.ts +0 -22
- package/dest/circuit_types/final_blob_accumulator.d.ts.map +0 -1
- package/dest/circuit_types/final_blob_accumulator.js +0 -63
- package/dest/circuit_types/final_blob_batching_challenges.d.ts +0 -15
- package/dest/circuit_types/final_blob_batching_challenges.d.ts.map +0 -1
- package/dest/circuit_types/final_blob_batching_challenges.js +0 -25
- package/dest/circuit_types/index.d.ts +0 -4
- package/dest/circuit_types/index.d.ts.map +0 -1
- package/dest/circuit_types/index.js +0 -4
- package/dest/deserialize.d.ts +0 -14
- package/dest/deserialize.d.ts.map +0 -1
- package/dest/deserialize.js +0 -33
- package/dest/hash.d.ts +0 -35
- package/dest/hash.d.ts.map +0 -1
- package/dest/hash.js +0 -69
- package/dest/kzg_context.d.ts +0 -4
- package/dest/kzg_context.d.ts.map +0 -1
- package/dest/kzg_context.js +0 -5
- package/src/blob_utils.ts +0 -71
- package/src/circuit_types/blob_accumulator.ts +0 -84
- package/src/circuit_types/final_blob_accumulator.ts +0 -75
- package/src/circuit_types/final_blob_batching_challenges.ts +0 -29
- package/src/circuit_types/index.ts +0 -4
- package/src/deserialize.ts +0 -38
- package/src/hash.ts +0 -77
- package/src/kzg_context.ts +0 -5
package/dest/blob_batching.d.ts
CHANGED
|
@@ -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[]
|
|
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[]
|
|
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[]
|
|
56
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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(
|
|
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,
|
|
1
|
+
{"version":3,"file":"blob_batching.d.ts","sourceRoot":"","sources":["../src/blob_batching.ts"],"names":[],"mappings":"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"}
|
package/dest/blob_batching.js
CHANGED
|
@@ -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 {
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
62
|
-
let z;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
84
|
-
const
|
|
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 <
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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/
|
|
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
|
|
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
|
-
|
|
192
|
-
]), this.yAcc.add(thisY.mul(this.gammaPow)), this.cAcc.add(
|
|
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
|
|
212
|
-
acc = await acc.accumulate(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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"}
|