Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement eigenDA client remaining features #3243

Open
wants to merge 51 commits into
base: main
Choose a base branch
from

Conversation

juanbono
Copy link

@juanbono juanbono commented Nov 8, 2024

What ❔

This PR adds extra functionality to the eigen client:

Why ❔

These features are needed to have a full featured client and to have the same features as when communicating with the EigenDA proxy.

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • Code has been formatted via zkstack dev fmt and zkstack dev lint.

juan518munoz and others added 23 commits October 31, 2024 17:14
Implement non auth blob dispersal for eigen client

---------

Co-authored-by: Gianbelinche <[email protected]>
feat(eigen-client-extra-features): get inclusion data
* initial commit

* Add tests

* Add memstore

* Add assert to tests

* Add rest of memstore

* Address pr comments

* Remove to retriable error

* Fix conflicts

* Document memstore

* Fix typo

---------

Co-authored-by: Juan Munoz <[email protected]>
blob size limit

---------

Co-authored-by: Gianbelinche <[email protected]>
* initial commit

* Add tests

* Add memstore

* Add assert to tests

* Add rest of memstore

* Add soft confirmations

* Remove print

* Address pr comments

* Remove to retriable error

* Fix conflicts

* Add inclusion data

* Change status query timeout to millis

* Document memstore

* Fix typo

* Fix typo

* Format

---------

Co-authored-by: Juan Munoz <[email protected]>
* initial commit

* Add tests

* Add memstore

* Add assert to tests

* Add rest of memstore

* Address pr comments

* Remove to retriable error

* Fix conflicts

* Add verifier

* Fix verifier

* Add path to points to config

* Fix typo

* Fix eigenda env test

* Fix verifier test

* Move eigendaservicemanager to generated

* Remove unneeded imports

* Document verifier

* Modify errors

* Address comments

* Fix conflicts

---------

Co-authored-by: Juan Munoz <[email protected]>
* initial commit

* move file location

* fix step
#329)

* Remove unused custom quorum numbers

* Add serial to tests
feat(eigen-client-extra-features): fix clippy and add doc
feat(eigen-client-extra-features): merge main
@juanbono juanbono changed the title Eigen client extra features Implement eigenDA client remaining features Nov 8, 2024
juan518munoz and others added 2 commits November 12, 2024 11:02
…dule-update

feat(eigen-client-extra-features): update contracts submodule
core/node/da_clients/src/eigen/verifier.rs Outdated Show resolved Hide resolved
fn test_verify_commitment() {
let verifier = super::Verifier::new(super::VerifierConfig {
verify_certs: true,
rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like the test relies on the external RPC, which means it could be blocking the CI if RPC is unavailable
let's rework this to remove the external dependency

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, the thing is that we can't test it locally since there is no working local setup for EigenDA. So we had to test on testnet directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use mocks instead of external calls?


let verifier_config = VerifierConfig {
verify_certs: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe let's make it a part of the config

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what part are you referring to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially thought about verify_certs variable, but now that I think about it, it shouldn't exist at all
to verify the inclusion or not should be defined by the use_dummy_inclusion_data variable, if it is true - the verifier won't even be used

rpc_url: config.eigenda_eth_rpc.clone(),
svc_manager_addr: config.eigenda_svc_manager_address.clone(),
max_blob_size: config.blob_size_limit,
path_to_points: config.path_to_points.clone(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly that these points have to be present every time this code is run? This would add an ops load that we would ideally want to avoid. Is there a way to get rid of the points file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need it every time that code is run. One solution can be to have the contents of the file bundled with the binary. The downside of that approach is that it will increase the size of the resulting binary in ~30mb

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I see
none of it is a great option tbh, let's do something to optimize this
maybe we could ask the EigenDA team to publish it to the S3/GCS bucket managed by them, and then in case the client is started with the verification enabled - it will fetch the points from the bucket

core/node/da_clients/src/eigen/sdk.rs Outdated Show resolved Hide resolved
core/node/da_clients/src/eigen/memstore.rs Outdated Show resolved Hide resolved
core/node/da_clients/Cargo.toml Outdated Show resolved Hide resolved
core/node/da_clients/Cargo.toml Outdated Show resolved Hide resolved
core/node/da_clients/Cargo.toml Outdated Show resolved Hide resolved
core/lib/protobuf_config/src/proto/config/da_client.proto Outdated Show resolved Hide resolved
* Remove kzgpad

* Add empty pad and unpad test
* Add backon instead loops

* Add constant for avg block time
* Remove extra config

* Use bn254 crate
* Add zksync client

* Call service manager with zksync client

* Add rest of calls

* Remove alloy

* Remove unwraps

* Remove todos

* Harcode function selectors

* Remove service manager json

* Fix clippy
…merge-main

Eigen client extra features merge main
pub disperser_rpc: String,
/// Block height needed to reach in order to consider the blob finalized
/// a value less or equal to 0 means that the disperser will not wait for finalization
pub eth_confirmation_depth: i32,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to use l1_ or settlement_layer_ here instead of eth_ because the settlement layer is not necessarily an Ethereum mainnet

/// a value less or equal to 0 means that the disperser will not wait for finalization
pub eth_confirmation_depth: i32,
/// URL of the Ethereum RPC server
pub eigenda_eth_rpc: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can reuse the RPC that's used for sending txs to L1, it is already provided in the secrets

/// Address of the service manager contract
pub eigenda_svc_manager_address: String,
/// Maximum size permitted for a blob in bytes
pub blob_size_limit: u32,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shouldn't be a part of the config, it is defined by the "protocol" i.e. the max value that EigenDA can handle and there is a check in da_dispatcher that max_pubdata_per_batch is lower than that value

/// Path to the file containing the points used for KZG
pub path_to_points: String,
/// Chain ID of the Ethereum network
pub chain_id: u64,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like it is unused

@@ -37,8 +37,19 @@ message CelestiaConfig {
}

message EigenConfig {
optional string rpc_node_url = 1;
optional uint64 inclusion_polling_interval_ms = 2;
reserved 1,2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move it to the bottom of the message for better readability and let's also reserve the field name, not only the index

(|| async { self.verifier.verify_certificate(blob_info.clone()).await })
.retry(
&ConstantBuilder::default()
.with_delay(Duration::from_secs(AVG_BLOCK_TIME))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you shouldn't use 12s delay here
if we assume this func is called with some interval (defined by da_dispatcher) - we shouldn't be doing any sleeps here at all, only retries in case the request to L1 failed

.verify_commitment(blob_info.blob_header.commitment.clone(), data)
.map_err(|_| anyhow::anyhow!("Failed to verify commitment"))?;

self.loop_verify_certificate(blob_info.clone(), disperse_elapsed)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, dispatch shouldn't be blocked by inclusion

}

impl Verifier {
const DEFAULT_PRIORITY_FEE_PER_GAS: u64 = 100;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it needed if the client won't be sending any txs?


impl Verifier {
const DEFAULT_PRIORITY_FEE_PER_GAS: u64 = 100;
const BATCH_ID_TO_METADATA_HASH_FUNCTION_SELECTOR: [u8; 4] = [236, 203, 191, 201];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is going to be difficult to maintain, could you please use something like this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just recalled that there is an easier way where you don't need to include the contracts in our codebase, example:

let func_selector = ethabi::short_signature(
            "validatePubdata",
            &[
                ParamType::FixedBytes(32),
                ParamType::FixedBytes(32),
                ParamType::FixedBytes(32),
                ParamType::FixedBytes(32),
                ParamType::Bytes,
            ],
        );

fn test_verify_commitment() {
let verifier = super::Verifier::new(super::VerifierConfig {
verify_certs: true,
rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use mocks instead of external calls?

gianbelinche and others added 11 commits November 28, 2024 15:38
* Move inclusion logic

* Remove unused import
* Move inclusion logic

* Remove verify cert
* Change eth conf depth for settlement layer

* Remove blob size limit

* Move reserved

* Change function name

* Add constants to lib

* Fix compilation
* initial commit

* remove anyhow err

* Format code

* Fix compilation

---------

Co-authored-by: Gianbelinche <[email protected]>
* Add option to download points

* Fix test

* Fix clippy

* Change name to points source

* Fix test

* Fix test
…s with mock tests (#356)

* initial commit

* create verifier client trait

* remove unwraps

* Update core/node/da_clients/src/eigen/verifier.rs

Ignore test

Co-authored-by: Gianbelinche <[email protected]>

* Update core/node/da_clients/src/eigen/sdk.rs

Co-authored-by: Gianbelinche <[email protected]>

* change ignore test comment

* Format code

---------

Co-authored-by: Gianbelinche <[email protected]>
…on docs (#360)

* Remove steps to run from integration docs

* Combine readme

* Clarify readme
…and-main

feat(eigen-client-extra-features): Merge Main

impl Default for PointsSource {
fn default() -> Self {
PointsSource::Path("".to_string())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that this was added to allow #[derive(Default)] for EigenConfig, but if someone actually uses it - it won't work for them
can we publish the points to some storage, e.g. GCS, and refer to that storage by default

/// Authenticated dispersal
pub authenticated: bool,
/// Verify the certificate of dispatched blobs
pub verify_cert: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it used anywhere?

optional bool authenticated = 10;
optional bool verify_cert = 11;
oneof points_source {
Path path = 12;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do it like this

oneof points_source {
    string path = 12;
    string link = 13;
}

oneof assumes that it is optional by default

The generated files are received by compiling the `.proto` files from EigenDA repo using the following function:
There a 3 milestones defined, we are currently on the first one.

### M0: Read and Write integration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I understand, that's more of a Lambda's milestone here, MatterLabs has slightly different ones
let's consider either removing this or making it more generic like

  • create an MVP client
  • improve the client //we are here
  • enable verification on L1
  • optimize


For this milestone the scope is to replace the mocked L1 verification logic with EigenDA compatible verifier. It should
integrate EigenDA certificate verification, and use it as the equivalent part for 4844 versioned hash. More importantly
modify the equivalence proof in Zksync L1 contract such that the proof can be verified correctly with respect to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I understand it correctly - you are saying you want to integrate the EigenDA inclusion verification in our circuits
I doubt that it is possible, most likely a separate proof will be used for it

impl Verifier {
pub const DEFAULT_PRIORITY_FEE_PER_GAS: u64 = 100;
async fn save_points(link: String) -> Result<String, VerificationError> {
let url_g1 = format!("{}{}", link, "/g1.point");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's better use the Url package for this, to avoid issues when someone provides a path with trailing slash and it all fails

&format!("{}{}", path, "/g1.point"),
"",
&format!("{}{}", path, "/g2.point.powerOf2"),
268435456, // 2 ^ 28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's maybe use constant for it?

Comment on lines +342 to +366
let mut data = BATCH_ID_TO_METADATA_HASH_FUNCTION_SELECTOR.to_vec();
let mut batch_id_vec = [0u8; 32];
U256::from(cert.blob_verification_proof.batch_id).to_big_endian(&mut batch_id_vec);
data.append(batch_id_vec.to_vec().as_mut());

let call_request = CallRequest {
to: Some(
H160::from_str(&self.cfg.svc_manager_addr)
.map_err(|_| VerificationError::ServiceManagerError)?,
),
data: Some(zksync_basic_types::web3::Bytes(data)),
..Default::default()
};

let res = self
.signing_client
.as_ref()
.call_contract_function(
call_request,
Some(BlockId::Number(BlockNumber::Number(context_block.into()))),
)
.await
.map_err(|_| VerificationError::ServiceManagerError)?;

let expected_hash = res.0.to_vec();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of this can be moved to the separate func for better readability

}
}

/// Mock struct for the Verifier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here with tests, can they be moved to a separate file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per comment above, let's remove those and use some remote storage as default

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants