This repository has been archived by the owner on Nov 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
6,426 additions
and
7,299 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
members = [ | ||
"programs/*", | ||
"packages/*", | ||
"operator/", | ||
] | ||
resolver = "2" | ||
|
||
|
@@ -14,8 +15,8 @@ keywords = ["cosmos", "ibc", "sp1", "tendermint", "ethereum", "bridge"] | |
authors = ["srdtrk <[email protected]>"] | ||
|
||
[workspace.dependencies] | ||
# Used by the programs | ||
sp1-zkvm = { git = "https://github.com/succinctlabs/sp1.git", tag = "v1.0.7-testnet" } | ||
sp1-sdk = { git = "https://github.com/succinctlabs/sp1.git", tag = "v1.0.7-testnet" } | ||
sp1-helper = { git = "https://github.com/succinctlabs/sp1.git", tag = "v1.0.7-testnet" } | ||
|
||
sp1-ics07-tendermint-shared = { path = "./packages/shared/" } | ||
|
@@ -32,6 +33,28 @@ serde = { version = "1.0", default-features = false } | |
sha2 = { version = "0.10.8", default-features = false } | ||
alloy-sol-types = "0.7.2" | ||
|
||
# Used by the operator | ||
sp1-sdk = { git = "https://github.com/succinctlabs/sp1.git", tag = "v1.0.7-testnet" } | ||
reqwest = { version = "0.11", features = ["json"] } | ||
tokio = { version = "1", features = ["full"] } | ||
serde_json = { version = "1.0", default-features = false, features = ["alloc"] } | ||
tendermint = { version = "0.36.0", default-features = false } | ||
# tendermint-light-client-verifier = { version = "0.35.0", default-features = false, features = [ | ||
# "rust-crypto", | ||
# ] } | ||
alloy-primitives = "0.7.2" | ||
bincode = "1.3.3" | ||
itertools = "0.12.1" | ||
serde_cbor = "0.11.2" | ||
dotenv = "0.15.0" | ||
subtle-encoding = "0.5.1" | ||
ethers = "2.0.14" | ||
anyhow = "1.0.82" | ||
clap = { version = "4.0", features = ["derive", "env"] } | ||
log = "0.4.21" | ||
async-trait = "0.1.80" | ||
hex = "0.4.3" | ||
|
||
[patch.crates-io] | ||
sha2-v0-10-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", branch = "patch-v0.10.8" } | ||
ed25519-consensus = { git = "https://github.com/sp1-patches/ed25519-consensus", branch = "patch-v2.1.0" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
[package] | ||
name = "sp1-ics07-tendermint-operator" | ||
version = { workspace = true } | ||
authors = { workspace = true } | ||
edition = { workspace = true } | ||
repository = { workspace = true } | ||
license = { workspace = true } | ||
|
||
[[bin]] | ||
name = "operator" | ||
path = "bin/operator.rs" | ||
|
||
[[bin]] | ||
name = "fixture" | ||
path = "bin/fixture.rs" | ||
|
||
[[bin]] | ||
name = "genesis" | ||
path = "bin/genesis.rs" | ||
|
||
[dependencies] | ||
sp1-sdk = { workspace = true } | ||
reqwest = { workspace = true } | ||
tokio = { workspace = true } | ||
serde_json = { workspace = true } | ||
serde = { workspace = true } | ||
tendermint = { workspace = true } | ||
tendermint-light-client-verifier = { workspace = true } | ||
alloy-sol-types = { workspace = true } | ||
alloy-primitives = { workspace = true } | ||
bincode = { workspace = true } | ||
itertools = { workspace = true } | ||
serde_cbor = { workspace = true } | ||
sha2 = { workspace = true } | ||
dotenv = { workspace = true } | ||
subtle-encoding = { workspace = true } | ||
ethers = { workspace = true } | ||
anyhow = { workspace = true } | ||
clap = { workspace = true } | ||
log = { workspace = true } | ||
async-trait = { workspace = true } | ||
hex = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use alloy_sol_types::{sol, SolType}; | ||
use clap::Parser; | ||
use serde::{Deserialize, Serialize}; | ||
use sp1_ics07_tendermint_operator::{util::TendermintRPCClient, TendermintProver}; | ||
use sp1_sdk::{utils::setup_logger, HashableKey}; | ||
use std::{env, path::PathBuf}; | ||
|
||
#[derive(Parser, Debug)] | ||
#[clap(author, version, about, long_about = None)] | ||
struct FixtureArgs { | ||
/// Trusted block. | ||
#[clap(long)] | ||
trusted_block: u64, | ||
|
||
/// Target block. | ||
#[clap(long, env)] | ||
target_block: u64, | ||
|
||
/// Fixture path. | ||
#[clap(long, default_value = "../contracts/fixtures")] | ||
fixture_path: String, | ||
} | ||
|
||
type TendermintProofOutput = sol! { | ||
tuple(uint64, uint64, bytes32, bytes32) | ||
}; | ||
|
||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct TendermintFixture { | ||
trusted_header_hash: String, | ||
target_header_hash: String, | ||
trusted_height: u64, | ||
target_height: u64, | ||
vkey: String, | ||
public_values: String, | ||
proof: String, | ||
} | ||
|
||
/// Writes the proof data for the given trusted and target blocks to the given fixture path. | ||
/// Example: | ||
/// ``` | ||
/// RUST_LOG=info cargo run --bin fixture --release -- --trusted-block=1 --target-block=5 | ||
/// ``` | ||
/// The fixture will be written to the path: ./contracts/fixtures/fixture.json | ||
#[tokio::main] | ||
async fn main() -> anyhow::Result<()> { | ||
dotenv::dotenv().ok(); | ||
setup_logger(); | ||
|
||
let args = FixtureArgs::parse(); | ||
|
||
let tendermint_rpc_client = TendermintRPCClient::default(); | ||
|
||
let (trusted_light_block, target_light_block) = tendermint_rpc_client | ||
.get_light_blocks(args.trusted_block, args.target_block) | ||
.await; | ||
|
||
let tendermint_prover = TendermintProver::new(); | ||
|
||
// Generate a header update proof for the specified blocks. | ||
let proof_data = | ||
tendermint_prover.generate_tendermint_proof(&trusted_light_block, &target_light_block); | ||
|
||
let bytes = proof_data.public_values.as_slice(); | ||
let (trusted_height, target_height, trusted_header_hash, target_header_hash) = | ||
TendermintProofOutput::abi_decode(bytes, false).unwrap(); | ||
|
||
let fixture = TendermintFixture { | ||
trusted_header_hash: hex::encode(trusted_header_hash), | ||
target_header_hash: hex::encode(target_header_hash), | ||
trusted_height, | ||
target_height, | ||
vkey: tendermint_prover.vkey.bytes32(), | ||
public_values: proof_data.public_values.bytes(), | ||
proof: proof_data.bytes(), | ||
}; | ||
|
||
// Save the proof data to the file path. | ||
let fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(args.fixture_path); | ||
|
||
// TODO: Change to prover.id | ||
let sp1_prover_type = env::var("SP1_PROVER"); | ||
if sp1_prover_type.as_deref() == Ok("mock") { | ||
std::fs::write( | ||
fixture_path.join("mock_fixture.json"), | ||
serde_json::to_string_pretty(&fixture).unwrap(), | ||
) | ||
.unwrap(); | ||
} else { | ||
std::fs::write( | ||
fixture_path.join("fixture.json"), | ||
serde_json::to_string_pretty(&fixture).unwrap(), | ||
) | ||
.unwrap(); | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use clap::Parser; | ||
use sp1_ics07_tendermint_operator::{util::TendermintRPCClient, TENDERMINT_ELF}; | ||
use sp1_sdk::{utils::setup_logger, HashableKey, MockProver, Prover}; | ||
|
||
#[derive(Parser, Debug)] | ||
#[clap(author, version, about, long_about = None)] | ||
struct GenesisArgs { | ||
/// Trusted block. | ||
#[clap(long)] | ||
trusted_block: Option<u64>, | ||
} | ||
|
||
/// Fetches the trusted header hash for the given block height. Defaults to the latest block height. | ||
/// Example: | ||
/// ``` | ||
/// RUST_LOG=info cargo run --bin genesis --release | ||
/// ``` | ||
/// | ||
#[tokio::main] | ||
async fn main() -> anyhow::Result<()> { | ||
dotenv::dotenv().ok(); | ||
setup_logger(); | ||
|
||
let args = GenesisArgs::parse(); | ||
|
||
// Generate the vkey hash to use in the contract. | ||
let prover = MockProver::new(); | ||
let (_, vk) = prover.setup(TENDERMINT_ELF); | ||
println!("TENDERMINT_VKEY_HASH={}", vk.bytes32()); | ||
|
||
let tendermint_client = TendermintRPCClient::default(); | ||
|
||
if let Some(trusted_block) = args.trusted_block { | ||
let commit = tendermint_client.get_commit(trusted_block).await?; | ||
println!("TRUSTED_HEIGHT={}", trusted_block); | ||
println!( | ||
"TRUSTED_HEADER_HASH={}", | ||
commit.result.signed_header.header.hash() | ||
); | ||
} else { | ||
let latest_commit = tendermint_client.get_latest_commit().await?; | ||
println!( | ||
"TRUSTED_HEIGHT={}", | ||
latest_commit.result.signed_header.header.height.value() | ||
); | ||
println!( | ||
"TRUSTED_HEADER_HASH={}", | ||
latest_commit.result.signed_header.header.hash() | ||
); | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
use alloy_primitives::U256; | ||
use alloy_sol_types::{sol, SolCall, SolValue}; | ||
use log::{debug, info}; | ||
use sp1_ics07_tendermint_operator::{ | ||
contract::ContractClient, util::TendermintRPCClient, TendermintProver, | ||
}; | ||
use sp1_sdk::utils::setup_logger; | ||
use std::time::Duration; | ||
|
||
sol! { | ||
contract SP1Tendermint { | ||
bytes32 public latestHeader; | ||
uint64 public latestHeight; | ||
|
||
function verifyTendermintProof( | ||
bytes calldata proof, | ||
bytes calldata publicValues | ||
) public; | ||
} | ||
} | ||
|
||
/// An implementation of a Tendermint Light Client operator that will poll an onchain Tendermint | ||
/// light client and generate a proof of the transition from the latest block in the contract to the | ||
/// latest block on the chain. Then, submits the proof to the contract and updates the contract with | ||
/// the latest block hash and height. | ||
#[tokio::main] | ||
async fn main() -> anyhow::Result<()> { | ||
dotenv::dotenv().ok(); | ||
setup_logger(); | ||
|
||
// Instantiate a contract client to interact with the deployed Solidity Tendermint contract. | ||
let contract_client = ContractClient::default(); | ||
|
||
// Instantiate a Tendermint prover based on the environment variable. | ||
let tendermint_rpc_client = TendermintRPCClient::default(); | ||
let prover = TendermintProver::new(); | ||
|
||
loop { | ||
// Read the existing trusted header hash from the contract. | ||
let contract_latest_height = SP1Tendermint::latestHeightCall {}.abi_encode(); | ||
let contract_latest_height = contract_client.read(contract_latest_height).await?; | ||
let contract_latest_height = U256::abi_decode(&contract_latest_height, true).unwrap(); | ||
let trusted_block_height: u64 = contract_latest_height.try_into().unwrap(); | ||
|
||
if trusted_block_height == 0 { | ||
panic!( | ||
"No trusted height found on the contract. Something is wrong with the contract." | ||
); | ||
} | ||
|
||
let chain_latest_block_height = tendermint_rpc_client.get_latest_block_height().await; | ||
let (trusted_light_block, target_light_block) = tendermint_rpc_client | ||
.get_light_blocks(trusted_block_height, chain_latest_block_height) | ||
.await; | ||
|
||
// Generate a proof of the transition from the trusted block to the target block. | ||
let proof_data = | ||
prover.generate_tendermint_proof(&trusted_light_block, &target_light_block); | ||
|
||
// Construct the on-chain call and relay the proof to the contract. | ||
let proof_as_bytes = hex::decode(&proof_data.proof.encoded_proof).unwrap(); | ||
let verify_tendermint_proof_call_data = SP1Tendermint::verifyTendermintProofCall { | ||
publicValues: proof_data.public_values.to_vec().into(), | ||
proof: proof_as_bytes.into(), | ||
} | ||
.abi_encode(); | ||
contract_client | ||
.send(verify_tendermint_proof_call_data) | ||
.await?; | ||
|
||
info!( | ||
"Updated the latest block of Tendermint light client at address {} from block {} to block {}.", | ||
contract_client.contract, trusted_block_height, chain_latest_block_height | ||
); | ||
|
||
// Sleep for 60 seconds. | ||
debug!("sleeping for 60 seconds"); | ||
tokio::time::sleep(Duration::from_secs(60)).await; | ||
} | ||
} |
Oops, something went wrong.