Skip to content

Commit

Permalink
Merge pull request #20 from worldcoin/dcbuild3r/tree-availability-ser…
Browse files Browse the repository at this point in the history
…vice-docs

PRO-382: tree-availability-service and state-bridge-service-docs
  • Loading branch information
0xKitsune authored Oct 31, 2023
2 parents 3d184cc + efc0efe commit e12b208
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 400 deletions.
482 changes: 226 additions & 256 deletions Cargo.lock

Large diffs are not rendered by default.

38 changes: 18 additions & 20 deletions bin/state_bridge_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ use state_bridge::root::IWorldIDIdentityManager;
use state_bridge::StateBridgeService;
use tracing::info;

/// The state bridge service propagates roots according to the specified relaying_period by
/// calling the propagateRoot() method on each specified World ID StateBridge. The state bridge
/// service will also make sure that it doesn't propagate roots that have already been propagated
/// and have finalized on the BridgedWorldID side
// The state bridge service propagates roots from the world tree. Frequency of root propagation is specified
// by the relaying_period. This service will not propagate roots that have already been propagated before.
#[derive(Parser, Debug)]
#[clap(
name = "State Bridge Service",
Expand All @@ -30,7 +28,7 @@ struct Options {
config: PathBuf,
}

/// Converts a u64 into a Duration using Duration::from_secs
// Converts a u64 into a Duration using Duration::from_secs
mod duration_seconds {
use std::time::Duration;

Expand Down Expand Up @@ -60,29 +58,29 @@ struct BridgeConfig {
bridged_rpc_url: String,
}

/// The config TOML file defines all the necessary parameters to spawn a state bridge service.
/// rpc_url - HTTP rpc url for an Ethereum node (string)
/// private_key - pk to an address that will call the propagateRoot() method on the StateBridge contract (string)
/// world_id_address - WorldIDIdentityManager contract address (string)
/// bridge_pair_addresses - List of StateBridge and BridgedWorldID contract address pairs (strings)
/// bridged_world_id_addresses - List of BridgedWorldID contract addresses (strings)
/// relaying_period: propagateRoot() call period time in seconds (u64)
/// block_confirmations - Number of block confirmations required for the propagateRoot call on the StateBridge contract (optional number)
// The config TOML file defines all the necessary parameters to spawn a state bridge service.
// rpc_url - HTTP rpc url for an Ethereum node (string)
// private_key - pk to an address that will call the propagateRoot() method on the StateBridge contract (string)
// world_id_address - WorldIDIdentityManager contract address (string)
// bridge_pair_addresses - List of StateBridge and BridgedWorldID contract address pairs (strings)
// bridged_world_id_addresses - List of BridgedWorldID contract addresses (strings)
// relaying_period: propagateRoot() call period time in seconds (u64)
// block_confirmations - Number of block confirmations required for the propagateRoot call on the StateBridge contract (optional number)
#[derive(Deserialize, Serialize, Debug, Clone)]
struct Config {
/// RPC URL for the HTTP provider (World ID IdentityManager)
// RPC URL for the HTTP provider (World ID IdentityManager)
rpc_url: String,
/// Private key to use for the middleware signer
// Private key to use for the middleware signer
private_key: String,
/// `WorldIDIdentityManager` contract address
// `WorldIDIdentityManager` contract address
world_id_address: H160,
/// List of `StateBridge` and `BridgedWorldID` pair addresses
// List of `StateBridge` and `BridgedWorldID` pair addresses
bridge_configs: Vec<BridgeConfig>,
/// `propagateRoot()` call period time in seconds
// `propagateRoot()` call period time in seconds
#[serde(with = "duration_seconds")]
relaying_period_seconds: Duration,
/// Number of block confirmations required for the `propagateRoot` call on the `StateBridge`
/// contract
// Number of block confirmations required for the `propagateRoot` call on the `StateBridge`
// contract
#[serde(default = "default_block_confirmations")]
block_confirmations: usize,
}
Expand Down
5 changes: 4 additions & 1 deletion bin/tree_availability_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use futures::StreamExt;
use tree_availability::TreeAvailabilityService;

#[derive(Parser, Debug)]
#[clap(name = "Tree Availability Service", about = "")]
#[clap(
name = "Tree Availability Service",
about = "The tree availability service periodically calls propagateRoot() on a World ID StateBridge contract."
)]
struct Opts {
#[clap(long, help = "Depth of the World Tree")]
tree_depth: usize,
Expand Down
31 changes: 31 additions & 0 deletions crates/state_bridge/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,29 @@ abigen!(
event_derives(serde::Deserialize, serde::Serialize)
);

/// The `StateBridge` takes care of listening to roots propagated from the `WorldRoot` and
/// propagates them periodically every time `relaying_period` elapses and the root was updated.
pub struct StateBridge<M: Middleware + 'static> {
/// Interface for the `StateBridge` contract
pub state_bridge: IStateBridge<M>,
/// Interface for the `BridgedWorldID` contract
pub bridged_world_id: IBridgedWorldID<M>,
/// Time delay between `propagateRoot()` transactions
pub relaying_period: Duration,
/// The number of block confirmations before a `propagateRoot()` transaction is considered finalized
pub block_confirmations: usize,
}

impl<M: Middleware> StateBridge<M> {
/// Initializes a StateBridge
///
/// `state_bridge`: `StateBridge` contract interface
///
/// `bridged_world_id`: `BridgedWorldID` contract interface
///
/// `relaying_period`: Time in between `propagateRoot()` calls
///
/// `block_confirmations: The number of blocks before a `propagateRoot()` call is considered finalized
pub fn new(
state_bridge: IStateBridge<M>,
bridged_world_id: IBridgedWorldID<M>,
Expand All @@ -51,6 +66,19 @@ impl<M: Middleware> StateBridge<M> {
})
}

/// Initializes a StateBridge with address and middleware
///
/// `bridge_address`: `StateBridge` contract address
///
/// `canonical_middleware`: middleware for the chain where the `StateBridge` is deployed
///
/// `bridged_world_id_address`: `BridgedWorldID` contract address
///
/// `derived_middleware`: middleware for the chain where the `BridgedWorldID` is deployed
///
/// `relaying_period`: Time in between `propagateRoot()` calls
///
/// `block_confirmations: The number of block confirmations before a `propagateRoot()` transaction is considered finalized
pub fn new_from_parts(
bridge_address: H160,
canonical_middleware: Arc<M>,
Expand All @@ -75,6 +103,9 @@ impl<M: Middleware> StateBridge<M> {
})
}

/// Spawns the `StateBridge` which listens to the `WorldRoot` `TreeChanged` events for new roots to propagate.
///
/// `root_rx`: The root receiver that listens to the `WorldRoot` root sender
pub async fn spawn(
&self,
mut root_rx: tokio::sync::broadcast::Receiver<Hash>,
Expand Down
1 change: 0 additions & 1 deletion crates/state_bridge/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use thiserror::Error;

use crate::root::Hash;

/// Wraps dependency errors and converts them to `StateBridgeError` using `thiserror`
#[derive(Error, Debug)]
pub enum StateBridgeError<M>
where
Expand Down
98 changes: 10 additions & 88 deletions crates/state_bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,7 @@
//! ### Description
//!
//! The state bridge service for the World ID protocol takes care of periodically relaying the latest roots from the World ID Identity Manager onto L2 networks or sidechains that implement native bridge on Ethereum or have an integration with third party messaging protocol. The state bridge service requires a deployment of the [`world-id-state-bridge`](github.com/worldcoin/world-id-state-bridge/) contracts which in turn also have to be connected to a valid [`world-id-contracts`](https://github.com/worldcoin/world-id-contracts/) deployment.
//!
//! ### Usage
//!
//! #### CLI
//!
//! TODO: specify how to run the state bridge service as a command-line utility
//!
//! #### Library
//! In order to launch a `StateBridgeService` as a library you can use the following example from the [`bridge_service.rs`](https://github.com/worldcoin/identity-sequencer/blob/359f0fe3ec62b18d6f569d8ad31967c048401fa1/crates/state_bridge/tests/bridge_service.rs#L37) test file as a guide.
//! ```
//! use state_bridge::bridge::{IBridgedWorldID, IStateBridge, StateBridge};
//! use state_bridge::root::IWorldIDIdentityManager;
//! use state_bridge::StateBridgeService;
//! use common::test_utilities::chain_mock::{spawn_mock_chain, MockChain};
//! // If you deploy your own state bridge and run your own Ethereum RPC
//! // (or use a third party service like Alchemy)
//! // you can instantiate your own variables by providing the right addresses
//! // and a middleware (implements ethers::middleware::Middleware).
//! #[tokio::test]
//! async fn doc_example() -> eyre::Result<()> {
//! let MockChain {
//! mock_state_bridge,
//! mock_bridged_world_id,
//! mock_world_id,
//! middleware,
//! anvil,
//! ..
//! } = spawn_mock_chain().await?;
//!
//! let relaying_period = std::time::Duration::from_secs(5);
//!
//! let world_id = IWorldIDIdentityManager::new(
//! mock_world_id.address(),
//! middleware.clone(),
//! );
//!
//! mock_state_bridge.propagate_root().send().await?.await?;
//!
//! let state_bridge_address = mock_state_bridge.address();
//!
//! let bridged_world_id_address = mock_bridged_world_id.address();
//!
//! let block_confirmations = 6;
//!
//! let mut state_bridge_service = StateBridgeService::new(world_id)
//! .await
//! .expect("couldn't create StateBridgeService");
//!
//! let state_bridge =
//! IStateBridge::new(state_bridge_address, middleware.clone());
//!
//! let bridged_world_id =
//! IBridgedWorldID::new(bridged_world_id_address, middleware.clone());
//!
//! let state_bridge =
//! StateBridge::new(state_bridge, bridged_world_id, relaying_period, block_confirmations)
//! .unwrap();
//!
//! state_bridge_service.add_state_bridge(state_bridge);
//!
//! state_bridge_service
//! .spawn()
//! .await
//! .expect("failed to spawn a state bridge service");
//! }
//! ```
//!
pub mod bridge;
pub mod error;
pub mod root;
Expand Down Expand Up @@ -101,15 +35,9 @@ impl<M> StateBridgeService<M>
where
M: Middleware,
{
/// constructor for the `StateBridgeService`
///
/// ### Arguments
/// Initializes the `StateBridgeService`
///
/// `world_tree`:`IWorldID ` - interface to the `WorldIDIdentityManager`
///
/// ### Output
///
/// `Result<StateBridgeService, StateBridgeError<M>>`
pub async fn new(
world_tree: IWorldIDIdentityManager<M>,
) -> Result<Self, StateBridgeError<M>> {
Expand All @@ -120,16 +48,11 @@ where
})
}

/// constructor for the `StateBridgeService`
///
/// ### Arguments
/// Initializes the `StateBridgeService`
///
/// `world_tree_address`:`H160` - interface to the `WorldIDIdentityManager`
/// `middleware`:`Arc\<M\>` - Middleware provider
///
/// ### Output
///
/// `Result<StateBridgeService, StateBridgeError<M>>`
/// `middleware`:`Arc<M>` - Middleware provider
pub async fn new_from_parts(
world_tree_address: H160,
middleware: Arc<M>,
Expand All @@ -145,30 +68,29 @@ where
}

/// Adds a state bridge to the list of state bridges the service will use
/// to propagate roots on chain to their destination chains
///
/// ### Args
/// to propagate roots on chain to their destination chains.
///
/// `state_bridge`: `StateBridge<M>` - state bridge contract interface with provider
///
/// ### Notes
///
/// Needs to be called before the spawn function so that the `StateBridgeService`
/// knows where to propagate roots to
/// knows where to propagate roots to.
pub fn add_state_bridge(&mut self, state_bridge: StateBridge<M>) {
self.state_bridges.push(state_bridge);
}

/// Spawns the `StateBridgeService`
/// Spawns the `StateBridgeService`.
pub async fn spawn(&mut self) -> Result<(), StateBridgeError<M>> {
// if no state bridge initialized then there is no point in spawning
// the state bridge service as there'd be no receivers for new roots
// the state bridge service as there'd be no receivers for new roots.
if self.state_bridges.is_empty() {
return Err(StateBridgeError::BridgesNotInitialized);
}

// We first instantiate the receivers on the state bridges
// so that the root sender doesn't yield an error when pushing roots
// through the channel
// through the channel.
for bridge in self.state_bridges.iter() {
self.handles.push(
bridge.spawn(self.canonical_root.root_tx.subscribe()).await,
Expand Down
18 changes: 6 additions & 12 deletions crates/state_bridge/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,9 @@ impl<M> WorldTreeRoot<M>
where
M: Middleware,
{
/// `WorldTreeRoot` constructor
/// Initializes `WorldTreeRoot`
///
/// ### Args
/// `world_id_identity_manager`:`IWorldIDIdentityManager\<M\>` - `WorldIDIdentityManager` interface
///
/// ### Output
/// `WorldTreeRoot` Result
/// `world_id_identity_manager`:`IWorldIDIdentityManager<M>` - `WorldIDIdentityManager` interface
pub async fn new(
world_id_identity_manager: IWorldIDIdentityManager<M>,
) -> Result<Self, StateBridgeError<M>> {
Expand All @@ -55,15 +51,13 @@ where
})
}

/// `WorldTreeRoot` constructor from address and middleware
/// Initializes `WorldTreeRoot` from address and middleware
///
/// `world_id_identity_manager`:`IWorldIDIdentityManager<M>` - `WorldIDIdentityManager` interface
///
/// ### Args
/// `world_id_identity_manager`:`IWorldIDIdentityManager\<M\>` - `WorldIDIdentityManager` interface
/// `world_tree_address`: `H160` - `WorldIDIdentityManager` contract address
/// `middleware`: `Arc\<M\>` - Middleware provider (ethers)
///
/// ### Output
/// `WorldTreeRoot` Result
/// `middleware`: `Arc<M>` - Middleware provider (ethers)
pub async fn new_from_parts(
world_tree_address: H160,
middleware: Arc<M>,
Expand Down
34 changes: 34 additions & 0 deletions crates/tree_availability/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
//! # Tree Availability Service
//!
//! The tree availability service is able to create an in-memory representation of the World ID
//! merkle tree by syncing the state of the World ID contract `registerIdentities` and `deleteIdentities`
//! function calldata and `TreeChanged` events. Once it syncs the latest state of the state of the tree, it
//! is able to serve inclusion proofs on the `/inclusionProof` endpoint. It also keeps a historical roots list
//! of `tree_history_size` size in order to serve proofs against older tree roots (including the roots of
//! World IDs bridged to other networks).
pub mod error;
pub mod server;
pub mod world_tree;
Expand All @@ -14,11 +23,27 @@ use world_tree::{Hash, PoseidonTree, WorldTree};

use crate::server::{inclusion_proof, synced};

/// Service that keeps the World Tree synced with `WorldIDIdentityManager` and exposes an API endpoint to serve inclusion proofs for a given World ID.
pub struct TreeAvailabilityService<M: Middleware + 'static> {
/// In-memory representation of the merkle tree containing all verified World IDs.
pub world_tree: Arc<WorldTree<M>>,
}

impl<M: Middleware> TreeAvailabilityService<M> {
/// Initializes new instance of `TreeAvailabilityService`,
///
/// # Arguments
///
/// * `tree_depth` - Depth of the merkle tree
/// * `dense_prefix_depth`: Depth of the tree that is densely populated. Nodes beyond the `dense_prefix_depth` will be stored through pointer based structures.
/// * `tree_history_size`: Number of historical roots to store in memory. This is used to serve proofs against historical roots.
/// * `world_tree_address`: Address of the `WorldIDIdentityManager` contract onchain
/// * `world_tree_creation_block`: Block number where `WorldIDIdentityManager` was deployed
/// * `middleware`: Provider to interact with Ethereum
///
/// # Returns
///
/// New instance of `TreeAvailabilityService`.
pub fn new(
tree_depth: usize,
dense_prefix_depth: usize,
Expand All @@ -44,6 +69,15 @@ impl<M: Middleware> TreeAvailabilityService<M> {
Self { world_tree }
}

/// Spawns an axum server and exposes an API endpoint to serve inclusion proofs for a given World ID. This function also spawns a new task to keep the world tree synced to the chain head.
///
/// # Arguments
///
/// * `port` - Port to bind the server to.
///
/// # Returns
///
/// Vector of `JoinHandle`s for the spawned tasks.
pub async fn serve(
self,
port: u16,
Expand Down
Loading

0 comments on commit e12b208

Please sign in to comment.