-
Notifications
You must be signed in to change notification settings - Fork 224
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into empty-block-id-in-vote
- Loading branch information
Showing
28 changed files
with
1,372 additions
and
363 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
unstable_features = true | ||
|
||
# comments | ||
comment_width = 100 | ||
wrap_comments = true | ||
|
||
# imports | ||
reorder_imports = true | ||
|
||
# strings | ||
format_strings = false | ||
max_width = 100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
|
||
members = [ | ||
"tendermint", | ||
"tendermint-lite", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "tendermint-lite" | ||
version = "0.1.0" | ||
authors = ["Ethan Buchman <[email protected]>"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
tendermint = { path = "../tendermint" } | ||
tokio = "0.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod requester; | ||
pub mod state; | ||
pub mod store; | ||
pub mod threshold; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
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::RPCRequester, 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(6000, 0); | ||
|
||
// setup requester for primary peer | ||
let client = block_on(rpc::Client::new(&RPC_ADDR.parse().unwrap())).unwrap(); | ||
let req = RPCRequester::new(client); | ||
let mut store = MemStore::new(); | ||
|
||
let vals_hash = | ||
Hash::from_hex_upper(hash::Algorithm::Sha256, SUBJECTIVE_VALS_HASH_HEX).unwrap(); | ||
|
||
subjective_init(Height::from(SUBJECTIVE_HEIGHT), vals_hash, &mut store, &req).unwrap(); | ||
|
||
loop { | ||
let latest = (&req).signed_header(0).unwrap(); | ||
let latest_peer_height = latest.header().height(); | ||
|
||
let latest = store.get(Height::from(0)).unwrap(); | ||
let latest_height = latest.last_header().header().height(); | ||
|
||
// only bisect to higher heights | ||
if latest_peer_height <= latest_height { | ||
std::thread::sleep(Duration::new(1, 0)); | ||
continue; | ||
} | ||
|
||
println!( | ||
"attempting bisection from height {:?} to height {:?}", | ||
store | ||
.get(Height::from(0)) | ||
.unwrap() | ||
.last_header() | ||
.header() | ||
.height(), | ||
latest_peer_height, | ||
); | ||
|
||
let now = &SystemTime::now(); | ||
lite::verify_and_update_bisection( | ||
latest_peer_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 ... but how to deal with the State ? | ||
* TODO: better name ? | ||
*/ | ||
fn subjective_init( | ||
height: Height, | ||
vals_hash: Hash, | ||
store: &mut MemStore, | ||
req: &RPCRequester, | ||
) -> 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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use tendermint::block; | ||
use tendermint::lite; | ||
use tendermint::rpc; | ||
use tendermint::validator; | ||
|
||
use core::future::Future; | ||
use tokio::runtime::Builder; | ||
|
||
/// RPCRequester wraps the Tendermint rpc::Client. | ||
pub struct RPCRequester { | ||
client: rpc::Client, | ||
} | ||
|
||
impl RPCRequester { | ||
pub fn new(client: rpc::Client) -> Self { | ||
RPCRequester { client } | ||
} | ||
} | ||
|
||
impl lite::types::Requester for RPCRequester { | ||
type SignedHeader = block::signed_header::SignedHeader; | ||
type ValidatorSet = validator::Set; | ||
|
||
/// Request the signed header at height h. | ||
/// If h==0, request the latest signed header. | ||
/// TODO: use an enum instead of h==0. | ||
fn signed_header<H>(&self, h: H) -> Result<Self::SignedHeader, lite::Error> | ||
where | ||
H: Into<block::Height>, | ||
{ | ||
let height: block::Height = h.into(); | ||
let r = match height.value() { | ||
0 => block_on(self.client.latest_commit()), | ||
_ => block_on(self.client.commit(height)), | ||
}; | ||
match r { | ||
Ok(response) => Ok(response.signed_header), | ||
Err(_error) => Err(lite::Error::RequestFailed), | ||
} | ||
} | ||
|
||
/// Request the validator set at height h. | ||
fn validator_set<H>(&self, h: H) -> Result<Self::ValidatorSet, lite::Error> | ||
where | ||
H: Into<block::Height>, | ||
{ | ||
let r = block_on(self.client.validators(h)); | ||
match r { | ||
Ok(response) => Ok(validator::Set::new(response.validators)), | ||
Err(_error) => Err(lite::Error::RequestFailed), | ||
} | ||
} | ||
} | ||
|
||
pub fn block_on<F: Future>(future: F) -> F::Output { | ||
Builder::new() | ||
.basic_scheduler() | ||
.enable_all() | ||
.build() | ||
.unwrap() | ||
.block_on(future) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
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::rpc; | ||
|
||
// TODO: integration test | ||
#[test] | ||
#[ignore] | ||
fn test_val_set() { | ||
let client = block_on(rpc::Client::new(&"localhost:26657".parse().unwrap())).unwrap(); | ||
let req = RPCRequester::new(client); | ||
let r1 = req.validator_set(5).unwrap(); | ||
let r2 = req.signed_header(5).unwrap(); | ||
assert_eq!(r1.hash(), r2.header().validators_hash()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use tendermint::lite::TrustedState; | ||
use tendermint::{block::signed_header::SignedHeader, validator::Set}; | ||
|
||
#[derive(Clone)] | ||
pub struct State { | ||
last_header: SignedHeader, | ||
vals: Set, | ||
} | ||
|
||
impl TrustedState for State { | ||
type LastHeader = SignedHeader; | ||
type ValidatorSet = Set; | ||
|
||
fn new(last_header: &Self::LastHeader, vals: &Self::ValidatorSet) -> Self { | ||
State { | ||
last_header: last_header.clone(), | ||
vals: vals.clone(), | ||
} | ||
} | ||
|
||
// height H-1 | ||
fn last_header(&self) -> &Self::LastHeader { | ||
&self.last_header | ||
} | ||
|
||
// height H | ||
fn validators(&self) -> &Self::ValidatorSet { | ||
&self.vals | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use crate::state::State; | ||
use tendermint::block::Height; | ||
use tendermint::lite::{Error, Header, SignedHeader, Store, TrustedState}; | ||
|
||
use std::collections::HashMap; | ||
|
||
#[derive(Default)] | ||
pub struct MemStore { | ||
height: Height, | ||
store: HashMap<Height, State>, | ||
} | ||
|
||
impl MemStore { | ||
pub fn new() -> MemStore { | ||
MemStore { | ||
height: Height::from(0), | ||
store: HashMap::new(), | ||
} | ||
} | ||
} | ||
|
||
impl Store for MemStore { | ||
type TrustedState = State; | ||
|
||
fn add(&mut self, trusted: &Self::TrustedState) -> Result<(), Error> { | ||
let height = trusted.last_header().header().height(); | ||
self.height = height; | ||
self.store.insert(height, trusted.clone()); | ||
Ok(()) | ||
} | ||
|
||
fn get(&self, h: Height) -> Result<&Self::TrustedState, Error> { | ||
let mut height = h; | ||
if h.value() == 0 { | ||
height = self.height | ||
} | ||
match self.store.get(&height) { | ||
Some(state) => Ok(state), | ||
None => Err(Error::RequestFailed), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.