Skip to content

Commit 10241fd

Browse files
authored
Blobstream service (#13)
* 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
1 parent 3c6d757 commit 10241fd

File tree

11 files changed

+475
-16
lines changed

11 files changed

+475
-16
lines changed

.vscode/settings.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"rust-analyzer.linkedProjects": [
33
"./light-client-guest/guest/Cargo.toml",
44
"./batch-guest/guest/Cargo.toml",
5-
"./host/Cargo.toml"
5+
"./host/Cargo.toml",
6+
"./core/Cargo.toml",
7+
"./service/Cargo.toml"
68
],
79
"rust-analyzer.check.extraEnv": {
810
"RISC0_SKIP_BUILD": "1",

Cargo.lock

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
resolver = "2"
3-
members = ["core", "host"]
3+
members = ["core", "host", "service"]
44
default-members = ["host"]
55

66
[workspace.dependencies]

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ cargo run -p host -- --help
3838
```
3939

4040
> 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)!
41+
42+
For more docs on running the Blobstream service, see [usage-guide.md](./usage-guide.md).

host/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
alloy = { version = "0.1", features = ["node-bindings", "network", "providers", "transports", "signer-local"] }
8+
alloy-contract = "0.1"
89
alloy-sol-types = "0.7"
910
anyhow = "1.0"
1011
batch-guest = { path = "../batch-guest" }
@@ -21,10 +22,10 @@ tendermint = { workspace = true }
2122
tendermint-light-client-verifier = { workspace = true }
2223
tendermint-rpc = { workspace = true, features = ["http-client"] }
2324
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }
25+
tracing = "0.1.40"
2426
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
2527

2628
[dev-dependencies]
27-
alloy-contract = "0.1"
2829
serde_json = "1.0"
2930
serde_with = { version = "3.8", features = ["base64"] }
3031
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }

host/src/lib.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ use risc0_tm_core::{
2121
IBlobstream::{IBlobstreamInstance, RangeCommitment},
2222
LightClientCommit,
2323
};
24-
use risc0_zkvm::{default_prover, is_dev_mode, sha::Digestible, ExecutorEnv, Prover, Receipt};
24+
use risc0_zkvm::{
25+
default_prover, is_dev_mode, sha::Digestible, ExecutorEnv, Prover, ProverOpts, Receipt,
26+
};
2527
use std::ops::Range;
2628
use tendermint::{block::Height, node::Id, validator::Set};
2729
use tendermint_light_client_verifier::types::LightBlock;
2830
use tendermint_rpc::{Client, HttpClient, Paging};
31+
use tracing::{instrument, Level};
2932

3033
async fn fetch_light_block(
3134
client: &HttpClient,
@@ -60,6 +63,7 @@ pub struct LightBlockProof {
6063
}
6164

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

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

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

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

132140
Ok(prove_info.receipt)
133141
}
134142

135143
/// Post batch proof to Eth based chain.
144+
#[instrument(skip(contract, receipt), err, level = Level::TRACE)]
136145
pub async fn post_batch<T, P, N>(
137146
contract: &IBlobstreamInstance<T, P, N>,
138147
receipt: &Receipt,
@@ -142,6 +151,7 @@ where
142151
P: Provider<T, N>,
143152
N: Network,
144153
{
154+
tracing::info!("Posting batch (dev mode={})", is_dev_mode());
145155
let seal = match is_dev_mode() {
146156
true => [&[0u8; 4], receipt.claim()?.digest().as_bytes()].concat(),
147157
false => groth16::encode(receipt.inner.groth16()?.seal.clone())?,

host/src/main.rs

+126-11
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,53 @@
1616

1717
use std::path::PathBuf;
1818

19+
use alloy::{
20+
hex::FromHex,
21+
network::EthereumWallet,
22+
primitives::{hex, Address, FixedBytes},
23+
providers::ProviderBuilder,
24+
signers::local::PrivateKeySigner,
25+
};
26+
use alloy_sol_types::sol;
1927
use clap::Parser;
2028
use host::prove_block_range;
29+
use risc0_tm_core::IBlobstream;
2130
use tendermint_rpc::HttpClient;
2231
use tokio::fs;
23-
use tracing_subscriber::EnvFilter;
32+
use tracing_subscriber::fmt::format;
33+
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};
34+
35+
// TODO elsewhere if keeping dev mode deploy through CLI
36+
sol!(
37+
#[sol(rpc)]
38+
MockVerifier,
39+
// TODO probably not ideal to reference build directory, fine for now.
40+
"../contracts/out/RiscZeroMockVerifier.sol/RiscZeroMockVerifier.json"
41+
);
42+
sol!(
43+
#[sol(rpc)]
44+
RiscZeroGroth16Verifier,
45+
// TODO probably not ideal to reference build directory, fine for now.
46+
"../contracts/out/RiscZeroGroth16Verifier.sol/RiscZeroGroth16Verifier.json"
47+
);
48+
49+
// Pulled from https://github.com/risc0/risc0-ethereum/blob/ebec385cc526adb9279c1af55d699c645ca6d694/contracts/src/groth16/ControlID.sol
50+
const CONTROL_ID: [u8; 32] =
51+
hex!("a516a057c9fbf5629106300934d48e0e775d4230e41e503347cad96fcbde7e2e");
52+
const BN254_CONTROL_ID: [u8; 32] =
53+
hex!("0eb6febcf06c5df079111be116f79bd8c7e85dc9448776ef9a59aaf2624ab551");
54+
55+
#[derive(Parser, Debug)]
56+
#[command(name = "blobstream0-cli")]
57+
#[command(bin_name = "blobstream0-cli")]
58+
enum BlobstreamCli {
59+
ProveRange(ProveRangeArgs),
60+
Deploy(DeployArgs),
61+
}
2462

2563
#[derive(Parser, Debug)]
2664
#[clap(author, version, about, long_about = None)]
27-
struct Args {
65+
struct ProveRangeArgs {
2866
/// The start height
2967
#[clap(long)]
3068
start: u64,
@@ -42,24 +80,101 @@ struct Args {
4280
out: PathBuf,
4381
}
4482

83+
#[derive(Parser, Debug)]
84+
#[clap(author, version, about, long_about = None)]
85+
struct DeployArgs {
86+
/// The Ethereum RPC URL
87+
#[clap(long)]
88+
eth_rpc: String,
89+
90+
/// Hex encoded private key to use for submitting proofs to Ethereum
91+
#[clap(long)]
92+
private_key_hex: String,
93+
94+
/// Address of risc0 verifier to use (either mock or groth16)
95+
#[clap(long)]
96+
verifier_address: Option<String>,
97+
98+
/// Trusted height for contract
99+
#[clap(long)]
100+
tm_height: u64,
101+
102+
/// Trusted block hash for contract
103+
#[clap(long)]
104+
tm_block_hash: String,
105+
106+
/// If deploying verifier, will it deploy the mock verifier
107+
#[clap(long)]
108+
dev: bool,
109+
}
110+
45111
#[tokio::main]
46112
async fn main() -> anyhow::Result<()> {
47113
tracing_subscriber::fmt()
114+
.event_format(format().compact())
115+
.with_span_events(FmtSpan::CLOSE)
48116
.with_env_filter(EnvFilter::from_default_env())
49117
.init();
50118

51-
let Args {
52-
start,
53-
end,
54-
tendermint_rpc,
55-
out,
56-
} = Args::parse();
119+
match BlobstreamCli::parse() {
120+
BlobstreamCli::ProveRange(range) => {
121+
let ProveRangeArgs {
122+
start,
123+
end,
124+
tendermint_rpc,
125+
out,
126+
} = range;
127+
128+
let client = HttpClient::new(tendermint_rpc.as_str())?;
129+
130+
let receipt = prove_block_range(&client, start..end).await?;
131+
132+
fs::write(out, bincode::serialize(&receipt)?).await?;
133+
}
134+
BlobstreamCli::Deploy(deploy) => {
135+
let signer: PrivateKeySigner = deploy.private_key_hex.parse()?;
136+
let wallet = EthereumWallet::from(signer);
57137

58-
let client = HttpClient::new(tendermint_rpc.as_str())?;
138+
let provider = ProviderBuilder::new()
139+
.with_recommended_fillers()
140+
.wallet(wallet)
141+
.on_http(deploy.eth_rpc.parse()?);
142+
let verifier_address: Address = if let Some(address) = deploy.verifier_address {
143+
address.parse()?
144+
} else {
145+
let deployed_address = if deploy.dev {
146+
tracing::debug!("Deploying mock verifier");
147+
MockVerifier::deploy(&provider, [0, 0, 0, 0].into())
148+
.await?
149+
.address()
150+
.clone()
151+
} else {
152+
tracing::debug!("Deploying groth16 verifier");
153+
RiscZeroGroth16Verifier::deploy(
154+
&provider,
155+
CONTROL_ID.into(),
156+
BN254_CONTROL_ID.into(),
157+
)
158+
.await?
159+
.address()
160+
.clone()
161+
};
162+
println!("deployed verifier to address: {}", deployed_address);
163+
deployed_address
164+
};
59165

60-
let receipt = prove_block_range(&client, start..end).await?;
166+
// Deploy the contract.
167+
let contract = IBlobstream::deploy(
168+
&provider,
169+
verifier_address,
170+
FixedBytes::<32>::from_hex(deploy.tm_block_hash)?,
171+
deploy.tm_height,
172+
)
173+
.await?;
61174

62-
fs::write(out, bincode::serialize(&receipt)?).await?;
175+
println!("deployed contract to address: {}", contract.address());
176+
}
177+
}
63178

64179
Ok(())
65180
}

service/Cargo.toml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "service"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
alloy = { version = "0.1", features = ["node-bindings", "network", "providers", "transports", "signer-local"] }
8+
alloy-sol-types = "0.7"
9+
anyhow = "1.0"
10+
# batch-guest = { path = "../batch-guest" }
11+
bincode = "1.3.3"
12+
# ciborium = { workspace = true }
13+
clap = { version = "4.5", features = ["derive"] }
14+
host = { path = "../host" }
15+
# light-client-guest = { path = "../light-client-guest" }
16+
# reqwest = "0.12.4"
17+
# risc0-ethereum-contracts = { git = "https://github.com/risc0/risc0-ethereum", tag = "v1.0.0" }
18+
risc0-tm-core = { path = "../core" }
19+
# risc0-zkvm = { version = "1.0.1" }
20+
# serde = { workspace = true }
21+
# tendermint = { workspace = true }
22+
# tendermint-light-client-verifier = { workspace = true }
23+
tendermint-rpc = { workspace = true, features = ["http-client"] }
24+
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }
25+
tracing = "0.1.40"
26+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

0 commit comments

Comments
 (0)