Skip to content

Commit

Permalink
WIP: Compile helios in a canister (#1)
Browse files Browse the repository at this point in the history
* Use stable Rust instead

* Vendor `ic-http` from `bitcoin-canister`

* config: Replace `reqwest` with `ic-http`

* Replace most of `ethers` with `ethers-core`

* consensus: replace `reqwest` with `ic-http`

* client: replace `ethers` with `ethers-core`

* client: Replace `gloo-timers` and `wasm-bindgen-futures` with `ic-cdk`

* Remove tokio::task::spawn_blocking

* remove ic-http/example_canister

* use ic-cdk instead of wasm-timer
  • Loading branch information
oblique authored Jul 24, 2023
1 parent ff80048 commit e42c2cd
Show file tree
Hide file tree
Showing 43 changed files with 1,762 additions and 966 deletions.
1,263 changes: 440 additions & 823 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dotenv = "0.15.0"
tokio = { version = "1", features = ["full"] }
eyre = "0.6.8"
dirs = "4.0.0"
ethers = { version = "2.0.2", features = [ "abigen" ] }
ethers = { version = "2.0.8", default-features = false }
env_logger = "0.9.0"
log = "0.4.17"
tracing-test = "0.2.4"
Expand Down
3 changes: 0 additions & 3 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
cargo-features = ["different-binary-name"]

[package]
name = "cli"
version = "0.4.1"
edition = "2021"

[[bin]]
name = "cli"
filename = "helios"
path = "src/main.rs"

[dependencies]
Expand Down
7 changes: 3 additions & 4 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = "2.0.2"
ethers-core = { version = "2.0.8", default-features = false }
futures = "0.3.23"
log = "0.4.17"
thiserror = "1.0.37"
Expand All @@ -23,7 +23,6 @@ jsonrpsee = { version = "0.15.1", features = ["full"] }
tokio = { version = "1", features = ["full"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
gloo-timers = "0.2.6"
wasm-bindgen-futures = "0.4.33"
ic-cdk-timers = "0.4.0"
ic-cdk = "0.10.0"
tokio = { version = "1", features = ["sync"] }

21 changes: 12 additions & 9 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use std::sync::Arc;

use config::networks::Network;
use consensus::errors::ConsensusError;
use ethers::prelude::{Address, U256};
use ethers::types::{
FeeHistory, Filter, Log, SyncingStatus, Transaction, TransactionReceipt, H256,
use ethers_core::types::{
Address, FeeHistory, Filter, Log, SyncingStatus, Transaction, TransactionReceipt, H256, U256,
};
use eyre::{eyre, Result};

Expand All @@ -24,9 +23,9 @@ use tokio::spawn;
use tokio::time::sleep;

#[cfg(target_arch = "wasm32")]
use gloo_timers::callback::Interval;
use ic_cdk::spawn;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::spawn_local;
use ic_cdk_timers::set_timer_interval;

use crate::database::Database;
use crate::errors::NodeError;
Expand Down Expand Up @@ -249,6 +248,8 @@ impl<DB: Database> Client<DB> {
let config = Arc::new(config);
let node = Node::new(config.clone())?;
let node = Arc::new(RwLock::new(node));

#[cfg(not(target_arch = "wasm32"))]
let mut rpc: Option<Rpc> = None;

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -331,17 +332,19 @@ impl<DB: Database> Client<DB> {

#[cfg(target_arch = "wasm32")]
fn start_advance_thread(&self) {
use std::time::Duration;

let node = self.node.clone();
Interval::new(12000, move || {

set_timer_interval(Duration::from_secs(12), move || {
let node = node.clone();
spawn_local(async move {
spawn(async move {
let res = node.write().await.advance().await;
if let Err(err) = res {
warn!("consensus error: {}", err);
}
});
})
.forget();
});
}

async fn boot_from_fallback(&self) -> eyre::Result<()> {
Expand Down
6 changes: 3 additions & 3 deletions client/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::Duration;

use ethers::prelude::{Address, U256};
use ethers::types::{
FeeHistory, Filter, Log, SyncProgress, SyncingStatus, Transaction, TransactionReceipt, H256,
use ethers_core::types::{
Address, FeeHistory, Filter, Log, SyncProgress, SyncingStatus, Transaction, TransactionReceipt,
H256, U256,
};
use eyre::{eyre, Result};

Expand Down
2 changes: 1 addition & 1 deletion client/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ethers::{
use ethers_core::{
abi::AbiEncode,
types::{Address, Filter, Log, SyncingStatus, Transaction, TransactionReceipt, H256, U256},
};
Expand Down
4 changes: 3 additions & 1 deletion common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = "2.0.2"
ethers-core = "2.0.8"
thiserror = "1.0.37"
ic-cdk = "0.10.0"
ic-http = { path = "../ic-http" }
15 changes: 14 additions & 1 deletion common/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ethers::types::H256;
use ethers_core::types::H256;
use ic_cdk::api::call::RejectionCode;
use thiserror::Error;

use crate::types::BlockTag;
Expand Down Expand Up @@ -42,3 +43,15 @@ impl<E: ToString> RpcError<E> {
}
}
}

#[derive(Debug, Error)]
pub enum HttpError {
#[error("canister call error: rejection code: {0:?}, message: {1}")]
CanisterCall(RejectionCode, String),
}

impl From<(RejectionCode, String)> for HttpError {
fn from(value: (RejectionCode, String)) -> Self {
HttpError::CanisterCall(value.0, value.1)
}
}
9 changes: 9 additions & 0 deletions common/src/http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::errors::HttpError;
use ic_cdk::api::management_canister::http_request::HttpResponse;

pub async fn http_get(url: &str) -> Result<HttpResponse, HttpError> {
let req = ic_http::create_request().get(url).build();
// TODO: should we pass cycles in http_get method or we should have a default one?
let resp = ic_http::http_request(req, 2_603_101_200).await?;
Ok(resp.0)
}
1 change: 1 addition & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod errors;
pub mod http;
pub mod types;
pub mod utils;
2 changes: 1 addition & 1 deletion common/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ethers::prelude::Address;
use ethers_core::types::Address;
use eyre::Result;
use ssz_rs::{Node, Vector};

Expand Down
7 changes: 3 additions & 4 deletions config/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

[package]
name = "config"
version = "0.4.1"
Expand All @@ -9,13 +8,13 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = "2.0.2"
ethers-core = { version = "2.0.8", default-features = false }
figment = { version = "0.10.7", features = ["toml", "env"] }
thiserror = "1.0.37"
log = "0.4.17"
reqwest = "0.11.13"
serde_yaml = "0.9.14"
strum = "0.24.1"
serde_json = "1.0.103"
strum = { version = "0.24.1", features = ["derive"] }
futures = "0.3.23"

common = { path = "../common" }
Expand Down
18 changes: 8 additions & 10 deletions config/src/checkpoints.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;

use ethers::types::H256;
use common::http::http_get;
use ethers_core::types::H256;
use serde::{Deserialize, Serialize};

use crate::networks;
Expand Down Expand Up @@ -87,9 +88,8 @@ impl CheckpointFallback {
/// The list is defined in [ethPandaOps/checkpoint-fallback-service](https://github.com/ethpandaops/checkpoint-sync-health-checks/blob/master/_data/endpoints.yaml).
pub async fn build(mut self) -> eyre::Result<Self> {
// Fetch the services
let client = reqwest::Client::new();
let res = client.get(CHECKPOINT_SYNC_SERVICES_LIST).send().await?;
let yaml = res.text().await?;
let resp = http_get(CHECKPOINT_SYNC_SERVICES_LIST).await?;
let yaml = String::from_utf8(resp.body)?;

// Parse the yaml content results.
let list: serde_yaml::Value = serde_yaml::from_str(&yaml)?;
Expand Down Expand Up @@ -122,10 +122,9 @@ impl CheckpointFallback {
}

async fn query_service(endpoint: &str) -> Option<RawSlotResponse> {
let client = reqwest::Client::new();
let constructed_url = Self::construct_url(endpoint);
let res = client.get(&constructed_url).send().await.ok()?;
let raw: RawSlotResponse = res.json().await.ok()?;
let resp = http_get(&constructed_url).await.ok()?;
let raw: RawSlotResponse = serde_json::from_slice(&resp.body).ok()?;
Some(raw)
}

Expand Down Expand Up @@ -199,10 +198,9 @@ impl CheckpointFallback {
/// service api url.
pub async fn fetch_checkpoint_from_api(url: &str) -> eyre::Result<H256> {
// Fetch the url
let client = reqwest::Client::new();
let constructed_url = Self::construct_url(url);
let res = client.get(constructed_url).send().await?;
let raw: RawSlotResponse = res.json().await?;
let resp = http_get(&constructed_url).await?;
let raw: RawSlotResponse = serde_json::from_slice(&resp.body)?;
let slot = raw.data.slots[0].clone();
slot.block_root
.ok_or_else(|| eyre::eyre!("Checkpoint not in returned slot"))
Expand Down
2 changes: 1 addition & 1 deletion config/tests/checkpoints.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use config::networks;
use ethers::types::H256;
use ethers_core::types::H256;

#[tokio::test]
async fn test_checkpoint_fallback() {
Expand Down
6 changes: 2 additions & 4 deletions consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ serde_json = "1.0.85"
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
milagro_bls = { git = "https://github.com/Snowfork/milagro_bls" }
ethers = "2.0.2"
ethers-core = { version = "2.0.8", default-features = false }
bytes = "1.2.1"
toml = "0.5.9"
async-trait = "0.1.57"
log = "0.4.17"
chrono = "0.4.23"
thiserror = "1.0.37"
reqwest = { version = "0.11.13", features = ["json"] }
superstruct = "0.7.0"

common = { path = "../common" }
Expand All @@ -29,5 +28,4 @@ openssl = { version = "0.10", features = ["vendored"] }
tokio = { version = "1", features = ["full"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-timer = "0.2.5"

ic-cdk = "0.10.0"
34 changes: 17 additions & 17 deletions consensus/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@ use super::rpc::ConsensusRpc;
use super::types::*;
use super::utils::*;

#[cfg(not(target_arch = "wasm32"))]
use std::time::SystemTime;
#[cfg(not(target_arch = "wasm32"))]
use std::time::UNIX_EPOCH;

#[cfg(target_arch = "wasm32")]
use wasm_timer::SystemTime;
#[cfg(target_arch = "wasm32")]
use wasm_timer::UNIX_EPOCH;

// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md
// does not implement force updates

Expand Down Expand Up @@ -531,13 +521,13 @@ impl<R: ConsensusRpc> ConsensusClient<R> {

fn age(&self, slot: u64) -> Duration {
let expected_time = self.slot_timestamp(slot);
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let now = duration_since_epoch();
let delay = now - std::time::Duration::from_secs(expected_time);
chrono::Duration::from_std(delay).unwrap()
}

pub fn expected_current_slot(&self) -> u64 {
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let now = duration_since_epoch();
let genesis_time = self.config.chain.genesis_time;
let since_genesis = now - std::time::Duration::from_secs(genesis_time);

Expand All @@ -555,11 +545,7 @@ impl<R: ConsensusRpc> ConsensusClient<R> {
let next_slot = current_slot + 1;
let next_slot_timestamp = self.slot_timestamp(next_slot);

let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();

let now = duration_since_epoch().as_secs();
let time_to_next_slot = next_slot_timestamp - now;
let next_update = time_to_next_slot + 4;

Expand All @@ -578,6 +564,20 @@ impl<R: ConsensusRpc> ConsensusClient<R> {
}
}

fn duration_since_epoch() -> std::time::Duration {
#[cfg(not(target_arch = "wasm32"))]
{
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
}

#[cfg(target_arch = "wasm32")]
{
std::time::Duration::from_nanos(ic_cdk::api::time())
}
}

fn get_participating_keys(
committee: &SyncCommittee,
bitfield: &Bitvector<512>,
Expand Down
Loading

0 comments on commit e42c2cd

Please sign in to comment.