mirror of
https://github.com/logos-blockchain/logos-blockchain-pocs.git
synced 2026-01-09 08:33:09 +00:00
Off eth ref (#67)
* dummy prover * delete binaries * absolute binary paths * revert processor change * improve prover with more idiomatic rust code * lightnode * lightnode * nomos types needed for client * credentials * basic http calls * cryptarchia info and block fetch loop * remove printlns * serve proofs through http interface * add missing deps * remove tracing initialization * check binary * fix logs * remove non-working code * fix --------- Co-authored-by: Petar Radovic <petar.radovic@gmail.com>
This commit is contained in:
parent
d464f8f465
commit
35cba3a980
@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["evm/processor", "evm/sequencer-node", "evm/prover"]
|
||||
members = ["evm/processor", "evm/sequencer-node", "evm/prover", "evm/lightnode"]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
@ -10,6 +10,7 @@ edition = "2024"
|
||||
evm-processor = { path = "evm/processor" }
|
||||
evm-sequencer-node = { path = "evm/sequencer-node" }
|
||||
evm-prover = { path = "evm/prover" }
|
||||
evm-lightnode = { path = "evm/lightnode" }
|
||||
|
||||
# External
|
||||
eyre = { version = "0.6" }
|
||||
|
||||
18
sz-poc-offsite-2025/evm/lightnode/Cargo.toml
Normal file
18
sz-poc-offsite-2025/evm/lightnode/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "evm-lightnode"
|
||||
edition = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
executor-http-client = { git = "https://github.com/logos-co/nomos", branch = "master" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
crypto-bigint = { version = "0.5.5", features = ["serde"] }
|
||||
risc0-zkvm = { version = "2" }
|
||||
indexmap = { version = "1.9.3" }
|
||||
url = { version = "2" }
|
||||
hex = { version = "0.4" }
|
||||
94
sz-poc-offsite-2025/evm/lightnode/src/lib.rs
Normal file
94
sz-poc-offsite-2025/evm/lightnode/src/lib.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use nomos::{CryptarchiaInfo, HeaderId};
|
||||
use reqwest::Url;
|
||||
use tracing::{error, info};
|
||||
|
||||
pub const CRYPTARCHIA_INFO: &str = "cryptarchia/info";
|
||||
pub const STORAGE_BLOCK: &str = "storage/block";
|
||||
|
||||
pub mod nomos;
|
||||
pub mod proofcheck;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Credentials {
|
||||
pub username: String,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
pub struct NomosClient {
|
||||
base_url: Url,
|
||||
reqwest_client: reqwest::Client,
|
||||
basic_auth: Credentials,
|
||||
}
|
||||
|
||||
impl NomosClient {
|
||||
pub fn new(base_url: Url, basic_auth: Credentials) -> Self {
|
||||
Self {
|
||||
base_url,
|
||||
reqwest_client: reqwest::Client::new(),
|
||||
basic_auth,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_cryptarchia_info(&self) -> Result<CryptarchiaInfo, String> {
|
||||
let url = self.base_url.join(CRYPTARCHIA_INFO).expect("Invalid URL");
|
||||
|
||||
info!("Requesting cryptarchia info from {}", url);
|
||||
let request = self.reqwest_client.get(url).basic_auth(
|
||||
&self.basic_auth.username,
|
||||
self.basic_auth.password.as_deref(),
|
||||
);
|
||||
|
||||
let response = request.send().await.map_err(|e| {
|
||||
error!("Failed to send request: {}", e);
|
||||
"Failed to send request".to_string()
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
error!("Failed to get cryptarchia info: {}", response.status());
|
||||
return Err("Failed to get cryptarchia info".to_string());
|
||||
}
|
||||
|
||||
let info = response.json::<CryptarchiaInfo>().await.map_err(|e| {
|
||||
error!("Failed to parse response: {}", e);
|
||||
"Failed to parse response".to_string()
|
||||
})?;
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
pub async fn get_block(&self, id: HeaderId) -> Result<serde_json::Value, String> {
|
||||
let url = self.base_url.join(STORAGE_BLOCK).expect("Invalid URL");
|
||||
|
||||
info!("Requesting block with HeaderId {}", id);
|
||||
let request = self
|
||||
.reqwest_client
|
||||
.post(url)
|
||||
.header("Content-Type", "application/json")
|
||||
.basic_auth(
|
||||
&self.basic_auth.username,
|
||||
self.basic_auth.password.as_deref(),
|
||||
)
|
||||
.body(serde_json::to_string(&id).unwrap());
|
||||
|
||||
let response = request.send().await.map_err(|e| {
|
||||
error!("Failed to send request: {}", e);
|
||||
"Failed to send request".to_string()
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
error!("Failed to get block: {}", response.status());
|
||||
return Err("Failed to get block".to_string());
|
||||
}
|
||||
|
||||
let json: serde_json::Value = response.json().await.map_err(|e| {
|
||||
error!("Failed to parse JSON: {}", e);
|
||||
"Failed to parse JSON".to_string()
|
||||
})?;
|
||||
|
||||
info!(
|
||||
"Block (raw): {}",
|
||||
serde_json::to_string_pretty(&json).unwrap()
|
||||
);
|
||||
|
||||
Ok(json)
|
||||
}
|
||||
}
|
||||
48
sz-poc-offsite-2025/evm/lightnode/src/main.rs
Normal file
48
sz-poc-offsite-2025/evm/lightnode/src/main.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use clap::Parser;
|
||||
use evm_lightnode::proofcheck;
|
||||
use url::Url;
|
||||
use std::path::PathBuf;
|
||||
use std::error;
|
||||
use tracing_subscriber::{EnvFilter, fmt};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about = "Light Node validator")]
|
||||
struct Args {
|
||||
#[clap(long, default_value = "info")]
|
||||
log_level: String,
|
||||
|
||||
#[clap(long, default_value = "http://localhost:8546")]
|
||||
rpc: Url,
|
||||
|
||||
#[clap(long, default_value = "http://localhost:8070")]
|
||||
prover_url: Url,
|
||||
|
||||
#[clap(long)]
|
||||
start_block: u64,
|
||||
|
||||
#[clap(long, default_value = "10")]
|
||||
batch_size: u64,
|
||||
|
||||
#[clap(long)]
|
||||
zeth_binary_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
let filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level));
|
||||
|
||||
fmt::fmt().with_env_filter(filter).with_target(false).init();
|
||||
|
||||
proofcheck::verify_proof(
|
||||
args.start_block,
|
||||
args.batch_size,
|
||||
&args.rpc,
|
||||
&args.prover_url,
|
||||
&args.zeth_binary_dir.unwrap_or_else(|| std::env::current_dir().unwrap()).join("zeth-ethereum"),
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
52
sz-poc-offsite-2025/evm/lightnode/src/nomos.rs
Normal file
52
sz-poc-offsite-2025/evm/lightnode/src/nomos.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use hex::FromHex;
|
||||
|
||||
// tip "4f573735fb987453f7467688ea4e034b9161e3ca200526faf5c8ce6db09da180"
|
||||
// slot 5085
|
||||
// height 1245
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CryptarchiaInfo {
|
||||
pub tip: HeaderId,
|
||||
pub slot: u64,
|
||||
pub height: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash, PartialOrd, Ord, Default)]
|
||||
pub struct HeaderId([u8; 32]);
|
||||
|
||||
impl<'de> Deserialize<'de> for HeaderId {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let hex_str = String::deserialize(deserializer)?;
|
||||
|
||||
let bytes = <[u8; 32]>::from_hex(hex_str)
|
||||
.map_err(|e| serde::de::Error::custom(format!("Invalid hex string: {}", e)))?;
|
||||
|
||||
Ok(HeaderId(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for HeaderId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let hex_str = hex::encode(self.0);
|
||||
serializer.serialize_str(&hex_str)
|
||||
}
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for HeaderId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for byte in &self.0 {
|
||||
write!(f, "{:02x}", byte)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
57
sz-poc-offsite-2025/evm/lightnode/src/proofcheck.rs
Normal file
57
sz-poc-offsite-2025/evm/lightnode/src/proofcheck.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use tokio::process::Command;
|
||||
use std::path::Path;
|
||||
use reqwest::Url;
|
||||
use tracing::{error, info};
|
||||
|
||||
pub async fn verify_proof(
|
||||
block_number: u64,
|
||||
block_count: u64,
|
||||
rpc: &Url,
|
||||
prover_url: &Url,
|
||||
zeth_bin: &Path,
|
||||
) -> Result<(), String> {
|
||||
info!(
|
||||
"Verifying proof for blocks {}-{}",
|
||||
block_number,
|
||||
block_number + block_count - 1
|
||||
);
|
||||
|
||||
let url = prover_url.join(&format!(
|
||||
"/?block_start={}&block_count={}",
|
||||
block_number, block_count
|
||||
)).map_err(|e| format!("Failed to construct URL: {}", e))?;
|
||||
let proof = reqwest::get(url).await
|
||||
.map_err(|e| format!("Failed to fetch proof: {}", e))?
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read proof response: {}", e))?;
|
||||
let filename = format!("{}-{}.zkp", block_number, block_count + block_number);
|
||||
tokio::fs::write(&filename, &proof)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to write proof to file: {}", e))?;
|
||||
|
||||
|
||||
let output = Command::new(zeth_bin)
|
||||
.args([
|
||||
"verify",
|
||||
&format!("--rpc={}", rpc),
|
||||
&format!("--block-number={}", block_number),
|
||||
&format!("--block-count={}", block_count),
|
||||
&format!("--file={}", filename),
|
||||
])
|
||||
.output().await
|
||||
.map_err(|e| format!("Failed to execute zeth-ethereum verify: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("zeth-ethereum verify command failed: {}", stderr);
|
||||
return Err(format!(
|
||||
"zeth-ethereum verify command failed with status: {}\nStderr: {}",
|
||||
output.status, stderr
|
||||
));
|
||||
}
|
||||
|
||||
info!("Proof verification successful");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -8,4 +8,4 @@ executor-http-client = { git = "https://github.com/logos-co/nomos", branch = "ma
|
||||
reqwest = "0.11"
|
||||
kzgrs-backend = { git = "https://github.com/logos-co/nomos", branch = "master" }
|
||||
bincode = "1"
|
||||
reth-tracing = { workspace = true }
|
||||
reth-tracing = { workspace = true }
|
||||
|
||||
@ -3,9 +3,11 @@ name = "evm-prover"
|
||||
edition = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8.3"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
39
sz-poc-offsite-2025/evm/prover/src/http.rs
Normal file
39
sz-poc-offsite-2025/evm/prover/src/http.rs
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
use axum::{
|
||||
extract::Query,
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
use tokio::fs;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ProofRequest {
|
||||
block_start: u64,
|
||||
block_count: u64
|
||||
}
|
||||
|
||||
|
||||
/// Handler for GET /
|
||||
pub async fn serve_proof(Query(query): Query<ProofRequest>) -> Response {
|
||||
let file_name = format!("{}-{}.zkp", query.block_start, query.block_count + query.block_start);
|
||||
|
||||
let path = PathBuf::from(&file_name);
|
||||
|
||||
// Read file contents
|
||||
match fs::read(&path).await {
|
||||
Ok(bytes) => (
|
||||
StatusCode::OK,
|
||||
bytes,
|
||||
).into_response(),
|
||||
Err(err) => {
|
||||
let status = if err.kind() == std::io::ErrorKind::NotFound {
|
||||
StatusCode::NOT_FOUND
|
||||
} else {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
};
|
||||
(status, format!("Error reading file: {}", err)).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,15 @@
|
||||
use clap::Parser;
|
||||
use reqwest::blocking::Client;
|
||||
use serde_json::{Value, json};
|
||||
use std::{path::PathBuf, process::Command, thread, time::Duration};
|
||||
use serde_json::{json, Value};
|
||||
use std::{path::PathBuf, process::Command, thread, time::Duration, net::SocketAddr};
|
||||
use tracing::{debug, error, info};
|
||||
use tracing_subscriber::{EnvFilter, fmt};
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
|
||||
mod http;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about = "Ethereum Proof Generation Tool")]
|
||||
@ -117,6 +123,12 @@ fn get_latest_block(client: &Client, rpc: &str) -> Result<u64, String> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = run_server() {
|
||||
error!("Error running server: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
let filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level));
|
||||
|
||||
@ -166,3 +178,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn run_server() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Build our application with a route
|
||||
let app = Router::new()
|
||||
.route("/", get(http::serve_proof));
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 8070));
|
||||
// Run it on localhost:8070
|
||||
tracing::info!("Serving files on http://{}", addr);
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
use evm_processor::{Processor, NomosDa, BasicAuthCredentials};
|
||||
use evm_processor::{BasicAuthCredentials, NomosDa, Processor};
|
||||
use futures::TryStreamExt as _;
|
||||
use reth::{
|
||||
api::{FullNodeTypes, NodePrimitives, NodeTypes},
|
||||
@ -25,13 +25,15 @@ where
|
||||
continue;
|
||||
};
|
||||
info!(committed_chain = ?new.range(), "Received commit");
|
||||
processor.process_blocks(
|
||||
new.inner()
|
||||
.0
|
||||
.clone()
|
||||
.into_blocks()
|
||||
.map(reth_ethereum::primitives::RecoveredBlock::into_block),
|
||||
).await;
|
||||
processor
|
||||
.process_blocks(
|
||||
new.inner()
|
||||
.0
|
||||
.clone()
|
||||
.into_blocks()
|
||||
.map(reth_ethereum::primitives::RecoveredBlock::into_block),
|
||||
)
|
||||
.await;
|
||||
|
||||
ctx.events
|
||||
.send(ExExEvent::FinishedHeight(new.tip().num_hash()))
|
||||
@ -57,7 +59,10 @@ fn main() -> eyre::Result<()> {
|
||||
let url = std::env::var("NOMOS_EXECUTOR").unwrap_or(TESTNET_EXECUTOR.to_string());
|
||||
let user = std::env::var("NOMOS_USER").unwrap_or_default();
|
||||
let password = std::env::var("NOMOS_PASSWORD").unwrap_or_default();
|
||||
let da = NomosDa::new( BasicAuthCredentials::new(user, Some(password)), url::Url::parse(&url).unwrap());
|
||||
let da = NomosDa::new(
|
||||
BasicAuthCredentials::new(user, Some(password)),
|
||||
url::Url::parse(&url).unwrap(),
|
||||
);
|
||||
let processor = Processor::new(da);
|
||||
let handle = Box::pin(
|
||||
builder
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user