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: integration test #431

Merged
merged 12 commits into from
Jul 10, 2020
4 changes: 4 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ jobs:
with:
command: test
args: -p tendermint --test integration --no-fail-fast -- --ignored
- uses: actions-rs/cargo@v1
xla marked this conversation as resolved.
Show resolved Hide resolved
with:
command: test
args: -p tendermint-light-client --test integration --no-fail-fast -- --ignored

test-integration-latest:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ Light Client:
- Expose latest_trusted from Supervisor Handle ([#394])
- Turn `Handle` into a trait for ease of integration and testability ([#401])
- Improve `Supervisor` ergonomics according to [ADR-007] ([#403])
- Add integration test ([#431])

[#394]: https://github.com/informalsystems/tendermint-rs/pull/394
[#401]: https://github.com/informalsystems/tendermint-rs/pull/401
[#403]: https://github.com/informalsystems/tendermint-rs/pull/403
[#431]: https://github.com/informalsystems/tendermint-rs/pull/431
[ADR-007]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/architecture/adr-007-light-client-supervisor-ergonomics.md

## [0.14.1] (2020-06-23)
Expand Down
1 change: 1 addition & 0 deletions light-client/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub fn trusted_store_contains_block_at_target_height(
target_height: Height,
) -> bool {
light_store.get(target_height, Status::Verified).is_some()
|| light_store.get(target_height, Status::Trusted).is_some()
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if there are more places like this. reminded me a little of #419

Copy link
Member Author

@romac romac Jul 9, 2020

Choose a reason for hiding this comment

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

Yeah I thought I had caught them all in #419 but evidently not. Will go over the codebase again to make sure.

}

pub fn is_within_trust_period(
Expand Down
4 changes: 3 additions & 1 deletion light-client/src/evidence.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::{components::io::IoError, types::PeerId};

use tendermint::{abci::transaction::Hash, evidence::Evidence};
use tendermint::abci::transaction::Hash;
use tendermint_rpc as rpc;

use contracts::{contract_trait, pre};
use std::collections::HashMap;

pub use tendermint::evidence::Evidence;

/// Interface for reporting evidence to full nodes, typically via the RPC client.
#[contract_trait]
pub trait EvidenceReporter: Send {
Expand Down
121 changes: 121 additions & 0 deletions light-client/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! Light Client integration tests.
//!
//! These are all ignored by default, since they test against running
//! `tendermint node --proxy_app=kvstore`. They can be run using:
//!
//! ```
//! cargo test -- --ignored
//! ```

use tendermint_light_client::{
components::{
clock::SystemClock,
io::{AtHeight, Io, IoError, ProdIo},
scheduler,
verifier::ProdVerifier,
},
evidence::{Evidence, EvidenceReporter},
fork_detector::ProdForkDetector,
light_client::{self, LightClient},
peer_list::PeerList,
state::State,
store::{memory::MemoryStore, LightStore},
supervisor::{Handle, Instance, Supervisor},
types::{PeerId, Status, Time, TrustThreshold},
};

use tendermint::abci::transaction::Hash as TransactionHash;

use std::collections::HashMap;
use std::time::Duration;

fn make_instance(peer_id: PeerId, options: light_client::Options, io: ProdIo) -> Instance {
xla marked this conversation as resolved.
Show resolved Hide resolved
let trusted_state = io
.fetch_light_block(peer_id, AtHeight::Highest)
.expect("could not request latest light block");

let mut light_store = MemoryStore::new();
light_store.insert(trusted_state, Status::Trusted);

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

let verifier = ProdVerifier::default();
let clock = SystemClock;
let scheduler = scheduler::basic_bisecting_schedule;

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

Instance::new(light_client, state)
}

struct TestEvidenceReporter;

#[contracts::contract_trait]
impl EvidenceReporter for TestEvidenceReporter {
fn report(&self, evidence: Evidence, peer: PeerId) -> Result<TransactionHash, IoError> {
panic!(
"unexpected fork detected for peer {} with evidence: {:?}",
peer, evidence
);
}
}

#[test]
#[ignore]
fn sync() {
let primary: PeerId = "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE".parse().unwrap();
let witness: PeerId = "CEFEEDBADFADAD0C0CEEFACADE0ADEADBEEFC0FF".parse().unwrap();

let node_address: tendermint::net::Address = "tcp://127.0.0.1:26657".parse().unwrap();

let mut peer_map = HashMap::new();
peer_map.insert(primary, node_address.clone());
peer_map.insert(witness, node_address);
Copy link
Member

Choose a reason for hiding this comment

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

Here the same RPC end-point is used twice. Do you think it makes sense add a comment that in a realistic scenario these would be different and that you'd check if the peerId matches?

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in 4128382.


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

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

let primary_instance = make_instance(primary, options, io.clone());
let witness_instance = make_instance(witness, options, io);

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

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

let handle = supervisor.handle();
std::thread::spawn(|| supervisor.run());

let max_iterations: usize = 20;

for i in 1..=max_iterations {
println!("[info ] - iteration {}/{}", i, max_iterations);

match handle.verify_to_highest() {
Ok(light_block) => {
println!("[info ] synced to block {}", light_block.height());
}
Err(err) => {
println!("[error] sync failed: {}", err);
panic!("failed to sync to highest: {}", err);
}
}

std::thread::sleep(Duration::from_millis(800));
}
}