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

feat(l2): multiple provers in file #1477

Open
wants to merge 32 commits into
base: l2/multiple_provers
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
81e210d
persist last_verfied_block in prover_server
fborello-lambda Nov 15, 2024
c72c380
pr_comments: unwrap -> expect
fborello-lambda Nov 15, 2024
0525cb7
Merge branch 'main' into l2/persist_verified_block
fborello-lambda Nov 15, 2024
bd3c7cc
cargo clippy
fborello-lambda Nov 15, 2024
772b1fa
wip
fborello-lambda Nov 15, 2024
a195fe5
prover_server: only respond if block was committed
fborello-lambda Nov 15, 2024
1b0aaae
Merge branch 'main' into l2/handle_not_committed_block
fborello-lambda Nov 15, 2024
7482b3f
add docs for OnChainProposer.verify()
fborello-lambda Nov 19, 2024
fe4d0d9
pr_comments: rm map_err
fborello-lambda Nov 19, 2024
d72d0fe
docs
fborello-lambda Nov 19, 2024
90d77f6
Merge branch 'main' into l2/handle_not_committed_block
fborello-lambda Nov 20, 2024
48c7bfc
fix
fborello-lambda Nov 20, 2024
1a53969
retry if any error is present
fborello-lambda Nov 20, 2024
4bb237d
separate logic into 2 different functions
fborello-lambda Nov 20, 2024
76a6588
committer: improve retry logic
fborello-lambda Nov 21, 2024
2bd4870
committer: simplify
fborello-lambda Nov 21, 2024
3799aa4
Merge branch 'main' into l2/handle_not_committed_block
fborello-lambda Nov 21, 2024
b04a492
prover_server: save state module
fborello-lambda Nov 21, 2024
aae4054
[wip] prover_server: basic logic
fborello-lambda Nov 21, 2024
0eae814
[wip] prover_server: simplify logic
fborello-lambda Nov 21, 2024
5c57104
wip
fborello-lambda Nov 21, 2024
6f19417
rm file
fborello-lambda Nov 21, 2024
23dcf3e
fix comments
fborello-lambda Nov 21, 2024
97fb12f
Merge remote-tracking branch 'origin/l2/prover_server_track_state' in…
fborello-lambda Dec 11, 2024
f43dbc8
feat(wip): save proofs in file
fborello-lambda Dec 11, 2024
992191a
feat: module that saves proofs from different proving systems
fborello-lambda Dec 12, 2024
067d787
refactor(prover_server): integrate the save_state mod with the prover…
fborello-lambda Dec 12, 2024
cc2b05a
feat: basic error handling
fborello-lambda Dec 12, 2024
4b71467
chore: lint
fborello-lambda Dec 12, 2024
787addc
chore: simple docs
fborello-lambda Dec 12, 2024
b58e573
chore: docs
fborello-lambda Dec 12, 2024
defb9ba
chore: docs
fborello-lambda Dec 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions cmd/ethrex_l2/src/commands/prove.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use clap::Args;
use ethrex_l2::utils::test_data_io::{generate_program_input, read_chain_file, read_genesis_file};
use ethrex_l2::utils::{
prover::proving_systems::ProverType,
test_data_io::{generate_program_input, read_chain_file, read_genesis_file},
};
use ethrex_prover_lib::prover::create_prover;

#[derive(Args)]
Expand Down Expand Up @@ -30,7 +33,7 @@ impl Command {
let chain = read_chain_file(&self.chain);
let program_input = generate_program_input(genesis, chain, self.block_number)?;

let mut prover = create_prover(ethrex_l2::proposer::prover_server::ProverType::RISC0);
let mut prover = create_prover(ProverType::RISC0);
prover.prove(program_input).expect("proving failed");
println!(
"Total gas consumption: {}",
Expand Down
3 changes: 3 additions & 0 deletions crates/l2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ secp256k1.workspace = true
keccak-hash = "0.10.0"
envy = "0.4.2"
thiserror.workspace = true
directories = "5.0.1"

zkvm_interface = { path = "./prover/zkvm/interface/", default-features = false }

# risc0
risc0-zkvm = { version = "1.2.0" }
# sp1
Expand Down
23 changes: 23 additions & 0 deletions crates/l2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,26 @@ ci_test: ## 🚧 Runs the L2's integration test, used by the github's CI

test: ## 🚧 Runs the L2's integration test, run `make init` and in a new terminal make test
BRIDGE_ADDRESS=$$(grep 'L1_WATCHER_BRIDGE_ADDRESS' .env | cut -d= -f2) ON_CHAIN_PROPOSER_ADDRESS=$$(grep 'COMMITTER_ON_CHAIN_PROPOSER_ADDRESS' .env | cut -d= -f2) cargo test --release testito -- --nocapture


# Purge L2's state

UNAME_S:=$(shell uname -s)
PROJECT_NAME:=ethrex_l2_state

ifeq ($(UNAME_S),Linux)
PROJECT_PATH := $(HOME)/.local/share/${PROJECT_NAME}
else ifeq ($(UNAME_S),Darwin)
PROJECT_PATH := $(HOME)/Library/Application\ Support/${PROJECT_NAME}
else
$(error Unsupported platform: $(UNAME_S))
endif

purge_prover_state: ## 🧹 Removes the L2 state, only use to start fresh.
@echo "Are you sure you want to delete the directory: $(PROJECT_PATH) ? [y/n]"
@read answer; \
if [ "$$answer" != "y" ]; then \
echo "Operation canceled."; \
fi; \
rm -rf $(PROJECT_PATH); \
echo "Directory deleted."
13 changes: 10 additions & 3 deletions crates/l2/proposer/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::mpsc::SendError;

use crate::utils::merkle_tree::MerkleError;
use crate::utils::prover::errors::SaveStateError;
use crate::utils::{config::errors::ConfigError, eth_client::errors::EthClientError};
use ethereum_types::FromStrRadixErr;
use ethrex_core::types::{BlobsBundleError, FakeExponentialError};
Expand Down Expand Up @@ -31,9 +32,9 @@ pub enum ProverServerError {
EthClientError(#[from] EthClientError),
#[error("ProverServer failed to send transaction: {0}")]
FailedToVerifyProofOnChain(String),
#[error("ProverServer failed retrieve block from storage: {0}")]
FailedToRetrieveBlockFromStorage(#[from] StoreError),
#[error("ProverServer failed retrieve block from storaga, data is None.")]
#[error("ProverServer failed to access Store: {0}")]
FailedAccessingStore(#[from] StoreError),
#[error("ProverServer failed to retrieve block from storaga, data is None.")]
StorageDataIsNone,
#[error("ProverServer failed to create ProverInputs: {0}")]
FailedToCreateProverInputs(#[from] EvmError),
Expand All @@ -43,6 +44,12 @@ pub enum ProverServerError {
JoinError(#[from] JoinError),
#[error("ProverServer failed: {0}")]
Custom(String),
#[error("ProverServer failed to write to TcpStream: {0}")]
WriteError(String),
#[error("ProverServer failed to get data from Store: {0}")]
ItemNotFoundInStore(String),
#[error("ProverServer encountered a SaveStateError: {0}")]
SaveStateError(#[from] SaveStateError),
}

#[derive(Debug, thiserror::Error)]
Expand Down
218 changes: 52 additions & 166 deletions crates/l2/proposer/prover_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ use crate::utils::{
prover_server::ProverServerConfig,
},
eth_client::{eth_sender::Overrides, EthClient, WrappedTransaction},
prover::{
errors::SaveStateError,
proving_systems::{ProverType, ProvingOutput},
save_state::*,
},
};
use ethrex_core::{
types::{Block, BlockHeader},
Expand All @@ -16,7 +21,6 @@ use keccak_hash::keccak;
use secp256k1::SecretKey;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::Debug,
io::{BufReader, BufWriter, Write},
net::{IpAddr, Shutdown, TcpListener, TcpStream},
Expand All @@ -30,9 +34,6 @@ use tokio::{
};
use tracing::{debug, error, info, warn};

use risc0_zkvm::sha::Digestible;
use sp1_sdk::HashableKey;

#[derive(Debug, Serialize, Deserialize, Default)]
pub struct ProverInputData {
pub block: Block,
Expand All @@ -49,137 +50,6 @@ struct ProverServer {
on_chain_proposer_address: Address,
verifier_address: Address,
verifier_private_key: SecretKey,
proving_output_per_block: HashMap<u64, HashMap<ProverType, ProvingOutput>>,
}

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
/// Enum used to identify the different proving systems.
pub enum ProverType {
RISC0,
SP1,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Risc0Proof {
pub receipt: Box<risc0_zkvm::Receipt>,
pub prover_id: Vec<u32>,
}

pub struct Risc0ContractData {
pub block_proof: Vec<u8>,
pub image_id: Vec<u8>,
pub journal_digest: Vec<u8>,
}

impl Risc0Proof {
pub fn new(receipt: risc0_zkvm::Receipt, prover_id: Vec<u32>) -> Self {
Risc0Proof {
receipt: Box::new(receipt),
prover_id,
}
}

pub fn contract_data(&self) -> Result<Risc0ContractData, ProverServerError> {
// If we run the prover_client with RISC0_DEV_MODE=0 we will have a groth16 proof
// Else, we will have a fake proof.
//
// The RISC0_DEV_MODE=1 should only be used with DEPLOYER_CONTRACT_VERIFIER=0xAA
let block_proof = match self.receipt.inner.groth16() {
Ok(inner) => {
// The SELECTOR is used to perform an extra check inside the groth16 verifier contract.
let mut selector =
hex::encode(inner.verifier_parameters.as_bytes().get(..4).ok_or(
ProverServerError::Custom(
"Failed to get verify_proof_selector in send_proof()".to_owned(),
),
)?);
let seal = hex::encode(inner.clone().seal);
selector.push_str(&seal);
hex::decode(selector).map_err(|e| {
ProverServerError::Custom(format!("Failed to hex::decode(selector): {e}"))
})?
}
Err(_) => vec![32; 0],
};

let mut image_id: [u32; 8] = [0; 8];
for (i, b) in image_id.iter_mut().enumerate() {
*b = *self.prover_id.get(i).ok_or(ProverServerError::Custom(
"Failed to get image_id in handle_proof_submission()".to_owned(),
))?;
}

let image_id: risc0_zkvm::sha::Digest = image_id.into();
let image_id = image_id.as_bytes().to_vec();

let journal_digest = Digestible::digest(&self.receipt.journal)
.as_bytes()
.to_vec();

Ok(Risc0ContractData {
block_proof,
image_id,
journal_digest,
})
}
}

#[derive(Serialize, Deserialize, Clone)]
pub struct Sp1Proof {
pub proof: Box<sp1_sdk::SP1ProofWithPublicValues>,
pub vk: sp1_sdk::SP1VerifyingKey,
}

impl Debug for Sp1Proof {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Sp1Proof")
.field("proof", &self.proof)
.field("vk", &self.vk.bytes32())
.finish()
}
}

pub struct Sp1ContractData {
pub public_values: Vec<u8>,
pub vk: Vec<u8>,
pub proof_bytes: Vec<u8>,
}

impl Sp1Proof {
pub fn new(
proof: sp1_sdk::SP1ProofWithPublicValues,
verifying_key: sp1_sdk::SP1VerifyingKey,
) -> Self {
Sp1Proof {
proof: Box::new(proof),
vk: verifying_key,
}
}

pub fn contract_data(&self) -> Result<Sp1ContractData, ProverServerError> {
let vk = self
.vk
.bytes32()
.strip_prefix("0x")
.ok_or(ProverServerError::Custom(
"Failed to strip_prefix of sp1 vk".to_owned(),
))?
.to_string();
let vk_bytes = hex::decode(&vk)
.map_err(|_| ProverServerError::Custom("Failed hex::decode(&vk)".to_owned()))?;

Ok(Sp1ContractData {
public_values: self.proof.public_values.to_vec(),
vk: vk_bytes,
proof_bytes: self.proof.bytes(),
})
}
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum ProvingOutput {
RISC0(Risc0Proof),
SP1(Sp1Proof),
}

/// Enum for the ProverServer <--> ProverClient Communication Protocol.
Expand Down Expand Up @@ -269,7 +139,6 @@ impl ProverServer {
on_chain_proposer_address,
verifier_address: config.verifier_address,
verifier_private_key: config.verifier_private_key,
proving_output_per_block: HashMap::new(),
})
}

Expand Down Expand Up @@ -382,16 +251,26 @@ impl ProverServer {

let mut tx_submitted = false;

if let Some(inner_hmap) = self.proving_output_per_block.get(&block_to_verify) {
// If we have all the proofs send a transaction to verify them on chain
if inner_hmap.contains_key(&ProverType::RISC0)
&& inner_hmap.contains_key(&ProverType::SP1)
{
self.handle_proof_submission(block_to_verify).await?;
// Remove the Proofs for the block_number
self.proving_output_per_block.remove(&block_to_verify);
tx_submitted = true;
// If we have all the proofs send a transaction to verify them on chain

let send_tx = match block_number_has_all_proofs(block_to_verify) {
Ok(has_all_proofs) => has_all_proofs,
Err(e) => {
if let SaveStateError::IOError(ref error) = e {
if error.kind() != std::io::ErrorKind::NotFound {
return Err(e.into());
}
} else {
return Err(e.into());
}
false
}
};
if send_tx {
self.handle_proof_submission(block_to_verify).await?;
// Remove the Proofs for that block_number
prune_state(block_to_verify)?;
tx_submitted = true;
}

let data: Result<ProofData, _> = serde_json::de::from_reader(buf_reader);
Expand Down Expand Up @@ -423,23 +302,33 @@ impl ProverServer {
return Ok(());
}

// The proof is stored,
// then if we have all the proofs, we send it in the loop's next iteration.
// Check if we have an entry for the block_number
let inner_hmap = self
.proving_output_per_block
.entry(block_number)
.or_default();

// Check if we have an entry for the proof in that block_number
// Get the ProverType, implicitly set by the ProvingOutput
let proving_type = match proving_output {
let prover_type = match proving_output {
ProvingOutput::RISC0(_) => ProverType::RISC0,
ProvingOutput::SP1(_) => ProverType::SP1,
};

// Check if we have the proof for that ProverType
// If we don't have it, insert it.
inner_hmap.entry(proving_type).or_insert(proving_output);
let has_proof = match block_number_has_state_file(
StateFileType::Proof(prover_type),
block_number,
) {
Ok(has_proof) => has_proof,
Err(e) => {
let error = format!("{e}");
if !error.contains("No such file or directory") {
return Err(e.into());
}
false
}
};
if !has_proof {
write_state(block_number, &StateType::Proof(proving_output))?;
}

// Then if we have all the proofs, we send the transaction in the next `handle_connection` call.
}
Err(e) => {
warn!("Failed to parse request: {e}");
Expand Down Expand Up @@ -535,24 +424,21 @@ impl ProverServer {
&self,
block_number: u64,
) -> Result<H256, ProverServerError> {
let proving_data =
self.proving_output_per_block
.get(&block_number)
.ok_or(ProverServerError::Custom(format!(
"Entry for {block_number} isn't present"
)))?;

let risc0_contract_data = match proving_data.get(&ProverType::RISC0) {
Some(ProvingOutput::RISC0(risc0_proof)) => risc0_proof.contract_data()?,
// TODO change error
let risc0_proving_output =
read_proof(block_number, StateFileType::Proof(ProverType::RISC0))?;
let risc0_contract_data = match risc0_proving_output {
ProvingOutput::RISC0(risc0_proof) => risc0_proof.contract_data()?,
_ => {
return Err(ProverServerError::Custom(
"RISC0 Proof isn't present".to_string(),
))
}
};

let sp1_contract_data = match proving_data.get(&ProverType::SP1) {
Some(ProvingOutput::SP1(sp1_proof)) => sp1_proof.contract_data()?,
let sp1_proving_output = read_proof(block_number, StateFileType::Proof(ProverType::SP1))?;
let sp1_contract_data = match sp1_proving_output {
ProvingOutput::SP1(sp1_proof) => sp1_proof.contract_data()?,
_ => {
return Err(ProverServerError::Custom(
"SP1 Proof isn't present".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions crates/l2/prover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ pub mod errors;
pub mod prover;
pub mod prover_client;

use ethrex_l2::{
proposer::prover_server::ProverType, utils::config::prover_client::ProverClientConfig,
use ethrex_l2::utils::{
config::prover_client::ProverClientConfig, prover::proving_systems::ProverType,
};
use tracing::warn;

Expand Down
Loading
Loading