Skip to content

Commit

Permalink
Blobstream service (#13)
Browse files Browse the repository at this point in the history
* basic service CLI pre-impl

* very basic version working, messy

* add midding comments

* remove unnecessary trait bounds

* implement bonsai support with g16 verifier deployment

* add commands for Sepolia

* add additional logging

* add trace span close events

* update usage guide

* update docs and cleanup

* gracefully handle http errors

* gracefully handle more errors, cap consecutive errors

* fmt
  • Loading branch information
austinabell authored Jul 11, 2024
1 parent 3c6d757 commit 10241fd
Show file tree
Hide file tree
Showing 11 changed files with 475 additions and 16 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"rust-analyzer.linkedProjects": [
"./light-client-guest/guest/Cargo.toml",
"./batch-guest/guest/Cargo.toml",
"./host/Cargo.toml"
"./host/Cargo.toml",
"./core/Cargo.toml",
"./service/Cargo.toml"
],
"rust-analyzer.check.extraEnv": {
"RISC0_SKIP_BUILD": "1",
Expand Down
18 changes: 18 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["core", "host"]
members = ["core", "host", "service"]
default-members = ["host"]

[workspace.dependencies]
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ cargo run -p host -- --help
```

> Note: This CLI as well as other APIs will change in the short term. If you need anything specific from this, [open an issue](https://github.com/risc0/blobstream0/issues/new)!
For more docs on running the Blobstream service, see [usage-guide.md](./usage-guide.md).
3 changes: 2 additions & 1 deletion host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
alloy = { version = "0.1", features = ["node-bindings", "network", "providers", "transports", "signer-local"] }
alloy-contract = "0.1"
alloy-sol-types = "0.7"
anyhow = "1.0"
batch-guest = { path = "../batch-guest" }
Expand All @@ -21,10 +22,10 @@ tendermint = { workspace = true }
tendermint-light-client-verifier = { workspace = true }
tendermint-rpc = { workspace = true, features = ["http-client"] }
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[dev-dependencies]
alloy-contract = "0.1"
serde_json = "1.0"
serde_with = { version = "3.8", features = ["base64"] }
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }
14 changes: 12 additions & 2 deletions host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ use risc0_tm_core::{
IBlobstream::{IBlobstreamInstance, RangeCommitment},
LightClientCommit,
};
use risc0_zkvm::{default_prover, is_dev_mode, sha::Digestible, ExecutorEnv, Prover, Receipt};
use risc0_zkvm::{
default_prover, is_dev_mode, sha::Digestible, ExecutorEnv, Prover, ProverOpts, Receipt,
};
use std::ops::Range;
use tendermint::{block::Height, node::Id, validator::Set};
use tendermint_light_client_verifier::types::LightBlock;
use tendermint_rpc::{Client, HttpClient, Paging};
use tracing::{instrument, Level};

async fn fetch_light_block(
client: &HttpClient,
Expand Down Expand Up @@ -60,6 +63,7 @@ pub struct LightBlockProof {
}

/// Prove a single block with the trusted light client block and the height to fetch and prove.
#[instrument(skip(prover, client, previous_block), err, level = Level::TRACE)]
pub async fn prove_block(
prover: &dyn Prover,
client: &HttpClient,
Expand Down Expand Up @@ -102,6 +106,7 @@ pub async fn prove_block(
}

/// Fetches and proves a range of light client blocks.
#[instrument(skip(client), err, level = Level::TRACE)]
pub async fn prove_block_range(client: &HttpClient, range: Range<u64>) -> anyhow::Result<Receipt> {
let prover = default_prover();

Expand All @@ -127,12 +132,16 @@ pub async fn prove_block_range(client: &HttpClient, range: Range<u64>) -> anyhow
let env = batch_env_builder.write(&batch_receipts)?.build()?;

// Note: must block in place to not have issues with Bonsai blocking client when selected
let prove_info = tokio::task::block_in_place(move || prover.prove(env, BATCH_GUEST_ELF))?;
tracing::debug!("Proving batch of blocks");
let prove_info = tokio::task::block_in_place(move || {
prover.prove_with_opts(env, BATCH_GUEST_ELF, &ProverOpts::groth16())
})?;

Ok(prove_info.receipt)
}

/// Post batch proof to Eth based chain.
#[instrument(skip(contract, receipt), err, level = Level::TRACE)]
pub async fn post_batch<T, P, N>(
contract: &IBlobstreamInstance<T, P, N>,
receipt: &Receipt,
Expand All @@ -142,6 +151,7 @@ where
P: Provider<T, N>,
N: Network,
{
tracing::info!("Posting batch (dev mode={})", is_dev_mode());
let seal = match is_dev_mode() {
true => [&[0u8; 4], receipt.claim()?.digest().as_bytes()].concat(),
false => groth16::encode(receipt.inner.groth16()?.seal.clone())?,
Expand Down
137 changes: 126 additions & 11 deletions host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,53 @@

use std::path::PathBuf;

use alloy::{
hex::FromHex,
network::EthereumWallet,
primitives::{hex, Address, FixedBytes},
providers::ProviderBuilder,
signers::local::PrivateKeySigner,
};
use alloy_sol_types::sol;
use clap::Parser;
use host::prove_block_range;
use risc0_tm_core::IBlobstream;
use tendermint_rpc::HttpClient;
use tokio::fs;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::format;
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};

// TODO elsewhere if keeping dev mode deploy through CLI
sol!(
#[sol(rpc)]
MockVerifier,
// TODO probably not ideal to reference build directory, fine for now.
"../contracts/out/RiscZeroMockVerifier.sol/RiscZeroMockVerifier.json"
);
sol!(
#[sol(rpc)]
RiscZeroGroth16Verifier,
// TODO probably not ideal to reference build directory, fine for now.
"../contracts/out/RiscZeroGroth16Verifier.sol/RiscZeroGroth16Verifier.json"
);

// Pulled from https://github.com/risc0/risc0-ethereum/blob/ebec385cc526adb9279c1af55d699c645ca6d694/contracts/src/groth16/ControlID.sol
const CONTROL_ID: [u8; 32] =
hex!("a516a057c9fbf5629106300934d48e0e775d4230e41e503347cad96fcbde7e2e");
const BN254_CONTROL_ID: [u8; 32] =
hex!("0eb6febcf06c5df079111be116f79bd8c7e85dc9448776ef9a59aaf2624ab551");

#[derive(Parser, Debug)]
#[command(name = "blobstream0-cli")]
#[command(bin_name = "blobstream0-cli")]
enum BlobstreamCli {
ProveRange(ProveRangeArgs),
Deploy(DeployArgs),
}

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
struct ProveRangeArgs {
/// The start height
#[clap(long)]
start: u64,
Expand All @@ -42,24 +80,101 @@ struct Args {
out: PathBuf,
}

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct DeployArgs {
/// The Ethereum RPC URL
#[clap(long)]
eth_rpc: String,

/// Hex encoded private key to use for submitting proofs to Ethereum
#[clap(long)]
private_key_hex: String,

/// Address of risc0 verifier to use (either mock or groth16)
#[clap(long)]
verifier_address: Option<String>,

/// Trusted height for contract
#[clap(long)]
tm_height: u64,

/// Trusted block hash for contract
#[clap(long)]
tm_block_hash: String,

/// If deploying verifier, will it deploy the mock verifier
#[clap(long)]
dev: bool,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.event_format(format().compact())
.with_span_events(FmtSpan::CLOSE)
.with_env_filter(EnvFilter::from_default_env())
.init();

let Args {
start,
end,
tendermint_rpc,
out,
} = Args::parse();
match BlobstreamCli::parse() {
BlobstreamCli::ProveRange(range) => {
let ProveRangeArgs {
start,
end,
tendermint_rpc,
out,
} = range;

let client = HttpClient::new(tendermint_rpc.as_str())?;

let receipt = prove_block_range(&client, start..end).await?;

fs::write(out, bincode::serialize(&receipt)?).await?;
}
BlobstreamCli::Deploy(deploy) => {
let signer: PrivateKeySigner = deploy.private_key_hex.parse()?;
let wallet = EthereumWallet::from(signer);

let client = HttpClient::new(tendermint_rpc.as_str())?;
let provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(wallet)
.on_http(deploy.eth_rpc.parse()?);
let verifier_address: Address = if let Some(address) = deploy.verifier_address {
address.parse()?
} else {
let deployed_address = if deploy.dev {
tracing::debug!("Deploying mock verifier");
MockVerifier::deploy(&provider, [0, 0, 0, 0].into())
.await?
.address()
.clone()
} else {
tracing::debug!("Deploying groth16 verifier");
RiscZeroGroth16Verifier::deploy(
&provider,
CONTROL_ID.into(),
BN254_CONTROL_ID.into(),
)
.await?
.address()
.clone()
};
println!("deployed verifier to address: {}", deployed_address);
deployed_address
};

let receipt = prove_block_range(&client, start..end).await?;
// Deploy the contract.
let contract = IBlobstream::deploy(
&provider,
verifier_address,
FixedBytes::<32>::from_hex(deploy.tm_block_hash)?,
deploy.tm_height,
)
.await?;

fs::write(out, bincode::serialize(&receipt)?).await?;
println!("deployed contract to address: {}", contract.address());
}
}

Ok(())
}
26 changes: 26 additions & 0 deletions service/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "service"
version = "0.1.0"
edition = "2021"

[dependencies]
alloy = { version = "0.1", features = ["node-bindings", "network", "providers", "transports", "signer-local"] }
alloy-sol-types = "0.7"
anyhow = "1.0"
# batch-guest = { path = "../batch-guest" }
bincode = "1.3.3"
# ciborium = { workspace = true }
clap = { version = "4.5", features = ["derive"] }
host = { path = "../host" }
# light-client-guest = { path = "../light-client-guest" }
# reqwest = "0.12.4"
# risc0-ethereum-contracts = { git = "https://github.com/risc0/risc0-ethereum", tag = "v1.0.0" }
risc0-tm-core = { path = "../core" }
# risc0-zkvm = { version = "1.0.1" }
# serde = { workspace = true }
# tendermint = { workspace = true }
# tendermint-light-client-verifier = { workspace = true }
tendermint-rpc = { workspace = true, features = ["http-client"] }
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
Loading

0 comments on commit 10241fd

Please sign in to comment.