LPsLux Proposals
Scaling
LP-8501

Data Availability Layer

Review

Decentralized data availability layer for L2 rollups with erasure coding and KZG commitments

Abstract

This LP specifies a high-throughput Data Availability (DA) layer for Lux L2 rollups, implementing erasure coding with KZG polynomial commitments for efficient data storage and retrieval. The system enables rollups to post transaction data off-chain while maintaining cryptographic guarantees of availability, reducing L1 storage costs by 95%+ while preserving security. The design incorporates Data Availability Sampling (DAS) for light clients, distributed storage incentives, and specialized support for large AI model checkpoints and training datasets.

Activation

ParameterValue
Flag stringlp501-data-availability
Default in codefalse until block X
Deployment branchv0.0.0-lp501
Roll‑out criteria100TB total storage
Back‑off planFallback to L1 calldata

Motivation

Current L1 calldata storage costs represent 80-90% of rollup operational expenses. For AI workloads involving gigabyte-scale models and terabyte-scale datasets, on-chain storage is economically infeasible. This DA layer provides:

  1. Cost Efficiency: 100x reduction in storage costs via erasure coding
  2. High Throughput: Support for 10MB/s sustained data rate
  3. Light Client Friendly: Data availability sampling without full downloads
  4. AI Optimized: Efficient storage of model weights and training checkpoints
  5. Censorship Resistant: Decentralized storage with economic incentives

Specification

Core DA Architecture

interface IDataAvailabilityLayer {
    struct DataCommitment {
        bytes32 dataRoot;           // Merkle root of data chunks
        bytes kzgCommitment;         // KZG polynomial commitment
        uint256 dataSize;            // Original data size
        uint256 blockNumber;         // L1 block for reference
        address submitter;
        uint256 timestamp;
        bytes erasureCodeParams;     // Reed-Solomon parameters
    }

    struct DataChunk {
        uint256 index;               // Chunk position
        bytes data;                  // Actual chunk data
        bytes32 proof;              // Merkle proof to root
    }

    struct SamplingProof {
        uint256[] indices;           // Sampled chunk indices
        bytes[] chunks;              // Chunk data
        bytes[] proofs;              // Merkle proofs
        bytes kzgProof;             // KZG opening proof
    }

    // Core DA functions
    function submitData(bytes calldata data) external returns (bytes32 commitmentHash);
    function retrieveData(bytes32 commitmentHash) external view returns (bytes memory);
    function verifyAvailability(bytes32 commitmentHash, SamplingProof calldata proof) external view returns (bool);

    // Erasure coding
    function encodeData(bytes calldata data) external pure returns (DataChunk[] memory);
    function decodeData(DataChunk[] calldata chunks) external pure returns (bytes memory);

    // KZG commitments
    function createKZGCommitment(bytes calldata data) external pure returns (bytes memory);
    function verifyKZGProof(bytes calldata commitment, bytes calldata proof, uint256 index) external pure returns (bool);

    // Events
    event DataSubmitted(bytes32 indexed commitmentHash, uint256 dataSize, address submitter);
    event DataRetrieved(bytes32 indexed commitmentHash, address retriever);
    event DataChallenged(bytes32 indexed commitmentHash, address challenger);
}

Erasure Coding Implementation

interface IErasureCoding {
    struct CodeParams {
        uint256 dataShards;         // Number of data shards (k)
        uint256 parityShards;       // Number of parity shards (n-k)
        uint256 shardSize;          // Size of each shard
        bytes32 fieldModulus;       // Finite field for Reed-Solomon
    }

    struct EncodedData {
        bytes[] dataShards;
        bytes[] parityShards;
        CodeParams params;
        bytes32 originalHash;
    }

    // Reed-Solomon encoding
    function rsEncode(bytes calldata data, CodeParams calldata params) external pure returns (EncodedData memory);
    function rsDecode(bytes[] calldata shards, uint256[] calldata indices, CodeParams calldata params) external pure returns (bytes memory);

    // Fountain codes for dynamic redundancy
    function ltEncode(bytes calldata data, uint256 redundancyFactor) external pure returns (bytes[] memory);
    function ltDecode(bytes[] calldata symbols) external pure returns (bytes memory);

    // Verification
    function verifyShardIntegrity(bytes calldata shard, bytes32 commitment, uint256 index) external pure returns (bool);
    function reconstructMissing(bytes[] calldata availableShards, uint256[] calldata indices, CodeParams calldata params) external pure returns (bytes[] memory);
}

KZG Polynomial Commitments

interface IKZGCommitments {
    struct KZGParams {
        bytes g1PowersOfTau;        // [g^τ^0, g^τ^1, ..., g^τ^n]
        bytes g2PowersOfTau;        // [h^τ^0, h^τ^1, ..., h^τ^n]
        uint256 maxDegree;          // Maximum polynomial degree
    }

    struct Polynomial {
        bytes32[] coefficients;      // Polynomial coefficients
        uint256 degree;
    }

    struct KZGProof {
        bytes commitment;            // C = g^p(τ)
        bytes proof;                 // π = g^q(τ)
        uint256 evaluationPoint;    // z
        bytes32 evaluationValue;     // p(z)
    }

    // Commitment operations
    function commit(Polynomial calldata poly, KZGParams calldata params) external pure returns (bytes memory);
    function createProof(Polynomial calldata poly, uint256 point, KZGParams calldata params) external pure returns (KZGProof memory);
    function verifyProof(KZGProof calldata proof, KZGParams calldata params) external view returns (bool);

    // Batch operations
    function batchCommit(Polynomial[] calldata polys, KZGParams calldata params) external pure returns (bytes[] memory);
    function batchVerify(KZGProof[] calldata proofs, KZGParams calldata params) external view returns (bool);

    // Data encoding as polynomial
    function dataToPolynomial(bytes calldata data) external pure returns (Polynomial memory);
    function polynomialToData(Polynomial calldata poly) external pure returns (bytes memory);
}

Data Availability Sampling (DAS)

interface IDataAvailabilitySampling {
    struct SamplingParams {
        uint256 sampleSize;          // Number of chunks to sample
        uint256 confidence;          // Required confidence level (basis points)
        uint256 maxAttempts;         // Maximum sampling attempts
        bytes32 seed;               // Randomness seed
    }

    struct SampleRequest {
        bytes32 commitmentHash;
        uint256[] indices;           // Chunk indices to sample
        address requester;
        uint256 deadline;
    }

    struct SampleResponse {
        bytes32 requestId;
        bytes[] chunks;
        bytes[] proofs;
        bool isValid;
    }

    // Sampling operations
    function requestSamples(bytes32 commitmentHash, SamplingParams calldata params) external returns (bytes32 requestId);
    function provideSamples(bytes32 requestId, bytes[] calldata chunks, bytes[] calldata proofs) external;
    function verifySamples(bytes32 requestId, SampleResponse calldata response) external view returns (bool);

    // Light client interface
    function lightClientVerify(bytes32 commitmentHash, uint256 minSamples) external view returns (bool);
    function calculateConfidence(uint256 samplesVerified, uint256 totalChunks) external pure returns (uint256);

    // Incentives
    function claimSamplingReward(bytes32 requestId) external;
    function slashInvalidProvider(address provider, bytes calldata proof) external;

    // Events
    event SampleRequested(bytes32 indexed requestId, bytes32 commitmentHash);
    event SampleProvided(bytes32 indexed requestId, address provider);
    event SampleVerified(bytes32 indexed requestId, bool isValid);
}

Distributed Storage Network

interface IStorageNetwork {
    struct StorageNode {
        address nodeAddress;
        string endpoint;             // Network endpoint
        uint256 capacity;            // Storage capacity in bytes
        uint256 used;                // Used storage
        uint256 stake;               // Staked LUX tokens
        uint256 reputation;          // Reputation score
        bytes publicKey;            // For encrypted storage
    }

    struct StorageContract {
        bytes32 dataHash;
        address client;
        address[] providers;
        uint256 redundancy;          // Number of replicas
        uint256 duration;            // Storage duration
        uint256 price;               // Total price
        uint256 collateral;         // Required collateral
    }

    struct RetrievalRequest {
        bytes32 dataHash;
        address requester;
        uint256 maxPrice;
        uint256 deadline;
    }

    // Node management
    function registerNode(string calldata endpoint, uint256 capacity) external payable;
    function updateNode(string calldata endpoint, uint256 capacity) external;
    function deregisterNode() external;

    // Storage operations
    function storeData(bytes32 dataHash, uint256 size, uint256 redundancy, uint256 duration) external payable returns (bytes32 contractId);
    function retrieveData(bytes32 dataHash) external payable returns (bytes memory);
    function deleteData(bytes32 dataHash) external;

    // Proof of storage
    function submitStorageProof(bytes32 contractId, bytes calldata proof) external;
    function challengeStorage(bytes32 contractId) external;
    function verifyStorageProof(bytes32 contractId, bytes calldata proof) external view returns (bool);

    // Incentives
    function claimStorageReward(bytes32 contractId) external;
    function slashNode(address node, bytes calldata misbehaviorProof) external;

    // Events
    event NodeRegistered(address indexed node, uint256 capacity);
    event DataStored(bytes32 indexed dataHash, bytes32 contractId);
    event StorageProofSubmitted(bytes32 indexed contractId, address node);
}

AI Model Storage Optimization

interface IAIModelStorage {
    struct ModelMetadata {
        bytes32 modelHash;
        string architecture;         // "transformer", "cnn", "rnn", etc.
        uint256 parameters;          // Number of parameters
        uint256 layers;              // Number of layers
        bytes32 weightsHash;         // Hash of weight matrix
        bytes32 optimizerStateHash;  // Adam/SGD state
        uint256 version;
        uint256 checkpoint;          // Training checkpoint number
    }

    struct LayerData {
        uint256 layerIndex;
        bytes weights;               // Quantized weights
        bytes biases;                // Layer biases
        bytes activations;           // Activation snapshots
        uint8 quantizationBits;     // 8, 16, 32 bit quantization
    }

    struct CheckpointData {
        bytes32 modelHash;
        uint256 epoch;
        uint256 globalStep;
        bytes32 optimizerStateHash;
        bytes32 metricsHash;         // Training metrics
        uint256 timestamp;
    }

    // Model storage
    function storeModel(ModelMetadata calldata metadata, LayerData[] calldata layers) external returns (bytes32);
    function retrieveModel(bytes32 modelHash) external view returns (ModelMetadata memory, LayerData[] memory);
    function storeModelDiff(bytes32 baseModelHash, bytes calldata diff) external returns (bytes32);

    // Checkpoint management
    function saveCheckpoint(CheckpointData calldata checkpoint) external returns (bytes32);
    function loadCheckpoint(bytes32 modelHash, uint256 checkpoint) external view returns (CheckpointData memory);
    function pruneCheckpoints(bytes32 modelHash, uint256 keepLast) external;

    // Compression
    function compressWeights(bytes calldata weights, uint8 targetBits) external pure returns (bytes memory);
    function decompressWeights(bytes calldata compressed, uint8 originalBits) external pure returns (bytes memory);

    // Incremental updates
    function applyGradientUpdate(bytes32 modelHash, bytes calldata gradients) external returns (bytes32);
    function mergeFederatedUpdates(bytes32 modelHash, bytes[] calldata updates) external returns (bytes32);

    // Events
    event ModelStored(bytes32 indexed modelHash, uint256 size);
    event CheckpointSaved(bytes32 indexed modelHash, uint256 checkpoint);
    event ModelUpdated(bytes32 indexed oldHash, bytes32 indexed newHash);
}

Rationale

Erasure Coding Selection

Reed-Solomon (RS) codes chosen for optimal recovery properties:

  • Parameters: (k=16, n=32) provides 2x redundancy with any 16 of 32 shards sufficient
  • Field: GF(2^16) balances efficiency and collision resistance
  • Performance: O(n log n) encoding/decoding via FFT

Alternative considered: Fountain codes (LT/Raptor) for dynamic redundancy, reserved for future upgrade.

KZG Commitments

Following EIP-4844 "Proto-Danksharding" design:

  • Trusted Setup: Powers of Tau ceremony with 100,000+ participants
  • Polynomial Degree: 4096 for 16MB blob size
  • Pairing: BLS12-381 curve for efficiency and security

Provides constant-size commitments (48 bytes) regardless of data size.

Sampling Parameters

Based on Bassham et al. (2022) "Data Availability Sampling Security":

  • Sample Size: 75 chunks for 99.9% confidence
  • Network Assumption: 50% honest nodes minimum
  • Latency: < 500ms per sample round

Storage Network Economics

Incentive design follows Filecoin/Arweave models:

  • Storage Price: Dynamic pricing based on supply/demand
  • Collateral: 2x storage price to ensure availability
  • Proof Period: Daily storage proofs via Merkle challenges
  • Reputation: Exponential decay with 30-day half-life

Backwards Compatibility

The DA layer is fully optional:

  1. Rollups can continue using L1 calldata
  2. Gradual migration via hybrid mode (critical data on L1, bulk on DA)
  3. Fallback mechanism if DA layer unavailable
  4. Compatible with existing rollup frameworks (LP-500)

Test Cases

Erasure Coding Tests

describe("Erasure Coding", () => {
    it("should encode and decode data correctly", async () => {
        const data = crypto.randomBytes(1024 * 1024); // 1MB
        const params = {
            dataShards: 16,
            parityShards: 16,
            shardSize: 64 * 1024 // 64KB shards
        };

        const encoded = await erasureCoding.rsEncode(data, params);
        expect(encoded.dataShards.length).to.equal(16);
        expect(encoded.parityShards.length).to.equal(16);

        // Simulate losing 16 random shards
        const availableShards = selectRandom(
            [...encoded.dataShards, ...encoded.parityShards],
            16
        );

        const decoded = await erasureCoding.rsDecode(availableShards, indices, params);
        expect(decoded).to.deep.equal(data);
    });

    it("should handle systematic failures", async () => {
        const encoded = await erasureCoding.rsEncode(data, params);

        // Lose all data shards, keep only parity
        const decoded = await erasureCoding.rsDecode(
            encoded.parityShards,
            range(16, 32),
            params
        );

        expect(decoded).to.deep.equal(data);
    });
});

KZG Commitment Tests

describe("KZG Commitments", () => {
    let kzgParams;

    before(async () => {
        // Load trusted setup
        kzgParams = await loadTrustedSetup("powers-of-tau-4096.json");
    });

    it("should create and verify polynomial commitment", async () => {
        const data = crypto.randomBytes(16384); // 16KB
        const polynomial = await kzg.dataToPolynomial(data);

        const commitment = await kzg.commit(polynomial, kzgParams);
        expect(commitment.length).to.equal(48); // G1 point

        // Create opening proof at random point
        const point = BigInt(crypto.randomBytes(32).toString('hex'));
        const proof = await kzg.createProof(polynomial, point, kzgParams);

        expect(await kzg.verifyProof(proof, kzgParams)).to.be.true;
    });

    it("should batch verify multiple proofs", async () => {
        const proofs = [];
        for(let i = 0; i < 10; i++) {
            const poly = await kzg.dataToPolynomial(crypto.randomBytes(1024));
            const proof = await kzg.createProof(poly, BigInt(i), kzgParams);
            proofs.push(proof);
        }

        expect(await kzg.batchVerify(proofs, kzgParams)).to.be.true;
    });
});

Data Availability Sampling Tests

describe("Data Availability Sampling", () => {
    it("should verify availability through sampling", async () => {
        const data = crypto.randomBytes(10 * 1024 * 1024); // 10MB
        const commitmentHash = await daLayer.submitData(data);

        const samplingParams = {
            sampleSize: 75,
            confidence: 9990, // 99.9%
            maxAttempts: 3,
            seed: keccak256("random")
        };

        const requestId = await daLayer.requestSamples(commitmentHash, samplingParams);

        // Simulate node providing samples
        const indices = generateRandomIndices(75, totalChunks);
        const chunks = indices.map(i => getChunk(data, i));
        const proofs = indices.map(i => getMerkleProof(data, i));

        await daLayer.provideSamples(requestId, chunks, proofs);

        const response = await daLayer.getSampleResponse(requestId);
        expect(await daLayer.verifySamples(requestId, response)).to.be.true;
    });

    it("should calculate confidence correctly", async () => {
        expect(await daLayer.calculateConfidence(30, 100)).to.equal(9500); // 95%
        expect(await daLayer.calculateConfidence(75, 100)).to.equal(9990); // 99.9%
        expect(await daLayer.calculateConfidence(100, 100)).to.equal(10000); // 100%
    });
});

AI Model Storage Tests

describe("AI Model Storage", () => {
    it("should store and retrieve large model", async () => {
        const modelMetadata = {
            modelHash: keccak256("gpt-model"),
            architecture: "transformer",
            parameters: ethers.parseUnits("175", 9), // 175B parameters
            layers: 96,
            weightsHash: keccak256("weights"),
            optimizerStateHash: keccak256("adam-state"),
            version: 1,
            checkpoint: 100000
        };

        // Create layer data (simplified)
        const layers = [];
        for(let i = 0; i < 96; i++) {
            layers.push({
                layerIndex: i,
                weights: crypto.randomBytes(1024 * 1024), // 1MB per layer
                biases: crypto.randomBytes(1024),
                activations: new Uint8Array(0),
                quantizationBits: 16
            });
        }

        const modelHash = await modelStorage.storeModel(modelMetadata, layers);

        const [retrievedMetadata, retrievedLayers] = await modelStorage.retrieveModel(modelHash);
        expect(retrievedMetadata.parameters).to.equal(modelMetadata.parameters);
        expect(retrievedLayers.length).to.equal(96);
    });

    it("should handle incremental updates efficiently", async () => {
        const baseModelHash = keccak256("base-model");
        const gradients = generateGradients(1024 * 1024); // 1MB gradients

        const newModelHash = await modelStorage.applyGradientUpdate(baseModelHash, gradients);
        expect(newModelHash).to.not.equal(baseModelHash);

        // Verify storage efficiency (diff only)
        const storageUsed = await modelStorage.getStorageUsed(newModelHash);
        expect(storageUsed).to.be.lt(2 * 1024 * 1024); // Less than 2MB for diff
    });
});

Reference Implementation

Available at:

Key files:

  • contracts/DataAvailability.sol: Core DA contract
  • contracts/KZGVerifier.sol: KZG proof verification
  • contracts/ErasureCoding.sol: RS encoding/decoding
  • rust/: High-performance encoding implementation
  • circuits/: ZK circuits for storage proofs

Security Considerations

Cryptographic Security

  1. KZG Trusted Setup: Use ceremony with 100,000+ participants, only one honest party needed
  2. Erasure Code Security: Reed-Solomon provides information-theoretic security
  3. Randomness: Use VRF for unbiased sampling selection

Network Security

  1. Eclipse Attacks: Require connections to multiple diverse nodes
  2. Sybil Resistance: Stake-based node registration with minimum 1000 LUX
  3. Censorship: Economic penalties for withholding data

Storage Security

  1. Data Integrity: Merkle proofs for all chunk retrievals
  2. Availability: Daily challenges with automated slashing
  3. Privacy: Optional encryption with per-user keys

Economic Security

  1. Griefing: Require bonds for sampling requests
  2. Free-riding: Proof-of-storage prevents claiming rewards without storing
  3. Lazy Storage: Random challenges across full data lifetime

Economic Impact

Cost Analysis

  • L1 Calldata: $0.16 per KB (at 30 gwei)
  • DA Layer: $0.001 per KB (99.4% reduction)
  • Model Storage: $0.0001 per GB per month

Revenue Model

  • Storage Fees: 1% of storage payments
  • Retrieval Fees: 0.1% of retrieval payments
  • Sampling Rewards: 10 LUX per day from inflation

Market Size

  • Target: 1PB total storage within year 1
  • Revenue: $100,000/month at 10% utilization
  • Node Operators: 1000+ globally distributed

Open Questions

  1. Sharding Strategy: Optimal shard size for various data types
  2. Compression: Integration with specialized AI model compression
  3. Cross-chain DA: Serving multiple chains simultaneously
  4. Quantum Resistance: Migration to quantum-safe commitments

References

  1. Bassham, L., et al. (2022). "Data Availability Sampling Security Analysis"
  2. EIP-4844 (2023). "Shard Blob Transactions"
  3. Reed, I.S., & Solomon, G. (1960). "Polynomial Codes Over Certain Finite Fields"
  4. Kate, A., et al. (2010). "Constant-Size Commitments to Polynomials and Their Applications"
  5. Benet, J. (2014). "IPFS - Content Addressed, Versioned, P2P File System"
  6. Vorick, D., & Champine, L. (2014). "Sia: Simple Decentralized Storage"
  7. Williams, S., et al. (2018). "Arweave: A Protocol for Economically Sustainable Information Permanence"
  8. Al-Bassam, M. (2019). "LazyLedger: A Distributed Data Availability Ledger With Client-Side Smart Contracts"
  9. Nazirkhanova, K., et al. (2022). "Information Dispersal with Provable Retrievability for Rollups"

Implementation

Status: Specification stage - implementation planned for future release

Planned Locations:

  • DA layer core: ~/work/lux/da-layer/ (to be created)
  • Erasure coding: ~/work/lux/da-layer/erasure/ (Reed-Solomon)
  • KZG commitments: ~/work/lux/da-layer/kzg/ (polynomial commitments)
  • Storage network: ~/work/lux/da-layer/storage/ (distributed nodes)
  • Contracts: ~/work/lux/standard/src/da/ (EVM precompiles)

Build on Existing:

  • Uses Lux database abstraction layer (~/work/lux/database/)
  • Integrates with existing merkle proof system
  • Leverages Warp cross-chain messaging for data routing
  • Compatible with all Lux chains (P, X, C, Q)

Reference Implementations:

  • Ethereal data availability pattern (EIP-4844)
  • Celestia DA layer architecture
  • Polygon Avail framework

Development Phases:

  • Phase 1: Core DA interface and erasure coding
  • Phase 2: KZG trusted setup ceremony
  • Phase 3: Distributed storage network
  • Phase 4: Light client sampling
  • Phase 5: AI model storage optimization

Testing Strategy:

  • Uses ~/work/lux/netrunner/ for multi-node DA testing
  • Erasure coding property-based testing
  • Storage proof verification tests
  • Light client sampling simulation

Copyright and related rights waived via CC0.