Skip to content
This repository has been archived by the owner on Nov 29, 2024. It is now read-only.

Commit

Permalink
feat: large workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
srdtrk committed Jun 22, 2024
1 parent e627915 commit e9533af
Show file tree
Hide file tree
Showing 14 changed files with 6,426 additions and 7,299 deletions.
6,789 changes: 5,707 additions & 1,082 deletions Cargo.lock

Large diffs are not rendered by default.

25 changes: 24 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"programs/*",
"packages/*",
"operator/",
]
resolver = "2"

Expand All @@ -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/" }
Expand All @@ -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" }
42 changes: 42 additions & 0 deletions operator/Cargo.toml
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 }
99 changes: 99 additions & 0 deletions operator/bin/fixture.rs
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(())
}
53 changes: 53 additions & 0 deletions operator/bin/genesis.rs
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(())
}
80 changes: 80 additions & 0 deletions operator/bin/operator.rs
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;
}
}
Loading

0 comments on commit e9533af

Please sign in to comment.