Skip to content
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

PRO-382: tree-availability-service and state-bridge-service-docs #20

Merged
merged 11 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
482 changes: 226 additions & 256 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bin/state_bridge_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
/// and have finalized on the BridgedWorldID side.
#[derive(Parser, Debug)]
#[clap(
name = "State Bridge Service",
Expand Down
1 change: 1 addition & 0 deletions bin/tree_availability_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use futures::stream::FuturesUnordered;
use futures::StreamExt;
use tree_availability::TreeAvailabilityService;

/// CLI interface for the Tree Availability Service
#[derive(Parser, Debug)]
#[clap(name = "Tree Availability Service", about = "")]
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
struct Opts {
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.
Comment on lines +32 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 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.
/// Listens to new roots received by a `WorldRoot`, sending a `propagateRoot()` transaction
///
/// to bridge the root to a target chain.

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 in between `propagateRoot()` calls
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
pub relaying_period: Duration,
/// The number of blocks before a `propagateRoot()` call is considered finalized
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
pub block_confirmations: usize,
}

impl<M: Middleware> StateBridge<M> {
/// Constructor
///
/// `state_bridge`: `StateBridge` contract interface
///
/// `bridged_world_id`: `BridgedWorldID` contract interface
///
/// `relaying_period`: Time in between `propagateRoot()` calls
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// `relaying_period`: Time in between `propagateRoot()` calls
/// `relaying_period`: Time delay between `propagateRoot()` transaction

///
/// `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> {
})
}

/// Constructor 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// `relaying_period`: Time in between `propagateRoot()` calls
/// `relaying_period`: Time delay between `propagateRoot()` transactions

///
/// `block_confirmations: The number of blocks before a `propagateRoot()` call 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
Comment on lines +106 to +108
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 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
/// Spawns a task to listen for new roots from a broadcast channel,
/// sending a `propagateRoot()` transaction after the `relaying_period` has elapsed.
///
/// `root_rx`: Receiver to handle new roots sent by a `WorldRoot`

pub async fn spawn(
&self,
mut root_rx: tokio::sync::broadcast::Receiver<Hash>,
Expand Down
55 changes: 33 additions & 22 deletions crates/state_bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,30 @@
//!
//! #### CLI
//!
//! TODO: specify how to run the state bridge service as a command-line utility
//! Create a state_bridge_service.toml file which will hold the configuration parameters for the state bridge
//! service. You can use the example in the test as a template:
//!
//! ```toml
//! rpc_url = "127.0.0.1:8545"
//! private_key = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
//! world_id_address = "0x3f0BF744bb79A0b919f7DED73724ec20c43572B9"
//! bridge_configs = [
//! [
//! "Optimism",
//! # StateBridge Address
//! "0x3f0BF744bb79A0b919f7DED73724ec20c43572B9",
//! # BridgedWorldID Address
//! "0x4f0BF744bb79A0b919f7DED73724ec20c43572B9",
//! "127.0.0.1:8545",
//! ]
//! ]
//! relaying_period_seconds = 5
//! ```
//!
//! ```bash
//! cargo build --bin state-bridge-service --release
//! ./target/release/state-bridge-service --config <CONFIG>
//! ```
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
//!
//! #### 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.
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -101,15 +124,9 @@ impl<M> StateBridgeService<M>
where
M: Middleware,
{
/// constructor for the `StateBridgeService`
///
/// ### Arguments
/// ### Constructor for 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 +137,11 @@ where
})
}

/// constructor for the `StateBridgeService`
///
/// ### Arguments
/// Constructor for 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 +157,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
14 changes: 4 additions & 10 deletions crates/state_bridge/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ where
{
/// `WorldTreeRoot` constructor
///
/// ### 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 @@ -57,13 +53,11 @@ where

/// `WorldTreeRoot` constructor from address and middleware
///
/// ### Args
/// `world_id_identity_manager`:`IWorldIDIdentityManager\<M\>` - `WorldIDIdentityManager` interface
/// `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
1 change: 1 addition & 0 deletions crates/tree_availability/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ethers::types::Log;
use thiserror::Error;
use tokio::sync::mpsc::error::SendError;

/// Wraps dependency errors and converts them to `TreeAvailabilityError`s
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Error, Debug)]
pub enum TreeAvailabilityError<M>
where
Expand Down
155 changes: 155 additions & 0 deletions crates/tree_availability/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,147 @@
//! # 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).
//!
//! ### Usage
//!
//! #### CLI
//!
//! In order to run the tree availability service you can run the following commands
//!
//! ```bash
//! # Build the binary
//! cargo build --bin tree-availability-service --release
//! # Run the command
//! ./target/release/tree-availability-service
//! --tree-depth <TREE_DEPTH>
//! --tree-history-size <TREE_HISTORY_SIZE>
//! --dense-prefix-depth <DENSE_PREFIX_DEPTH>
//! --address <ADDRESS>
//! --creation-block <CREATION_BLOCK>
//! --rpc-endpoint <RPC_ENDPOINT>
//! ```
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
//!
//! #### Library
//!
//! A good example of how to use the `StateBridgeService` struct in a library is demonstrated in the [`inclusion_proof.rs`](https://github.com/worldcoin/identity-sequencer/blob/main/crates/tree_availability/tests/inclusion_proof.rs) test file.
//!
//! ```
//! use std::str::FromStr;
//!
//! use common::test_utilities::chain_mock::{spawn_mock_chain, MockChain};
//! use ethers::providers::Middleware;
//! use ethers::types::U256;
//! use futures::stream::FuturesUnordered;
//! use futures::StreamExt;
//! use hyper::StatusCode;
//! use tree_availability::error::TreeAvailabilityError;
//! use tree_availability::server::{InclusionProof, InclusionProofRequest};
//! use tree_availability::world_tree::tree_updater::pack_indices;
//! use tree_availability::world_tree::Hash;
//! use tree_availability::TreeAvailabilityService;
//! #[tokio::test]
//! async fn test_inclusion_proof() -> eyre::Result<()> {
//! // Initialize a new mock tree
//! let MockChain {
//! anvil: _anvil,
//! middleware,
//! mock_world_id,
//! ..
//! } = spawn_mock_chain().await?;
//!
//! // Register identities
//! let identity_commitments =
//! vec![U256::from(1), U256::from(2), U256::from(3)];
//!
//! let world_tree_creation_block =
//! middleware.get_block_number().await?.as_u64() - 1;
//!
//! mock_world_id
//! .register_identities(
//! [U256::zero(); 8],
//! U256::zero(),
//! 0,
//! identity_commitments,
//! U256::zero(),
//! )
//! .send()
//! .await?
//! .await?;
//!
//! // Delete an identity
//! mock_world_id
//! .delete_identities(
//! [U256::zero(); 8],
//! pack_indices(&[1]).into(),
//! U256::zero(),
//! U256::zero(),
//! )
//! .send()
//! .await?
//! .await?;
//!
//! // Initialize the tree availability service
//! let world_tree_address = mock_world_id.address();
//! let tree_availability_service = TreeAvailabilityService::new(
//! 3,
//! 1,
//! 5,
//! world_tree_address,
//! world_tree_creation_block,
//! middleware,
//! );
//!
//! let world_tree = tree_availability_service.world_tree.clone();
//!
//! // Spawn the service in a separate task
//! let server_handle = tokio::spawn(async move {
//! let handles = tree_availability_service.serve(8080).await;
//!
//! let mut handles = handles.into_iter().collect::<FuturesUnordered<_>>();
//! while let Some(result) = handles.next().await {
//! result.expect("TODO: propagate this error")?;
//! }
//!
//! Ok::<(), TreeAvailabilityError<_>>(())
//! });
//!
//! // Wait for the tree to be synced
//! loop {
//! if world_tree
//! .tree_updater
//! .synced
//! .load(std::sync::atomic::Ordering::Relaxed)
//! {
//! break;
//! }
//!
//! tokio::time::sleep(std::time::Duration::from_secs(1)).await;
//! }
//!
//! // Send a request to get an inclusion proof
//! let client = reqwest::Client::new();
//! let response = client
//! .post("http://127.0.0.1:8080/inclusionProof")
//! .json(&InclusionProofRequest {
//! identity_commitment: Hash::from(0x01),
//! root: None,
//! })
//! .send()
//! .await?;
//!
//! assert_eq!(response.status(), StatusCode::OK);
//!
//! // Return an inclusion proof
//! let proof: Option<InclusionProof> = response.json().await?;
//! assert!(proof.is_some());
//! }
//! ```
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved

pub mod error;
pub mod server;
pub mod world_tree;
Expand All @@ -14,11 +158,20 @@ use world_tree::{Hash, PoseidonTree, WorldTree};

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

/// The tree availability service data structure
pub struct TreeAvailabilityService<M: Middleware + 'static> {
/// The World ID merkle tree synced from the `WorldIDIdentityManager` contract
pub world_tree: Arc<WorldTree<M>>,
}

impl<M: Middleware> TreeAvailabilityService<M> {
/// Constructor
dcbuild3r marked this conversation as resolved.
Show resolved Hide resolved
/// `tree_depth`: the depth of the World ID contract (currently 30 in production - Nov 2023)
/// `dense_prefix_depth`: what is the depth of the tree that is densely populated (currently depth 10)
/// `tree_history_size`: how many historical roots and derived trees to hold in memory to serve proofs for
/// `world_tree_address`: `WorldIDIdentityManager` contract address
/// `world_tree_creation_block`: block at which `WorldIDIdentityManager` contract was deployed
/// `middleware`: Ethereum provider
pub fn new(
tree_depth: usize,
dense_prefix_depth: usize,
Expand All @@ -44,6 +197,8 @@ impl<M: Middleware> TreeAvailabilityService<M> {
Self { world_tree }
}

/// Spins up the /inclusionProof endpoint to serve proofs of inclusion for the World ID tree
/// `port`: which port on the machine will serve HTTP requests
pub async fn serve(
self,
port: u16,
Expand Down
Loading
Loading