EigenDA: Start developing on Blobs today

Ahmed Castro - Feb 16 - - Dev Community

Danksharding is coming to Ethereum Mainnet in less than a month, giving rollup developers more block space at a lower cost. However, even with this progress, all demands won't be fully met. This has led different projects to roll out their own data availability solutions or DA in short. Celestia, Near, and others are building their own solutions but my current favorite is EigenDA.

Why EigenDA?

I believe EigenDA offers the best economic guarantees and the best compatibility with dApps on Ethereum and L2s since it is native to the Ethereum Mainnet and uses Ether as the base economic security. Lately, my focus has been on developing dApps on L3s without economic incentives for attacks, such as social web3 applications or video games. I think EigenDA is a good solution for this even if it doesn't offer the same security as Ethereum. This has led me to research deeper into EigenDA technology.

This article is my current progress on my research on how to use EigenDA, where we'll learn how to interact with blobs. How to store them and how rollups can use them to scale.

Setup your environment

Install grpcurl

EigenDA suggests using grpcurl to disperse and retrieve blobs.

MacOS

brew install grpcurl
Enter fullscreen mode Exit fullscreen mode

Linux

In order to install it we will need the Go compiler, install it if you haven't.

https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz 
export PATH=$PATH:/usr/local/go/bin
Enter fullscreen mode Exit fullscreen mode

Now install grpcurl.

go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
Enter fullscreen mode Exit fullscreen mode

grpcurl is now installed at $HOME/go/bin/grpcurl.

Install foundry

Install the forge command if you haven't.

curl -L https://foundry.paradigm.xyz | bash
foundryup
Enter fullscreen mode Exit fullscreen mode

Clone the repo

Now clone the EigenDA repo and cd into it.

git clone https://github.com/Layr-Labs/eigenda.git
cd eigenda
Enter fullscreen mode Exit fullscreen mode

1. Disperse a blob

Let's start by putting some data on EigenDA, this process is called "Dispersing a Blob". If you're building a rollup you might want to store your transaction data history to allow users to permissionlessly retrieve any asset, funds or any information from L2 to L1. To achieve this we need to store data in the form of a merkle tree root, one merkle root at the time every now and then.

We're keeping it simple for now, in the following command we store "Hello Eigen DA!", feel free to put whatever data you want.

$HOME/go/bin/grpcurl -import-path ./api/proto -proto ./api/proto/disperser/disperser.proto -d '{"data": "Hello Eigen DA!", "security_params": [{"quorum_id": 0, "adversary_threshold": 25, "quorum_threshold": 50}]}' disperser-goerli.eigenda.xyz:443 disperser.Disperser/DisperseBlob
Enter fullscreen mode Exit fullscreen mode

This will return a json that includes a requestId value, save it somewhere. We will need it in the following steps.

2. Query the blob status

Wait about a minute and call the following.

$HOME/go/bin/grpcurl -import-path ./api/proto -proto ./api/proto/disperser/disperser.proto -d '{"request_id": "YOUR REQUEST ID"}' disperser-goerli.eigenda.xyz:443 disperser.Disperser/GetBlobStatus
Enter fullscreen mode Exit fullscreen mode

This will return a json with your blob status. Save it somewhere, we'll need it in a moment.

Optional: Query the data you stored directly by running the following command and passing the info.blobVerificationProof.batchHeaderHash returned on out blob status json.

$HOME/go/bin/grpcurl -import-path ./api/proto -proto ./api/proto/disperser/disperser.proto -d '{"batch_header_hash": "YOUR BLOB HEADER HASH", "blob_index":"0"}' disperser-goerli.eigenda.xyz:443 disperser.Disperser/RetrieveBlob
Enter fullscreen mode Exit fullscreen mode

3. Verify the blob on-chain

Now, create a contracts/scripts/BlobVerification.sol file and put all your blob status data on the contract as described below. The blob status json uses base64 encoding, so I recommend you using this decoder online, and this hex to dec converter for the X and Y commitment.

contracts/scripts/BlobVerification.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

import "forge-std/Script.sol";
import "../src/rollup/MockRollup.sol";
import {EigenDARollupUtils} from "../src/libraries/EigenDARollupUtils.sol";

contract MockRollupDeployer is Script {
    function run() external {
        vm.startBroadcast();

        IEigenDAServiceManager.QuorumBlobParam[]
            memory quorumBlobParams = new IEigenDAServiceManager.QuorumBlobParam[](1);

        quorumBlobParams[0] = IEigenDAServiceManager.QuorumBlobParam(
            /*quorumNumber*/0,
            /*adversaryThresholdPercentage*/25,
            /*quorumThresholdPercentage*/50,
            /*chunkLength*/1);

        IEigenDAServiceManager.BlobHeader memory blobHeader = IEigenDAServiceManager.BlobHeader(
            BN254.G1Point(/*X*/4317633011943442688312675280968246407649523414935450151451943114581321374794,
                          /*Y*/19103178923486544823523596462585377039936043999112285141018622651122914686888),
            /*dataLength*/ 1,
            quorumBlobParams
        );

        address eigenDAServiceManager = 0xa3b1689Ab85409B15e07d2ED50A6EA9905074Ee5;
        //0x1eEa1C6b573f192F33BE6AA56dC9080314a89491;

        IEigenDAServiceManager.BatchHeader memory batchHeader 
            = IEigenDAServiceManager.BatchHeader(
                /*bytes32 blobHeadersRoot*/ 0x1d1af6d981553efb8d0a7b6d6022b56d1fbf2a615d1dd5a034e6870e87159a68,
                /*bytes quorumNumbers*/ hex"00",
                /*bytes quorumThresholdPercentages*/ hex"5f", 
                /*uint32 referenceBlockNumber*/ 10544012
            );

        IEigenDAServiceManager.BatchMetadata memory batchMetadata
            = IEigenDAServiceManager.BatchMetadata(
                batchHeader,
                /*bytes32 signatoryRecordHash*/ 0x3bb6b9f58f262b89fb02e339a045438b4765f6f09ca136d045556b3869e07b70,
                /*uint96 fee*/0,
                /*uint32 confirmationBlockNumber*/10544016
            );

        EigenDARollupUtils.BlobVerificationProof memory blobVerificationProof
            = EigenDARollupUtils.BlobVerificationProof(
                /*uint32 batchId*/ 12580,
                /*uint8 blobIndex*/ 9,
                batchMetadata,
                /*bytes inclusionProof*/ hex"082915e241f357b6c2f43fac8767a909286b7f9e392c04a40caf9be28486e561b2127431770921e8f3461e1a4d845e2991e803d1c6591655873bf1cc03702cb5626b47f9b14bb86bbfaf11ad723b5bdaa3a25fc33dda9f768e58c1df463d9e0d279e7a791b0bdcbe8f32610c0ff7cfb99fd15ad0aa9091d2d02ab520a34536793829e2605b7aa684f488b51ffff369f49e23fc075b1d0413fca94f9bb670b92a024b2b6efbf73d9f0522191941d18c15ea5713fb87c679cd7abf2fdec390b5fd",
                /*bytes quorumThresholdIndexes*/ hex"00"
            );

        EigenDARollupUtils.verifyBlob(blobHeader, IEigenDAServiceManager(eigenDAServiceManager), blobVerificationProof);

        vm.stopBroadcast();
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Test it

Currently, EigenDA is deployed on Goerli. Let's fork it and run an RPC locally.

anvil --fork-url https://goerli.gateway.tenderly.co
#https://rpc.tornadoeth.cash/goerli
Enter fullscreen mode Exit fullscreen mode

If all data is correct, the following command should not revert.

cd contracts
forge script script/BlobVerification.sol --rpc-url http://127.0.0.1:8545
Enter fullscreen mode Exit fullscreen mode

What's next?

Now that we know how to call the verifyBlob() function from EigenDA, we should be able to verify rollup data stored on a format similar to the following draft.

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

struct G1Point {
    uint256 X;
    uint256 Y;
}

contract MyNaiveOptimisticCrentralizedRollupDA is Ownable {
    mapping(uint timestamp => bytes32 data) public dataHistory;
    mapping(uint timestamp => G1Point commitment) public commitmentsHistory;

    function postData(uint timestamp, bytes32 data) public onlyOwner {
        dataHistory[timestamp] = data;
    }

    function postCommitment(uint timestamp, G1Point memory commitment) public onlyOwner {
        commitmentsHistory[timestamp] = commitment;
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice that we need a trusted actor to post the data on-chain. This is part of the role of what is commonly known as "The Sequencer" who need to do the following:

  1. Gather all the transaction data from users on L2
  2. Disperse it to EigenDA
  3. Post both the commitment and data on L1 and verify it using EigenDA's verifyBlob function

Take a look at this official example from EigenDA that shows a very naive implementation of a Rollup that posts blobs optimistically and then challenge them.

Also, take a look at the official documentation.

I think there is a lot more to come about blobs in terms of tools and use cases. I'll be covering it here. Stay tuned!

Thanks for reading this guide!

Follow Filosofía Código on dev.to and in Youtube for everything related to Blockchain development.

. . . . . . . . . . . . . . . . . . . . . . . . .