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

[light-client] Supervisor #302

Merged
merged 39 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
101a96f
Remove Peers struct, only supply one peer to each light client instance
romac Jun 4, 2020
b60811e
Remove fork detector from light client instance
romac Jun 4, 2020
4fccace
Add Debug instance to LightClient
romac Jun 4, 2020
6478b46
Require all components and their deps to be clonable
romac Jun 4, 2020
d5436d3
Start work on supervisor and fork detection
romac Jun 4, 2020
4210a8c
Allowing supplying the state to work over to the light client
romac Jun 4, 2020
6829e5b
Remove unnecessary instances of operations traits
romac Jun 4, 2020
97a999f
Remove unnecessary Clone and DynClone instances
romac Jun 4, 2020
667a770
Improve docs
romac Jun 4, 2020
f0e71d4
Small refactor in supervisor
romac Jun 4, 2020
41fe43e
Finalize fork detection
romac Jun 5, 2020
215d24f
Fix wrong uses of internal light client state instead of the supplied…
romac Jun 5, 2020
2f0c90d
Remove light client's internal state
romac Jun 5, 2020
0926735
Remove outdated comments
romac Jun 10, 2020
f96f4d5
Ensure the supervisor can be sent across thread boundaries
romac Jun 10, 2020
14c5959
Rename Handler to Handle
romac Jun 10, 2020
f6c1fae
Update comment
romac Jun 10, 2020
7685749
Update example to invoke supervisor
romac Jun 10, 2020
b860060
Formatting
romac Jun 10, 2020
8b77006
Working supervisor example
romac Jun 10, 2020
e8b0b66
Remove light store committed by error
romac Jun 10, 2020
ae571d7
Compare headers hashes instead of for equality in fork detector
romac Jun 10, 2020
431b950
Bubble up I/O errors that may occur during fork detection
romac Jun 10, 2020
81a61d4
Inject fork detector into supervisor
romac Jun 10, 2020
4dbcde0
Deduplicate Handle::verify_to_highest/verify_to_target
romac Jun 10, 2020
b2a3756
Move PeerList and its builder into their own module
romac Jun 10, 2020
2df87dc
Record error which caused peer to be deemed faulty
romac Jun 10, 2020
0b7b066
Remove prelude module in favor of explicit imports
romac Jun 11, 2020
e044ab5
Hoist primary hash computation outside of for loop
romac Jun 11, 2020
7e9279c
Throw an error if there are no witnesses when doing fork detection
romac Jun 11, 2020
289ca33
Rename leftover secondaries to witnesses
romac Jun 11, 2020
35f6ee9
Set clock_drift parameter to 10 seconds to match Go default
romac Jun 11, 2020
8fca5c1
Add configurable timeouts to Io component
romac Jun 12, 2020
7a32855
Add the primary node as a witness in the example CLI
romac Jun 12, 2020
56bc022
Remove witness from peers when request times out during fork detection
romac Jun 12, 2020
d24d1bf
Fix PeerList::swap_primary
romac Jun 15, 2020
5e24ec8
Improve PeerListBuilder API
romac Jun 15, 2020
809d394
Add doc comments
romac Jun 15, 2020
b6b50a8
Use enum instead of 0 to denote latest height
romac Jun 15, 2020
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
12 changes: 7 additions & 5 deletions light-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ edition = "2018"
tendermint = { path = "../tendermint" }

anomaly = { version = "0.2.0", features = ["serializer"] }
contracts = "0.4.0"
crossbeam-channel = "0.4.2"
derive_more = "0.99.5"
futures = "0.3.4"
prost-amino = "0.5.0"
serde = "1.0.106"
serde_cbor = "0.11.1"
serde_derive = "1.0.106"
sled = "0.31.0"
static_assertions = "1.1.0"
thiserror = "1.0.15"
futures = "0.3.4"
tokio = "0.2.20"
prost-amino = "0.5.0"
contracts = "0.4.0"
sled = "0.31.0"
serde_cbor = "0.11.1"

[dev-dependencies]
serde_json = "1.0.51"
Expand Down
106 changes: 71 additions & 35 deletions light-client/examples/light_client.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
use tendermint_light_client::{
components::{
clock::SystemClock,
io::{AtHeight, Io, ProdIo},
scheduler,
verifier::ProdVerifier,
},
fork_detector::ProdForkDetector,
light_client::{self, LightClient},
peer_list::PeerList,
state::State,
store::{sled::SledStore, LightStore, VerifiedStatus},
supervisor::{Instance, Supervisor},
types::{Height, PeerId, Time, TrustThreshold},
};

use gumdrop::Options;
use tendermint_light_client::prelude::Height;

use std::collections::HashMap;
use std::path::PathBuf;
use std::{
path::{Path, PathBuf},
time::Duration,
};

#[derive(Debug, Options)]
struct CliOptions {
Expand Down Expand Up @@ -57,46 +75,47 @@ fn main() {
}
}

fn sync_cmd(opts: SyncOpts) {
use tendermint_light_client::components::scheduler;
use tendermint_light_client::prelude::*;

let primary_addr = opts.address;
let primary: PeerId = "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE".parse().unwrap();

fn make_instance(
peer_id: PeerId,
addr: tendermint::net::Address,
db_path: impl AsRef<Path>,
opts: &SyncOpts,
) -> Instance {
let mut peer_map = HashMap::new();
peer_map.insert(primary, primary_addr);
peer_map.insert(peer_id, addr);

let mut io = ProdIo::new(peer_map);
let timeout = Duration::from_secs(10);
let io = ProdIo::new(peer_map, Some(timeout));

let db = sled::open(opts.db_path).unwrap_or_else(|e| {
let db = sled::open(db_path).unwrap_or_else(|e| {
println!("[ error ] could not open database: {}", e);
std::process::exit(1);
});

let mut light_store = SledStore::new(db);

if let Some(height) = opts.trusted_height {
let trusted_state = io.fetch_light_block(primary, height).unwrap_or_else(|e| {
println!("[ error ] could not retrieve trusted header: {}", e);
std::process::exit(1);
});
let trusted_state = io
.fetch_light_block(peer_id, AtHeight::At(height))
.unwrap_or_else(|e| {
println!("[ error ] could not retrieve trusted header: {}", e);
std::process::exit(1);
});

light_store.insert(trusted_state, VerifiedStatus::Verified);
} else {
if light_store.latest(VerifiedStatus::Verified).is_none() {
println!("[ error ] no trusted state in database, please specify a trusted header");
std::process::exit(1);
}
}

let peers = Peers {
primary,
witnesses: Vec::new(),
};

let state = State {
peers,
light_store: Box::new(light_store),
verification_trace: HashMap::new(),
};

let options = Options {
let options = light_client::Options {
trust_threshold: TrustThreshold {
numerator: 1,
denominator: 3,
Expand All @@ -109,27 +128,44 @@ fn sync_cmd(opts: SyncOpts) {
let verifier = ProdVerifier::default();
let clock = SystemClock;
let scheduler = scheduler::basic_bisecting_schedule;
let fork_detector = ProdForkDetector::default();

let mut light_client = LightClient::new(
state,
options,
clock,
scheduler,
verifier,
fork_detector,
io,
);
let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io);

Instance::new(light_client, state)
}

fn sync_cmd(opts: SyncOpts) {
let addr = opts.address.clone();

let primary: PeerId = "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE".parse().unwrap();
let witness: PeerId = "CEFEEDBADFADAD0C0CEEFACADE0ADEADBEEFC0FF".parse().unwrap();

let primary_path = opts.db_path.clone().join(primary.to_string());
let witness_path = opts.db_path.clone().join(witness.to_string());

let primary_instance = make_instance(primary, addr.clone(), primary_path, &opts);
let witness_instance = make_instance(witness, addr.clone(), witness_path, &opts);

let peer_list = PeerList::builder()
.primary(primary, primary_instance)
.witness(witness, witness_instance)
.build();

let mut supervisor = Supervisor::new(peer_list, ProdForkDetector::default());
let mut handle = supervisor.handle();

std::thread::spawn(|| supervisor.run());

loop {
match light_client.verify_to_highest() {
handle.verify_to_highest_async(|result| match result {
Ok(light_block) => {
println!("[ info ] synced to block {}", light_block.height());
}
Err(e) => {
println!("[ error ] sync failed: {}", e);
}
}
});

std::thread::sleep(Duration::from_millis(800));
romac marked this conversation as resolved.
Show resolved Hide resolved
}
}
26 changes: 26 additions & 0 deletions light-client/src/callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::fmt;

/// A boxed `FnOnce(A) -> () + Send`.
pub struct Callback<A> {
inner: Box<dyn FnOnce(A) -> () + Send>,
}

impl<A> fmt::Debug for Callback<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Callback").finish()
}
}

impl<A> Callback<A> {
/// Box the given closure in a `Callback`.
pub fn new(inner: impl FnOnce(A) -> () + Send + 'static) -> Self {
Self {
inner: Box::new(inner),
}
}

/// Call the underlying closure on `result`.
pub fn call(self, result: A) -> () {
(self.inner)(result);
}
}
1 change: 0 additions & 1 deletion light-client/src/components.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Components used by the Light Client.

pub mod clock;
pub mod fork_detector;
pub mod io;
pub mod scheduler;
pub mod verifier;
5 changes: 3 additions & 2 deletions light-client/src/components/clock.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::prelude::*;
use crate::types::Time;

/// Abstracts over the current time.
pub trait Clock {
pub trait Clock: Send {
/// Get the current time.
fn now(&self) -> Time;
}

/// Provides the current wall clock time.
#[derive(Copy, Clone)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> Time {
Expand Down
54 changes: 0 additions & 54 deletions light-client/src/components/fork_detector.rs

This file was deleted.

Loading