Skip to content

Blobstream service #13

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

Merged
merged 14 commits into from
Jul 11, 2024
Merged
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -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",
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]
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
@@ -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" }
@@ -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
@@ -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,
@@ -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,
@@ -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();

@@ -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,
@@ -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())?,
137 changes: 126 additions & 11 deletions host/src/main.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -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