Розробка кастомного уровня Data Availability
Data Availability — це проблема, яку більшість розробників блокчейна недооцінюють до тих пір, поки не сталкиваються з нею у production. Класична формулювання: як легкий клієнт може переконатися, що блок-продюсер дійсно опублікував всі дані транзакцій, не завантажуючи блок цілком? Якщо дані не доступні — неможливо верифікувати fraud proofs, неможливо восстановити стан, неможливо побудувати rollup поверх цього шару. DA — це фундамент довіри до всієї системи.
Зачем кастомний DA-слой
Існуючі рішення — Celestia, EigenDA, Avail, Ethereum danksharding (EIP-4844 + full danksharding) — покривають більшість потреб. Кастомний DA потрібен у специфічних випадках:
- Вимоги до продуктивності перевищують публічні DA-мережі (Celestia: ~8MB/block, EigenDA: до кількох MB/s на operator set)
- Приватні дані: публічні DA-шари публікують дані відкрито. Для enterprise/permissioned приложений потрібна інша модель
- Спеціалізована схема кодування: під конкретні типи даних (наприклад, ZK-proof батчі зі специфічною структурою)
- Суверенність: небажання залежати від третьої мережи й її токеномики
- Специфічні гарантії finality: відмінні від існуючих рішень
Теоретична основа: Data Availability Sampling
Ключова інновація сучасних DA-шарів — DAS (Data Availability Sampling). Замість того щоб завантажувати весь блок, легкий клієнт випадково семплює невелику кількість чанків. Якщо достатньо семплів доступні — з високою ймовірністю весь блок доступний.
Це працює завдяки erasure coding: дані блока кодуються з надлишковістю 2x (наприклад, 1MB даних → 2MB закодованих чанків). Якщо доступна принаймні 50% чанків, вихідні дані можна відновити. Тому, щоб сховати дані, блок-продюсер повинен сховати >50% всіх чанків — легко обнаруживается при DAS.
Reed-Solomon vs. 2D erasure coding
Простий Reed-Solomon працює в одному вимірі. Celestia використовує 2D Reed-Solomon: дані організуються в матрицю й кодуються по рядках та стовпцях. Це дозволяє локалізувати fraud proofs — доказати що конкретний рядок або стовпець некорректно закодовані, не завантажуючи весь блок.
Вихідні дані (k×k матриця):
[d00 d01 ... d0k]
[d10 d11 ... d1k]
...
Після 2D RS кодування (2k×2k):
[d00 d01 ... d0k | p00 p01 ... p0k] ← parity по рядках
[d10 d11 ... d1k | p10 p11 ... p1k]
...
[p00 p01 ... p0k | pp00 pp01 ... ] ← parity по стовпцях + діагоналях
KZG Polynomial Commitments
Для ефективних DA-proofs необхідний механізм, що дозволяє доказати що конкретний чанк є частиною конкретного блока без завантаження всього блока. KZG commitments (Kate-Zaverucha-Goldberg, EIP-4844) — оптимальне рішення:
- Дані блока інтерпретуються як поліном
p(x)степеняn-1 - Commitment
C = [p(τ)]₁— точка на BLS12-381 elliptic curve (48 байт) - Proof для конкретного значення
p(z) = y— ще одна точка (48 байт) - Верифікація: pairing check
e(C - [y]₁, [1]₂) = e(π, [τ - z]₂)
Розмір proof — константний (48 байт) незалежно від розміру даних. Верифікація — одна pairing операція (~2ms на сучасному залізі).
Для кастомного DA-шару реалізація KZG commitments виглядає так:
use ark_bls12_381::{Bls12_381, Fr, G1Projective};
use ark_poly::{univariate::DensePolynomial, UVPolynomial};
use ark_poly_commit::kzg10::{KZG10, Powers, VerifierKey};
pub struct DALayer {
powers: Powers<Bls12_381>,
vk: VerifierKey<Bls12_381>,
}
impl DALayer {
pub fn commit_blob(&self, data: &[u8]) -> (Commitment, Vec<Fr>) {
let scalars = bytes_to_field_elements(data);
let poly = DensePolynomial::from_coefficients_vec(scalars.clone());
let (commitment, _) = KZG10::commit(&self.powers, &poly, None, None).unwrap();
(commitment, scalars)
}
pub fn generate_proof(&self, data: &[Fr], index: usize) -> Proof {
let poly = DensePolynomial::from_coefficients_vec(data.to_vec());
let point = Fr::from(index as u64);
KZG10::open(&self.powers, &poly, point, &Fr::zero()).unwrap()
}
pub fn verify_chunk(
&self,
commitment: &Commitment,
index: usize,
value: Fr,
proof: &Proof,
) -> bool {
let point = Fr::from(index as u64);
KZG10::check(&self.vk, commitment, point, value, proof).unwrap()
}
}
Мережевий рівень: P2P і зберігання
Розподіл даних
Дані повинні бути розповсюджені між достатньою кількістю нод так, щоб відмова будь-якого меньшинства нод не привела до недоступності даних. Типова схема:
- Producer (sequencer/block producer) розрізає блок на чанки, створює commitments
- Чанки розповсюджуються по DA nodes через P2P gossip (libp2p рекомендується)
- Кожен DA node зберігає підмножину чанків і аттестує їхню доступність
- Light clients роблять DAS, вибираючи випадкові ноди для семплювання
Аттестації доступності
DA nodes публікують signed аттестації: "я храню чанки X для блока Y". Ці аттестації агрегуються для формування Data Availability Certificate (DAC). У приватних DA-шарах (enterprise) достатньо порогового кількості підписів від довірених комітетів. У публічних — потрібен більш складний механізм.
pub struct DAAttestation {
pub block_hash: [u8; 32],
pub block_height: u64,
pub chunk_indices: Vec<u32>, // які чанки зберігає ця нода
pub timestamp: u64,
pub signature: BLSSignature, // BLS для агрегації
}
pub struct DAC {
pub block_hash: [u8; 32],
pub aggregate_signature: BLSAggregatedSignature,
pub signer_bitfield: BitVec, // які члени комітету підписали
pub threshold_met: bool,
}
BLS-підписи вибрані не випадково: BLS підтримує signature aggregation — n підписів агрегуються в одну, що критично важливо для ефективності при великих комітетах.
Інтеграція з rollup
DA-шар — це не standalone продукт, він завжди інтегрується з rollup або іншою системою. Rollup sequencer повинен:
- Після формування батча — відправити дані в DA-шар
- Дочекатися DAC (Data Availability Certificate)
- Включити хеш DAC в L1 state root commitment
L1 контракт rollup верифікує наявність коректного DAC перед прийняттям state root:
contract RollupWithCustomDA {
IDALayerVerifier public daVerifier;
struct BatchCommitment {
bytes32 stateRoot;
bytes32 dataCommitment; // KZG commitment на дані батча
bytes dacCertificate; // агрегована BLS підпись комітету
}
function submitBatch(BatchCommitment calldata batch) external {
// верифікуємо що DA комітет аттестовав доступність даних
require(
daVerifier.verifyDAC(
batch.dataCommitment,
batch.dacCertificate,
REQUIRED_THRESHOLD // наприклад 2/3 комітету
),
"DA not certified"
);
// приймаємо state root тільки якщо дані доступні
pendingRoots[currentBatchId] = batch.stateRoot;
emit BatchSubmitted(currentBatchId, batch.stateRoot, batch.dataCommitment);
currentBatchId++;
}
}
Порівняння підходів
| Підхід | Throughput | Гарантії безпеки | Складність |
|---|---|---|---|
| Ethereum calldata | ~375KB/блок | Full Ethereum security | Низька |
| EIP-4844 blobs | ~768KB/блок | Full Ethereum security | Низька |
| Celestia | ~8MB/блок | DAS + fraud proofs | Середня |
| EigenDA | До десятків MB/s | EigenLayer restaking | Середня |
| Custom DA (committee) | Залежить від конфігурації | Довіренийй комітет | Висока |
| Custom DA (DAS+KZG) | Масштабується | Cryptographic guarantees | Дуже висока |
Кастомний DA з DAS і KZG — максимально складне рішення. Для більшості проектів Celestia або EigenDA забезпечать необхідну пропускну здатність з перевіреною безпекою. Кастомний DA виправданий коли специфічні вимоги до приватності, продуктивності або суверенності не можуть бути задоволені існуючими рішеннями.







