Skip to content

Commit 2bd1c26

Browse files
Lulu-Zhou-EigenLabsmmurrscody-littley
authored
docs: chunk assignment (#2215)
* update document for chunk assignment algorithm * docs - fix chunks assignment equation formatting Fixed the broken formatting * Docs - fix max chunks eq * address comments * update chunk assignment scheme in security parameter doc; add explanation for chunk assignment formula * align the notation for coding rate * update chunk assignment graph; add chunk assignment graph to the doc * Apply suggestions from code review clarification for custom quorum and operators. Co-authored-by: Cody Littley <56973212+cody-littley@users.noreply.github.com> --------- Co-authored-by: Matt Murray <37455908+mmurrs@users.noreply.github.com> Co-authored-by: Cody Littley <56973212+cody-littley@users.noreply.github.com>
1 parent f3ecb63 commit 2bd1c26

File tree

3 files changed

+164
-69
lines changed

3 files changed

+164
-69
lines changed
149 KB
Loading
Lines changed: 162 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,207 @@
11
## Assignment Module
22

3-
> Warning: this page describes the assignment logic for EigenDA V1. We need to update it with Blazar assignment logic which is very different.
3+
The assignment module determines how encoded blob chunks are allocated to validators based on the Ethereum chain state, specifically validator stakes and quorum memberships. Given the validator state and blob parameters, it produces a deterministic mapping from validators to chunk indices. The mapping ensures that a sufficient number of signatures and honest validators implies that data is available.
44

5-
The assignment module is essentially a rule which takes in the Ethereum chain state and outputs an allocation of chunks to DA operators. This can be generalized to a function that outputs a set of valid allocations.
6-
7-
A chunk assignment has the following parameters:
8-
1) **Indices**: the chunk indices that will be assigned to each DA node. Some DA nodes receive more than one chunk.
9-
2) **ChunkLength**: the length of each chunk (measured in number of symbols, as defined by the encoding module). We currently require all chunks to be of the same length, so this parameter is a scalar.
10-
11-
The assignment module is implemented by the `AssignmentCoordinator` interface.
5+
The assignment module is implemented in `core/v2/assignment.go`. For blobs dispersed to multiple quorums, the algorithm employs overlap optimization to minimize storage requirements while maintaining security guarantees.
126

137
![image](../../assets/assignment-module.png)
148

9+
### Chunk Assignment Algorithm within One Quorum
1510

16-
### Chunk Assignment Scheme Overview
17-
18-
Our chunk assignment scheme (CAS) assigns encoded chunks to validators proportionally to their stake, ensuring that any coalition of validators with sufficient combined stake can reconstruct the blob. Given:
11+
The chunk assignment scheme assigns encoded chunks to validators proportionally to their stake, ensuring that any coalition of validators with sufficient combined stake can reconstruct the blob.
1912

13+
Given:
2014
- A set of $n$ validators with stakes $\eta_1, \eta_2, \ldots, \eta_n$, where $\sum_{i=1}^n \eta_i = 1$
2115
- A set of $c$ chunks to be assigned to the validators
2216

23-
The assignment algorithm `GetAssignments` works as follows:
17+
Within a single quorum, the number of chunks assigned to validator $i$ is:
18+
```math
19+
c_i = \lceil \eta_i(c - n) \rceil
20+
```
21+
This assignment ensures that the total number of assigned chunks is less than or equal to the total number of chunks $c$, since $\sum_{i=1}^n c_i = \sum_{i=1}^n \lceil \eta_i(c - n) \rceil \le \sum_{i=1}^n [\eta_i(c - n) + 1] = c$.
2422

25-
1. **Initial allocation:** For each validator $i$, calculate the base number of chunks:
26-
$$
27-
c'_i = \lceil \eta_i(c - n) \rceil
28-
$$
23+
This guarantees that the chunks assigned to validators within a quorum are **non-overlapping**. In other words, each validator in a quorum contributes **distinct chunks** for reconstruction. The proof that any subset of validators with sufficient combined stake can reconstruct the blob is provided in [Security Parameters](./security-parameters.md).
2924

30-
2. **Total initial assignment:** Compute
31-
$$
32-
c' = \sum_{i=1}^n c'_i
33-
$$
25+
### Chunk Assignment for Multiple Quorums
3426

35-
3. **Sort validators:** Order validators deterministically and assign index $k_i$ to each validator $i$.
27+
EigenDA supports blobs dispersed to multiple quorums simultaneously. The security threshold is guaranteed to hold for each quorum independently, as shown in the previous section. The multi-quorum assignment algorithm minimizes storage requirements through overlap optimization while maintaining security guarantees.
3628

37-
4. **Final assignment:** The number of chunks assigned to validator $i$ is:
38-
$$
39-
c_i = c'_i + \mathbb{I}_{k_i \leq c - c'}
40-
$$
41-
where $\mathbb{I}$ is the indicator function that adds one extra chunk to the first $c - c'$ validators to ensure the total number of assigned chunks equals $c$.
29+
#### Storage Optimization Strategy
4230

43-
We will prove that any subset of validators with sufficient combined stake can reconstruct the blob in [Security Parameters](./security-parameters.md).
31+
The assignment algorithm uses two key strategies to minimize storage:
4432

45-
### Assignment Logic
33+
1. **Chunk Overlap Maximization:** When a validator participates in multiple quorums for the same blob, the algorithm reuses the same chunk indices across quorums whenever possible.
4634

47-
The standard assignment coordinator implements a very simple logic for determining the number of chunks per node and the chunk length, which we describe here.
35+
2. **Reconstruction Capping:** Each validator is assigned at most the number of chunks needed to independently reconstruct the blob.
4836

49-
**Chunk Length**
37+
**Example:** Consider a validator with 5% stake in quorum 0 and 15% stake in quorum 1. Without optimization, the validator might receive two non-overlapping sets of chunks (one per quorum), totaling up to 20% of all chunks. With overlap optimization, the validator stores only `max(chunks_quorum_0, chunks_quorum_1)` unique chunks, which is 15% of the total chunks. With reconstruction capping, if the [coding rate](./security-parameters.md#blob-parameters) is $\gamma = 1/8$, the validator only needs to store 1/8 of the total chunks.
5038

51-
Chunk lengths must be sufficiently small that operators with a small proportion of stake will be able to receive a quantity of data commensurate with their stake share. For each operator $i$, let $S_i$ signify the amount of stake held by that operator.
39+
#### Algorithm Components
5240

53-
We require that the chunk size $C$ satisfy
41+
The multi-quorum assignment algorithm consists of four key functions:
5442

55-
$$
56-
C \le \text{NextPowerOf2}\left(\frac{B}{\gamma}\max\left(\frac{\min_jS_j}{\sum_jS_j}, \frac{1}{M_\text{max}} \right) \right)
57-
$$
43+
**1. GetAssignmentsForQuorum:** Calculates assignments for a single quorum independently using the stake-proportional algorithm described above.
5844

45+
**2. AddAssignmentsForQuorum:** Generates the assignment for a new quorum while maximizing overlap with a baseline quorum assignment through a two-phase process:
5946

60-
where $\gamma = \beta-\alpha$, with $\alpha$ and $\beta$ the adversary and quorum thresholds as defined in the [Overview](../overview.md).
47+
- **Phase 1 (Overlap Maximization):** For each validator, reuse as many chunk indices as possible from the baseline quorum assignment, up to the number required for the new quorum. Mark these reused indices as "used."
6148

62-
This means that as long as an operator has a stake share of at least $1/M_\text{max}$, then the encoded data that they will receive will be within a factor of 2 of their share of stake. Operators with less than $1/M_\text{max}$ of stake will receive no more than a $1/M_\text{max}$ of the encoded data. $M_\text{max}$ represents the maximum number of chunks that the disperser can be required to encode per blob. This limit is included because proving costs scale somewhat super-linearly with the number of chunks.
49+
- **Phase 2 (Gap Filling):** Distribute the remaining unused chunk indices to validators who need additional chunks beyond what was reused from the baseline, ensuring each validator receives their stake-proportional allocation in the new quorum.
6350

64-
In the future, additional constraints on chunk length may be added; for instance, the chunk length may be set in order to maintain a fixed number of chunks per blob across all system states. Currently, the protocol does not mandate a specific value for the chunk length, but will accept the range satisfying the above constraint. The `CalculateChunkLength` function is provided as a convenience function that can be used to find a chunk length satisfying the protocol requirements.
51+
This algorithm guarantees that validators participating in both quorums store only `max(chunks_in_quorum_1, chunks_in_quorum_2)` unique chunks rather than the sum.
6552

66-
**Index Assignment**
53+
**3. MergeAssignmentsAndCap:** Merges assignments across all quorums and caps the total at the reconstruction threshold:
54+
```math
55+
\text{max\_chunks} = c \cdot \gamma
56+
```
57+
where $c$ is the total number of chunks and $\gamma$ is the [coding rate](./security-parameters.md#blob-parameters). This cap exists because once a validator has enough unique chunks to reconstruct the blob, additional chunks provide no incremental security benefit. Therefore, pruning the extra chunks improves performance and reduces storage and bandwidth requirements without affecting security.
6758

68-
For each operator $i$, let $S_i$ signify the amount of stake held by that operator. We want for the number of chunks assigned to operator $i$ to satisfy
59+
**4. GetAssignmentsForBlob:** Coordinates the full multi-quorum assignment process:
60+
1. Generate the assignment for quorum 0 using `GetAssignmentsForQuorum`
61+
2. Generate assignments for all other quorums using `AddAssignmentsForQuorum` with quorum 0 as the baseline
62+
3. Merge all per-quorum assignments using `MergeAssignmentsAndCap` to produce the final assignment for each validator
6963

70-
$$
71-
\frac{\gamma m_i C}{B} \ge \frac{S_i}{\sum_j S_j}
72-
$$
64+
**Note on Optimality:** The algorithm produces optimal storage assignments for two quorums. For three or more quorums, the assignment is not guaranteed to be globally optimal. Since quorums 0 and 1 are the "default" quorums and are expected to be the larger than custom quorums (i.e. containing the most validators), the algorithm achieves near-optimal storage reduction for the majority of validators.
7365

74-
Let
66+
### Code Walkthrough
67+
Notation note: In the code, we sometimes use the term `operator` to refer to a `validator`, although `validator` is now the preferred term.
7568

76-
$$
77-
m_i = \text{ceil}\left(\frac{B S_i}{C\gamma \sum_j S_j}\right)\tag{1}
78-
$$
69+
**Location:** `core/v2/assignment.go`
7970

80-
**Correctness**
81-
Let's show that any sets $U_q$ and $U_a$ satisfying the constraints in the [Consensus Layer Overview](../overview.md#consensus-layer), the data held by the operators $U_q \setminus U_a$ will constitute an entire blob. The amount of data held by these operators is given by
71+
**Data Structure:**
72+
```go
73+
type Assignment struct {
74+
Indices []uint32 // Explicit list of chunk indices (non-contiguous)
75+
}
76+
```
8277

83-
$$
84-
\sum_{i \in U_q \setminus U_a} m_i C
85-
$$
78+
**Core Functions:**
8679

87-
We have from (1) and from the definitions of $U_q$ and $U_a$ that
80+
**1. GetAssignmentsForQuorum (`core/v2/assignment.go:40-90`)**
8881

89-
$$
90-
\sum_{i \in U_q \setminus U_a} m_i C \ge =\frac{B}{\gamma}\sum_{i \in U_q \setminus U_a}\frac{S_i}{\sum_j S_j} = \frac{B}{\gamma}\frac{\sum_{i \in U_q} S_i - \sum_{i \in U_a} S_i}{\sum_jS_j} \ge B \frac{\beta-\alpha}{\gamma} = B \tag{2}
91-
$$
82+
Assigns chunks for a single quorum with deterministic ordering:
9283

93-
Since the unique data held by these operators exceeds the size of a blob, the encoding module ensures that the original blob can be reconstructed from this data.
84+
```go
85+
func GetAssignmentsForQuorum(
86+
state *core.OperatorState,
87+
blobParams *core.BlobVersionParameters,
88+
quorum core.QuorumID,
89+
) (map[core.OperatorID]*Assignment, []core.OperatorID, error)
90+
```
9491

92+
Algorithm:
93+
1. Sort operators by hex ID for determinism
94+
2. Calculate effective chunks: `effectiveNumChunks = NumChunks - MaxNumOperators`
95+
3. For each operator $i$: `chunksForOperator = ceil((effectiveNumChunks × stake_i) / totalStake)`
96+
4. Assign contiguous indices starting from offset 0
97+
5. Return assignments and ordered operator list
9598

96-
## Validation Actions
99+
**2. AddAssignmentsForQuorum (`core/v2/assignment.go:99-161`)**
97100

98-
Validation with respect to assignments is performed at different layers of the protocol:
101+
Maximizes overlap with a baseline assignment:
99102

100-
### DA Nodes
103+
```go
104+
func AddAssignmentsForQuorum(
105+
assignments map[core.OperatorID]*Assignment, // Baseline from first quorum
106+
state *core.OperatorState,
107+
blobParams *core.BlobVersionParameters,
108+
quorum core.QuorumID,
109+
) (map[core.OperatorID]*Assignment, error)
110+
```
101111

102-
When the DA node receives a `StoreChunks` request, it performs the following validation actions relative to each blob header:
103-
- It uses the `ValidateChunkLength` to validate that the `ChunkLength` for the blob satisfies the above constraints.
104-
- It uses `GetOperatorAssignment` to calculate the chunk indices for which it is responsible, and verifies that each of the chunks that it has received lies on the polynomial at these indices (see [Encoding validation actions](./encoding.md#validation-actions))
112+
Two-phase algorithm:
113+
- **Phase 1 (Lines 115-136):** For each operator, reuse indices from baseline up to their allotted count for this quorum
114+
- **Phase 2 (Lines 145-158):** Distribute unused indices to operators needing more chunks
105115

106-
This step ensures that each honest node has received the blobs for which it is accountable.
116+
**3. MergeAssignmentsAndCap (`core/v2/assignment.go:167-220`)**
107117

108-
Since the DA nodes will allow a range of `ChunkLength` values, as long as they satisfy the constraints of the protocol, it is necessary for there to be consensus on the `ChunkLength` that is in use for a particular blob and quorum. For this reason, the `ChunkLength` is included in the `BlobQuorumParam` which is hashed to create the merkle root contained in the `BatchHeaderHash` signed by the DA nodes.
118+
```go
119+
func MergeAssignmentsAndCap(
120+
assignments []map[core.OperatorID]*Assignment,
121+
blobParams *core.BlobVersionParameters,
122+
) map[core.OperatorID]Assignment
123+
```
109124

110-
### Rollup Smart Contract
125+
Merges all quorum assignments and caps at `maxChunks = NumChunks / CodingRate`
111126

112-
When the rollup confirms its blob against the EigenDA batch, it checks that the `ConfirmationThreshold` for the blob is greater than the `AdversaryThreshold`. This means that if the `ChunkLength` determined by the disperser is invalid, the batch cannot be confirmed as a sufficient number of nodes will not sign.
127+
**4. GetAssignmentsForBlob (`core/v2/assignment.go:227-266`)**
128+
129+
Main entry point coordinating the full multi-quorum assignment:
130+
131+
```go
132+
func GetAssignmentsForBlob(
133+
state *core.OperatorState,
134+
blobParams *core.BlobVersionParameters,
135+
quorums []core.QuorumID,
136+
) (map[core.OperatorID]Assignment, error) {
137+
// Sort quorums for determinism
138+
sort.Slice(quorums, ...)
139+
140+
// Process first quorum
141+
assignmentsList[0], _, err = GetAssignmentsForQuorum(state, blobParams, quorums[0])
142+
143+
// Process remaining quorums with overlap optimization
144+
for i := 1; i < len(quorums); i++ {
145+
assignmentsList[i], err = AddAssignmentsForQuorum(
146+
assignmentsList[0], state, blobParams, quorums[i])
147+
}
148+
149+
// Merge and cap
150+
return MergeAssignmentsAndCap(assignmentsList, blobParams)
151+
}
152+
```
153+
154+
**Usage in Node Chunk Download (`node/node_v2.go:40-105`):**
155+
156+
```go
157+
func (n *Node) DetermineChunkLocations(
158+
batch *corev2.Batch,
159+
operatorState *core.OperatorState,
160+
) {
161+
for _, cert := range batch.BlobCertificates {
162+
// Get assignment for this operator across ALL quorums in the blob
163+
assgn, err := corev2.GetAssignmentForBlob(
164+
operatorState,
165+
blobParams,
166+
cert.BlobHeader.QuorumNumbers, // Multiple quorums
167+
n.Config.ID)
168+
169+
// Request specific chunk indices from relay
170+
req.chunkRequests = append(req.chunkRequests, &relay.ChunkRequestByIndex{
171+
BlobKey: blobKey,
172+
Indices: assgn.Indices, // Explicit indices with overlap optimization
173+
})
174+
}
175+
}
176+
```
177+
178+
**Usage in Validation (`core/v2/validator.go:49-79`):**
179+
180+
```go
181+
func (v *shardValidator) validateBlobParams(
182+
blob *BlobShard,
183+
blobParams *core.BlobVersionParameters,
184+
operatorState *core.OperatorState,
185+
) (*Assignment, error) {
186+
// Get assignment across all quorums for this blob
187+
assignment, err := GetAssignmentForBlob(
188+
operatorState,
189+
blobParams,
190+
blob.BlobHeader.QuorumNumbers, // All quorums
191+
v.operatorID)
192+
193+
// Validate chunk count
194+
if assignment.NumChunks() != uint32(len(blob.Bundle)) {
195+
return error
196+
}
197+
198+
// Validate chunk lengths
199+
for _, chunk := range blob.Bundle {
200+
if chunk.Length() != expectedChunkLength {
201+
return error
202+
}
203+
}
204+
205+
return &assignment, nil
206+
}
207+
```

docs/spec/src/protocol/architecture/security-parameters.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ We define the **Blob parameters** as a tuple **$(n, c, \gamma)$** where:
1414

1515
- $n$ (`MaxNumOperators`): Maximum number of validators allowed in EigenDA.
1616
- $c$ (`NumChunks`): The total number of encoded chunks after erasure coding (must be a power of 2).
17-
- $\gamma$ (`1/CodingRate`): The ratio of original data to total encoded chunks, providing redundancy (must be an inverse power of 2). Note that for representational purposes, the `CodingRate` in our code is the inverse of $\gamma$, the standard coding rate used in coding theory.
17+
- $\gamma$ (`1/CodingRate`): The ratio of original data to total encoded chunks, providing redundancy (must be an inverse power of 2). Note that for representational purposes, the `CodingRate` in our code is the inverse of $\gamma$, while $\gamma$ is the the standard coding rate used in coding theory.
1818

1919
Among the blob parameters, `CodingRate` and `NumChunks` are used in the [encoding](./encoding.md) process, while `NumChunks` and `MaxNumOperators` are used in the chunk [assignment](./assignment.md) process.
2020

@@ -49,7 +49,7 @@ Formally, we need to show that for any set of validators $H$ with total stake $\
4949
**Proof:**
5050

5151
By the chunk assignment scheme, we have:
52-
$$c_i \geq c'_i = \lceil \eta_i(c - n) \rceil $$
52+
$$c_i = \lceil \eta_i(c - n) \rceil $$
5353
$$\geq \eta_i(c - n)$$
5454

5555
Therefore, since $\sum_{i \in H} \eta_i \geq \frac{c}{c-n} \gamma$, we have:

0 commit comments

Comments
 (0)