Skip to content

Commit

Permalink
Remove light client's internal state
Browse files Browse the repository at this point in the history
  • Loading branch information
romac committed Jun 5, 2020
1 parent 8ce22d9 commit d42c7aa
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 78 deletions.
7 changes: 3 additions & 4 deletions light-client/examples/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn sync_cmd(opts: SyncOpts) {
light_store.insert(trusted_state, VerifiedStatus::Verified);
}

let state = State {
let mut state = State {
light_store: Box::new(light_store),
verification_trace: HashMap::new(),
};
Expand All @@ -104,11 +104,10 @@ fn sync_cmd(opts: SyncOpts) {
let clock = SystemClock;
let scheduler = scheduler::basic_schedule;

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

loop {
match light_client.verify_to_highest() {
match light_client.verify_to_highest(&mut state) {
Ok(light_block) => {
println!("[ info ] synced to block {}", light_block.height());
}
Expand Down
25 changes: 12 additions & 13 deletions light-client/src/fork_detector.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};

use crate::prelude::*;
use crate::supervisor::Instance;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ForkDetection {
Expand All @@ -18,8 +19,8 @@ pub trait ForkDetector {
fn detect_forks(
&self,
light_block: &LightBlock,
primary: &LightClient,
secondaries: Vec<&LightClient>,
trusted_state: &LightBlock,
secondaries: Vec<&Instance>,
) -> ForkDetection;
}

Expand All @@ -41,14 +42,16 @@ impl ForkDetector for ProdForkDetector {
fn detect_forks(
&self,
light_block: &LightBlock,
primary: &LightClient,
secondaries: Vec<&LightClient>,
trusted_state: &LightBlock,
secondaries: Vec<&Instance>,
) -> ForkDetection {
let mut forks = Vec::with_capacity(secondaries.len());

for secondary in secondaries {
let mut state: State = todo();
let mut state = State::new(MemoryStore::new());

let secondary_block = secondary
.light_client
.get_or_fetch_block(light_block.height(), &mut state)
.unwrap(); // FIXME: unwrap

Expand All @@ -57,21 +60,17 @@ impl ForkDetector for ProdForkDetector {
continue;
}

let latest_trusted = primary
.state
.light_store
.latest(VerifiedStatus::Verified)
.unwrap(); // FIXME: unwrap

state
.light_store
.update(latest_trusted, VerifiedStatus::Verified);
.update(trusted_state.clone(), VerifiedStatus::Verified);

state
.light_store
.update(secondary_block.clone(), VerifiedStatus::Unverified);

let result = secondary.verify_to_target_with_state(light_block.height(), &mut state);
let result = secondary
.light_client
.verify_to_target(light_block.height(), &mut state);

match result {
Ok(_) => forks.push(Fork::Forked(secondary_block)),
Expand Down
42 changes: 4 additions & 38 deletions light-client/src/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ impl Options {
/// is designed for this security model.
pub struct LightClient {
pub peer: PeerId,
pub state: State,
pub options: Options,
clock: Box<dyn Clock>,
scheduler: Box<dyn Scheduler>,
Expand All @@ -59,7 +58,6 @@ impl fmt::Debug for LightClient {
f.debug_struct("LightClient")
.field("peer", &self.peer)
.field("options", &self.options)
.field("state", &self.state)
.finish()
}
}
Expand All @@ -68,7 +66,6 @@ impl LightClient {
/// Constructs a new light client
pub fn new(
peer: PeerId,
state: State,
options: Options,
clock: impl Clock + 'static,
scheduler: impl Scheduler + 'static,
Expand All @@ -77,7 +74,6 @@ impl LightClient {
) -> Self {
Self {
peer,
state,
options,
clock: Box::new(clock),
scheduler: Box::new(scheduler),
Expand All @@ -89,13 +85,13 @@ impl LightClient {
/// Attempt to update the light client to the latest block of the primary node.
///
/// Note: This functin delegates the actual work to `verify_to_target`.
pub fn verify_to_highest(&mut self) -> Result<LightBlock, Error> {
pub fn verify_to_highest(&mut self, state: &mut State) -> Result<LightBlock, Error> {
let target_block = match self.io.fetch_light_block(self.peer, LATEST_HEIGHT) {
Ok(last_block) => last_block,
Err(io_error) => bail!(ErrorKind::Io(io_error)),
};

self.verify_to_target(target_block.height())
self.verify_to_target(target_block.height(), state)
}

/// Attemps to update the light client to a block of the primary node at the given height.
Expand Down Expand Up @@ -127,30 +123,6 @@ impl LightClient {
/// - If the core verification loop invariant is violated [LCV-INV-TP.1]
/// - If verification of a light block fails
/// - If it cannot fetch a block from the blockchain
///
/// ## Note
/// - This method actually delegates the actual work to `verify_to_target_with_state` over the current state.
// #[pre(
// light_store_contains_block_within_trusting_period(
// self.state.light_store.as_ref(),
// self.options.trusting_period,
// self.clock.now(),
// )
// )]
#[post(
ret.is_ok() ==> trusted_store_contains_block_at_target_height(
self.state.light_store.as_ref(),
target_height,
)
)]
pub fn verify_to_target(&mut self, target_height: Height) -> Result<LightBlock, Error> {
let mut state = std::mem::replace(&mut self.state, State::new(MemoryStore::new()));
let result = self.verify_to_target_with_state(target_height, &mut state);
self.state = state;
result
}

/// See `verify_to_target`
// #[pre(
// light_store_contains_block_within_trusting_period(
// state.light_store.as_ref(),
Expand All @@ -164,14 +136,13 @@ impl LightClient {
target_height,
)
)]
pub fn verify_to_target_with_state(
pub fn verify_to_target(
&self,
target_height: Height,
state: &mut State,
) -> Result<LightBlock, Error> {
// Let's first look in the store to see whether we have already successfully verified this block
if let Some(light_block) = self
.state
if let Some(light_block) = state
.light_store
.get(target_height, VerifiedStatus::Verified)
{
Expand Down Expand Up @@ -249,11 +220,6 @@ impl LightClient {
}
}

/// Get the verification trace for the block at target_height.
pub fn get_trace(&self, target_height: Height) -> Vec<LightBlock> {
self.state.get_trace(target_height)
}

/// Look in the light store for a block from the given peer at the given height.
/// If one cannot be found, fetch the block from the given peer.
///
Expand Down
54 changes: 36 additions & 18 deletions light-client/src/supervisor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub enum Event {
#[derive(Default)]
pub struct PeerListBuilder {
primary: Option<PeerId>,
peers: HashMap<PeerId, LightClient>,
peers: HashMap<PeerId, Instance>,
}

impl PeerListBuilder {
Expand All @@ -29,8 +29,8 @@ impl PeerListBuilder {
self
}

pub fn peer(&mut self, peer_id: PeerId, client: LightClient) -> &mut Self {
self.peers.insert(peer_id, client);
pub fn peer(&mut self, peer_id: PeerId, instance: Instance) -> &mut Self {
self.peers.insert(peer_id, instance);
self
}

Expand All @@ -45,9 +45,15 @@ impl PeerListBuilder {
}
}

#[derive(Debug)]
pub struct Instance {
pub light_client: LightClient,
pub state: State,
}

#[derive(Debug)]
pub struct PeerList {
peers: HashMap<PeerId, LightClient>,
peers: HashMap<PeerId, Instance>,
primary: PeerId,
}

Expand All @@ -56,23 +62,23 @@ impl PeerList {
PeerListBuilder::default()
}

pub fn get(&self, peer_id: &PeerId) -> Option<&LightClient> {
pub fn get(&self, peer_id: &PeerId) -> Option<&Instance> {
self.peers.get(peer_id)
}

pub fn get_mut(&mut self, peer_id: &PeerId) -> Option<&mut LightClient> {
pub fn get_mut(&mut self, peer_id: &PeerId) -> Option<&mut Instance> {
self.peers.get_mut(peer_id)
}

pub fn primary(&self) -> Option<&LightClient> {
pub fn primary(&self) -> Option<&Instance> {
self.peers.get(&self.primary)
}

pub fn primary_mut(&mut self) -> Option<&mut LightClient> {
pub fn primary_mut(&mut self) -> Option<&mut Instance> {
self.peers.get_mut(&self.primary)
}

pub fn secondaries(&self) -> Vec<&LightClient> {
pub fn secondaries(&self) -> Vec<&Instance> {
self.peers
.keys()
.filter(|peer_id| peer_id != &&self.primary)
Expand Down Expand Up @@ -116,11 +122,21 @@ impl Supervisor {
#[pre(self.peers.primary().is_some())]
pub fn verify_to_target(&mut self, height: Height) -> VerificationResult {
while let Some(primary) = self.peers.primary_mut() {
let verdict = primary.verify_to_target(height);
let verdict = primary
.light_client
.verify_to_target(height, &mut primary.state);

match verdict {
Ok(light_block) => {
let outcome = self.detect_forks(&light_block);
// SAFETY: There must be a latest trusted state otherwise verification would have failed.
let trusted_state = primary
.state
.light_store
.latest(VerifiedStatus::Verified)
.unwrap();

let outcome = self.detect_forks(&light_block, &trusted_state);

match outcome {
Some(forks) => {
let mut forked = Vec::with_capacity(forks.len());
Expand Down Expand Up @@ -166,16 +182,18 @@ impl Supervisor {
}

#[pre(self.peers.primary().is_some())]
fn detect_forks(&mut self, light_block: &LightBlock) -> Option<Vec<Fork>> {
fn detect_forks(
&mut self,
light_block: &LightBlock,
trusted_state: &LightBlock,
) -> Option<Vec<Fork>> {
if self.peers.secondaries().is_empty() {
return None;
}

let primary = self.peers.primary().unwrap();
let secondaries = self.peers.secondaries();

let fork_detector = ProdForkDetector::new();
let result = fork_detector.detect_forks(light_block, primary, secondaries);
let fork_detector = ProdForkDetector::new(); // TODO: Should be injectable
let result =
fork_detector.detect_forks(light_block, &trusted_state, self.peers.secondaries());

match result {
ForkDetection::Detected(forks) => Some(forks),
Expand Down Expand Up @@ -239,7 +257,7 @@ impl Handler {

match receiver.recv().unwrap() {
Event::VerificationSuccessed(header) => Ok(header),
Event::VerificationFailed(_err) => todo!(),
Event::VerificationFailed(err) => Err(err),
_ => todo!(),
}
}
Expand Down
10 changes: 5 additions & 5 deletions light-client/tests/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,11 @@ impl Clock for MockClock {
fn verify_bisection(
untrusted_height: Height,
light_client: &mut LightClient,
state: &mut State,
) -> Result<Vec<LightBlock>, Error> {
light_client
.verify_to_target(untrusted_height)
.map(|_| light_client.get_trace(untrusted_height))
.verify_to_target(untrusted_height, state)
.map(|_| state.get_trace(untrusted_height))
}

fn run_bisection_test(tc: TestBisection<LightBlock>) {
Expand Down Expand Up @@ -182,7 +183,7 @@ fn run_bisection_test(tc: TestBisection<LightBlock>) {
let mut light_store = MemoryStore::new();
light_store.insert(trusted_state, VerifiedStatus::Verified);

let state = State {
let mut state = State {
light_store: Box::new(light_store),
verification_trace: HashMap::new(),
};
Expand All @@ -191,15 +192,14 @@ fn run_bisection_test(tc: TestBisection<LightBlock>) {

let mut light_client = LightClient::new(
primary,
state,
options,
clock,
basic_schedule,
verifier,
io.clone(),
);

match verify_bisection(untrusted_height, &mut light_client) {
match verify_bisection(untrusted_height, &mut light_client, &mut state) {
Ok(new_states) => {
let untrusted_light_block = io
.fetch_light_block(primary.clone(), untrusted_height)
Expand Down

0 comments on commit d42c7aa

Please sign in to comment.