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

Implement builder DSL for light client initialization, and other things #583

Merged
merged 31 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ad4f5e7
Add Send + Sync bound on light client Handle
romac Sep 23, 2020
ef64d96
Remove default implementation on Handle trait
romac Sep 23, 2020
0801054
Whitespace
romac Sep 23, 2020
08b73b5
Fix light-node code after Handle update
romac Sep 23, 2020
978190d
Remove unused mut qualifier after Supervisor update
romac Sep 23, 2020
07a2427
Derive Clone on SupervisorHandle
romac Sep 23, 2020
363e883
Add DSL for building supervisor and light clients
romac Sep 24, 2020
fde8856
Refactor Io component
romac Sep 24, 2020
6f352fe
Refactor Supervisor builder
romac Sep 24, 2020
4de977f
Re-add a TODO that got lost in the refactor
romac Sep 24, 2020
905a80b
Document builders
romac Sep 24, 2020
f8a63ab
Simplify return types of supervisor builder methods
romac Sep 24, 2020
b6f9da9
Update light client example to use builders
romac Sep 24, 2020
9b142db
Perform more validation when doing subjective initialization
romac Sep 24, 2020
fa7c2f5
Add option to use trusted state alrady in store to light client builder
romac Sep 24, 2020
60dd63e
Use builders in light node start command
romac Sep 24, 2020
26d1efe
Improve API of PeerListBuilder
romac Sep 24, 2020
d88b1f6
Finish adapting start command to use supervisor builder
romac Sep 24, 2020
2a65f69
Fix WASM build
romac Sep 24, 2020
ee4bbaa
Expose a couple functions on the supervisor
romac Sep 25, 2020
610035a
Block on async tasks in a new thread to avoid nesting Tokio runtimes
romac Sep 29, 2020
49d687e
Cleanup
romac Sep 29, 2020
15b4e9c
Nest std_ext module under utils
romac Sep 29, 2020
36a0a1a
Feature-guard `block_on` for WASM
romac Sep 29, 2020
d2380be
Fix typo in comment
romac Sep 29, 2020
c95ebcd
Ensure that at least one witness is provided in the SupervisorBuilder
romac Sep 29, 2020
e0f28f2
Rename SupervisorBuilder::unwrap to inner to express is infallability
romac Sep 29, 2020
0e1709d
Remove LightClientBuilder::trust_primary_latest since we do not need …
romac Sep 29, 2020
2705247
Make `LightClientBuilder::trust_light_block` method private
romac Sep 29, 2020
3f496fd
Perform more thorough validation of the trusted light block in the li…
romac Sep 29, 2020
ad4ba11
Update light client integration test to use builders
romac Sep 30, 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
102 changes: 35 additions & 67 deletions light-client/examples/light_client.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
use std::collections::HashMap;
use std::{
path::{Path, PathBuf},
time::Duration,
};

use gumdrop::Options;

use tendermint_light_client::supervisor::{Handle as _, Instance, Supervisor};
use tendermint::Hash;
use tendermint_rpc as rpc;

use tendermint_light_client::supervisor::{Handle as _, Instance};
use tendermint_light_client::{
components::{
clock::SystemClock,
io::{AtHeight, Io, ProdIo},
scheduler,
verifier::ProdVerifier,
},
evidence::ProdEvidenceReporter,
fork_detector::ProdForkDetector,
light_client::{self, LightClient},
peer_list::PeerList,
state::State,
builder::{LightClientBuilder, SupervisorBuilder},
light_client,
store::{sled::SledStore, LightStore},
types::{Height, PeerId, Status, TrustThreshold},
types::{Height, PeerId, TrustThreshold},
};

#[derive(Debug, Options)]
Expand Down Expand Up @@ -55,6 +48,11 @@ struct SyncOpts {
meta = "HEIGHT"
)]
trusted_height: Option<Height>,
#[options(
help = "hash of the initial trusted state (optional if store already initialized)",
meta = "HASH"
)]
trusted_hash: Option<Hash>,
#[options(
help = "path to the database folder",
meta = "PATH",
Expand Down Expand Up @@ -82,82 +80,52 @@ fn make_instance(
db_path: impl AsRef<Path>,
opts: &SyncOpts,
) -> Instance {
let mut peer_map = HashMap::new();
peer_map.insert(peer_id, addr);

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

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(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, Status::Verified);
} else if light_store.latest(Status::Verified).is_none() {
println!("[ error ] no trusted state in database, please specify a trusted header");
std::process::exit(1);
}
let light_store = SledStore::new(db);
let trusted_state = light_store.latest_trusted_or_verified();

let state = State {
light_store: Box::new(light_store),
verification_trace: HashMap::new(),
};
let rpc_client = rpc::HttpClient::new(addr).unwrap();

let options = light_client::Options {
trust_threshold: TrustThreshold {
numerator: 1,
denominator: 3,
},
trust_threshold: TrustThreshold::default(),
trusting_period: Duration::from_secs(36000),
clock_drift: Duration::from_secs(1),
};

let verifier = ProdVerifier::default();
let clock = SystemClock;
let scheduler = scheduler::basic_bisecting_schedule;
let builder =
LightClientBuilder::prod(peer_id, rpc_client, Box::new(light_store), options, None);

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

Instance::new(light_client, state)
if let (Some(height), Some(hash)) = (opts.trusted_height, opts.trusted_hash) {
builder.trust_primary_at(height, hash).unwrap().build()
} else if let Some(trusted_state) = trusted_state {
builder.trust_light_block(trusted_state).unwrap().build()
} else {
eprintln!("[ error ] no trusted state in database, please specify a trusted header");
std::process::exit(1)
}
}

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

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

let primary_addr = opts.address.clone();
let witness_addr = opts.address.clone();

let primary_path = opts.db_path.join(primary.to_string());
let witness_path = opts.db_path.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 mut peer_addr = HashMap::new();
peer_addr.insert(primary, addr.clone());
peer_addr.insert(witness, addr);

let peer_list = PeerList::builder()
.primary(primary, primary_instance)
.witness(witness, witness_instance)
.build();
let primary_instance = make_instance(primary, primary_addr.clone(), primary_path, &opts);
let witness_instance = make_instance(witness, witness_addr.clone(), witness_path, &opts);

let mut supervisor = Supervisor::new(
peer_list,
ProdForkDetector::default(),
ProdEvidenceReporter::new(peer_addr),
);
let supervisor = SupervisorBuilder::new()
.primary(primary, primary_addr, primary_instance)
.witness(witness, witness_addr, witness_instance)
.build_prod();

let handle = supervisor.handle();

Expand Down
9 changes: 9 additions & 0 deletions light-client/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! DSL for building light clients and supervisor

mod light_client;
pub use light_client::LightClientBuilder;

mod supervisor;
pub use supervisor::SupervisorBuilder;

pub mod error;
54 changes: 54 additions & 0 deletions light-client/src/builder/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Errors raised by the builder DSL

use anomaly::BoxError;
use anomaly::Context;
use tendermint::block::Height;
use tendermint::Hash;
use thiserror::Error;

use crate::components::io::IoError;

/// An error raised by the builder
pub type Error = anomaly::Error<Kind>;

/// The various error kinds raised by the builder
#[derive(Debug, Clone, Error, PartialEq)]
pub enum Kind {
/// I/O error
#[error("I/O error: {0}")]
Io(#[from] IoError),

/// Height mismatch
#[error("height mismatch: given = {given}, found = {found}")]
HeightMismatch {
/// Height of trusted header
given: Height,
/// Height of fetched header
found: Height,
},

/// Hash mismatch
#[error("hash mismatch: given = {given}, found = {found}")]
HashMismatch {
/// Hash of trusted header
given: Hash,
/// hash of fetched header
found: Hash,
},

/// Invalid light block
#[error("invalid light block")]
InvalidLightBlock,

/// No trusted state as found in the store
#[error("no trusted state in store")]
NoTrustedStateInStore,
}

impl Kind {
/// Add additional context (i.e. include a source error and capture a backtrace).
/// You can convert the resulting `Context` into an `Error` by calling `.into()`.
pub fn context(self, source: impl Into<BoxError>) -> Context<Self> {
Context::new(self, Some(source.into()))
}
}
Loading