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

first take at hooking up a node #117

Merged
merged 5 commits into from
Dec 31, 2019
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
1 change: 1 addition & 0 deletions tendermint-lite/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod requester;
pub mod state;
pub mod store;
pub mod threshold;
134 changes: 134 additions & 0 deletions tendermint-lite/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use tendermint::hash;
use tendermint::lite;
use tendermint::lite::Error;
use tendermint::lite::{
Header as _, Requester as _, SignedHeader as _, Store as _, TrustedState as _,
ValidatorSet as _,
};
use tendermint::rpc;
use tendermint::{block::Height, Hash};

use tendermint_lite::{
requester::Requester, state::State, store::MemStore, threshold::TrustThresholdOneThird,
};

use core::future::Future;
use std::time::{Duration, SystemTime};
use tokio::runtime::Builder;

// TODO: these should be config/args
static SUBJECTIVE_HEIGHT: u64 = 1;
static SUBJECTIVE_VALS_HASH_HEX: &str =
"A5A7DEA707ADE6156F8A981777CA093F178FC790475F6EC659B6617E704871DD";
static RPC_ADDR: &str = "localhost:26657";

// TODO: this should somehow be configurable ...
static THRESHOLD: &TrustThresholdOneThird = &TrustThresholdOneThird {};

pub fn block_on<F: Future>(future: F) -> F::Output {
Builder::new()
.basic_scheduler()
.enable_all()
.build()
.unwrap()
.block_on(future)
}

fn main() {
// TODO: this should be config
let trusting_period = Duration::new(600, 0);

// setup requester for primary peer
let client = block_on(rpc::Client::new(&RPC_ADDR.parse().unwrap())).unwrap();
let req = Requester::new(client);
let mut store = MemStore::new();

let vals_hash =
Hash::from_hex_upper(hash::Algorithm::Sha256, SUBJECTIVE_VALS_HASH_HEX).unwrap();

validate_and_init_subjective_state(
Height::from(SUBJECTIVE_HEIGHT),
vals_hash,
&mut store,
&req,
)
.unwrap();

loop {
// NOTE: 0 is a bad idea. use an Enum{ Height, LatestHeight } or something
// instead ..
let latest = (&req).signed_header(0).unwrap();
let latest_height = latest.header().height();

println!(
"attempting bisection from height {:?} to height {:?}",
store
.get(Height::from(0))
.unwrap()
.last_header()
.header()
.height(),
latest_height,
);

let now = &SystemTime::now();
lite::verify_and_update_bisection(
latest_height,
THRESHOLD,
&trusting_period,
now,
&req,
&mut store,
)
.unwrap();

println!("Succeeded bisecting!");

// notifications ?

// sleep for a few secs ?
}
}

/*
* The following is initialization logic that should have a
* function in the lite crate like:
* `subjective_init(height, vals_hash, store, requester) -> Result<(), Error`
* it would fetch the initial header/vals from the requester and populate a
* trusted state and store it in the store ...
* TODO: this should take traits ...
* TODO: better name ?
*/
fn validate_and_init_subjective_state(
height: Height,
vals_hash: Hash,
store: &mut MemStore,
req: &Requester,
) -> Result<(), Error> {
if store.get(height).is_ok() {
// we already have this !
return Ok(());
}

// check that the val hash matches
let vals = req.validator_set(height)?;

if vals.hash() != vals_hash {
// TODO
panic!("vals hash dont match")
}

let signed_header = req.signed_header(SUBJECTIVE_HEIGHT)?;

// TODO: validate signed_header.commit() with the vals ...

let next_vals = req.validator_set(height.increment())?;

// TODO: check next_vals ...

let trusted_state = &State::new(&signed_header, &next_vals);

store.add(trusted_state)?;

Ok(())
}
14 changes: 7 additions & 7 deletions tendermint-lite/src/requester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use tendermint::validator;
use core::future::Future;
use tokio::runtime::Builder;

// TODO rename to RPCRequester or something ...
pub struct Requester {
client: rpc::Client,
}

impl Requester {
fn new(client: rpc::Client) -> Self {
pub fn new(client: rpc::Client) -> Self {
Requester { client }
}
}
Expand All @@ -28,7 +29,10 @@ impl lite::types::Requester for Requester {
let r = block_on(self.client.commit(h));
match r {
Ok(response) => Ok(response.signed_header),
Err(e) => Err(lite::Error::RequestFailed),
Err(e) => {
println!("REQUEST ERR: {:?}", e);
Err(lite::Error::RequestFailed)
}
}
}

Expand All @@ -40,7 +44,7 @@ impl lite::types::Requester for Requester {
let r = block_on(self.client.validators(h));
match r {
Ok(response) => Ok(validator::Set::new(response.validators)),
Err(e) => Err(lite::Error::RequestFailed),
Err(_error) => Err(lite::Error::RequestFailed),
}
}
}
Expand All @@ -57,15 +61,11 @@ pub fn block_on<F: Future>(future: F) -> F::Output {
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
use block::signed_header::SignedHeader;
use tendermint::lite::types::Header as LiteHeader;
use tendermint::lite::types::Requester as LiteRequester;
use tendermint::lite::types::SignedHeader as LiteSignedHeader;
use tendermint::lite::types::ValidatorSet as LiteValSet;
use tendermint::net;
use tendermint::rpc;
use validator::Set;

#[test]
fn test_val_set() {
Expand Down
3 changes: 1 addition & 2 deletions tendermint-lite/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use tendermint::lite::TrustedState;
use tendermint::{block::signed_header::SignedHeader, lite, validator::Set};

use tendermint::{block::signed_header::SignedHeader, validator::Set};

#[derive(Clone)]
pub struct State {
Expand Down
13 changes: 7 additions & 6 deletions tendermint-lite/src/store.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use crate::state::State;
use tendermint::block::Height;
use tendermint::lite::{Store, TrustedState, SignedHeader, Header, Error};
use tendermint::lite::{Error, Header, SignedHeader, Store, TrustedState};

use std::collections::HashMap;

#[derive(Default)]
pub struct MemStore {
height: Height,
store: HashMap<Height, State>
store: HashMap<Height, State>,
}

impl MemStore {
fn new() -> MemStore {
pub fn new() -> MemStore {
MemStore {
height: Height::from(0),
store: HashMap::new(),
Expand All @@ -33,13 +34,13 @@ impl Store for MemStore {
if h.value() == 0 {
height = self.height
}
match self.store.get(&height){
match self.store.get(&height) {
Some(state) => Ok(state),
None => Err(Error::RequestFailed)
None => Err(Error::RequestFailed),
}
}

fn get_smaller_or_equal(&self, h: Height) -> Result<Self::TrustedState, Error> {
fn get_smaller_or_equal(&self, _height: Height) -> Result<Self::TrustedState, Error> {
Err(Error::RequestFailed)
}
}
11 changes: 11 additions & 0 deletions tendermint-lite/src/threshold.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use tendermint::lite::TrustThreshold;

pub struct TrustThresholdOneThird {}
impl TrustThreshold for TrustThresholdOneThird {}

pub struct TrustThresholdTwoThirds {}
impl TrustThreshold for TrustThresholdTwoThirds {
fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool {
signed_voting_power * 3 > total_voting_power * 2
}
}
9 changes: 9 additions & 0 deletions tendermint/src/lite/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,25 @@ where
// validate the untrusted header against its commit, vals, and next_vals
let untrusted_header = untrusted_sh.header();
let untrusted_commit = untrusted_sh.commit();
println!(
"validating untrusted header at height {:?}: {:?}, {:?}",
untrusted_height,
untrusted_header.hash(),
untrusted_commit.header_hash(),
);
validate_vals_and_commit(untrusted_header, untrusted_commit, untrusted_vals)?;
validate_next_vals(untrusted_header, untrusted_next_vals)?;

// if the new height is not sequential, check if we can skip
if untrusted_height > trusted_height.increment() {
let trusted_vals = trusted_state.validators();
println!("checking if we can skip to header {:?}", untrusted_height);
verify_commit_trusting(trusted_vals, untrusted_commit, trust_threshold)?;
println!("... ok to skip to header {:?}!", untrusted_height);
}

// verify the untrusted commit
println!("verifying commit for header {:?}", untrusted_height);
verify_commit_full(untrusted_vals, untrusted_sh.commit())
}

Expand Down
6 changes: 4 additions & 2 deletions tendermint/src/rpc/endpoint/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ pub struct Request {
impl Request {
/// Create a new request for commit info about a particular block
pub fn new(height: block::Height) -> Self {
Self {
height: Some(height),
let mut height_opt = Some(height);
if height.value() == 0 {
height_opt = None
}
Self { height: height_opt }
Copy link
Contributor

@tarcieri tarcieri Dec 30, 2019

Choose a reason for hiding this comment

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

I'd probably write this like:

Self {
    height: match height.value() {
        0 => None,
        _ => Some(height)
    }
}

Alternatively perhaps define a method on Height for this? It seems useful.

Copy link
Contributor

Choose a reason for hiding this comment

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

This was a shortcut to get the integration to work. There ought to be a more explicit way to request the latest height that depending on the implicit menaing of 0.

Copy link
Contributor

Choose a reason for hiding this comment

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

Addressed in 7477c9d

Copy link
Member Author

Choose a reason for hiding this comment

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

Opened an issue for this #118

}
}

Expand Down