Skip to content

Issue #53 Dummy Adapter #54

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

Merged
merged 35 commits into from
Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3e60b7e
Workspace - add `Adapter` crate
elpiel Jun 4, 2019
8182c8f
domain - validator - make `fixtures` module `pub`, instead of `pub(cr…
elpiel Jun 4, 2019
76d7cc6
adapter - Cargo.toml - add `domain` to deps and enable `fixtures` fea…
elpiel Jun 4, 2019
2564845
adapter - lib - start impl SanityChecker & add simple trait Adapter
elpiel Jun 4, 2019
637b4f7
adapter - separate SanityChecker, Adapter and add a feature for Dummy…
elpiel Jun 4, 2019
396afab
adapter - Cargo.toml - add the `dummy-adapter` feature
elpiel Jun 4, 2019
ebe5653
domain - channel - impl SpecValidators, as we need to handle only a l…
elpiel Jun 4, 2019
b0df45e
adapter - sanity - simplify the check if the adapter exists in the ch…
elpiel Jun 4, 2019
020bafe
Makefile - add `tasks.clippy` to `dev-test-flow` after `rustfmt` for …
elpiel Jun 4, 2019
0e3fa1a
`Clippy` warnings fix - take `&str` instead of `&String`
elpiel Jun 4, 2019
5dc46f8
Clippy:
elpiel Jun 4, 2019
c8bffbe
adapter - `Adapter::whoami()` use `&str` instead of `&String`
elpiel Jun 4, 2019
a5b8dd3
domain - channel - fixtures - valid_until should always be in the future
elpiel Jun 5, 2019
728dac8
adapter - Cargo.toml - add `chrono` and `time`
elpiel Jun 5, 2019
747edfd
domain - Asset - derive `PartialEq` & `Eq`
elpiel Jun 5, 2019
96789d8
domain - BigNum - impl `Add` & `Sub`
elpiel Jun 5, 2019
db07d26
adapter - Config and ConfigBuilder
elpiel Jun 5, 2019
748c30b
[+test] adapter - sanity - impl all the validation checks
elpiel Jun 5, 2019
9b6de2a
[test] adapter - sanity - fix minimum fee test
elpiel Jun 5, 2019
6847f8f
domain - validator - fixtures - optional `fee` argument
elpiel Jun 5, 2019
947f5fb
[test] adapter - sanity - SanityCheck correct channel validation test
elpiel Jun 5, 2019
29a7408
run `rustfmt`
elpiel Jun 5, 2019
e80e06f
adapter - Cargo.toml - add `hex`
elpiel Jun 6, 2019
e6d2fdd
adapter - add `sign` & `verify` + impl dummy `sign` & `verify`
elpiel Jun 6, 2019
733a345
[+test] adapter - dummy - `sign` & `verify` docs-tests + a unit test
elpiel Jun 6, 2019
c6f4b2d
adapter - Adapter - `get_auth` method:
elpiel Jun 6, 2019
6cda319
[+test] adapter - DummyAdapter - fix implementation of `get_auth`
elpiel Jun 6, 2019
d9c581e
adapter - DummyAdapter - move the success `get_auth` call to doc-test
elpiel Jun 6, 2019
c292e72
adapter - add doc-test attribute to enable the `dummy-adapter` feature
elpiel Jun 7, 2019
f7768b4
adapter - dummy - why does CI fails :(
elpiel Jun 7, 2019
0103245
adapter - add `futures-preview`
elpiel Jun 7, 2019
e135726
adapter - enable `async_await` & await_macro` features + add them to …
elpiel Jun 7, 2019
14e680e
adapter - `sign`, `verify` & `validate_channel` to `Future` based
elpiel Jun 7, 2019
2c2a288
adapter - `get_auth` to `Future` based
elpiel Jun 7, 2019
42f6d4a
Makefile.toml - Use the new env. variable for `clippy` arguments
elpiel Jun 10, 2019
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
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ env:
- CARGO_MAKE_RUN_CLIPPY="true"
matrix:
fast_finish: true
install:
- if [[ $(cargo list | grep -c make) == 0 ]]; then cargo install --debug cargo-make ; else echo "Cargo make already installed, continuing..."; fi
script:
- which cargo-make || cargo install cargo-make
- cargo make ci-flow
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]

members = [
"sentry",
"adapter",
"domain",
"sentry",
]
12 changes: 10 additions & 2 deletions Makefile.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
[env]
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = "true"
CARGO_MAKE_CLIPPY_ARGS = "--all-features -- -D warnings"

[tasks.clippy]
args = ["clippy", "--all-features", "--", "-D", "warnings"]
[tasks.dev-test-flow]
dependencies = [
"format-flow",
"clippy",
"pre-build",
"build",
"post-build",
"test-flow",
]
22 changes: 22 additions & 0 deletions adapter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "adapter"
version = "0.1.0"
authors = ["Lachezar Lechev <[email protected]>"]
edition = "2018"

[features]
# Allows you to use a Dummy implementation of the Adapter for testing purposes
dummy-adapter = []

[dependencies]
domain = {path = "../domain"}
# Futures
futures-preview = { version = "=0.3.0-alpha.16" }
# Time handling
chrono = "0.4"
time = "0.1.42"
# To/From Hex
# @TODO: Move this to the `dummy-adapter` feature if we use it only there!
hex = "0.3.2"
[dev-dependencies]
domain = {path = "../domain", features = ["fixtures"]}
96 changes: 96 additions & 0 deletions adapter/src/adapter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use domain::{Asset, BigNum, Channel};

use crate::sanity::SanityChecker;
use futures::{Future, FutureExt};
use std::pin::Pin;

pub type AdapterFuture<T> = Pin<Box<dyn Future<Output = Result<T, AdapterError>> + Send>>;

#[derive(Debug, Eq, PartialEq)]
pub enum AdapterError {
Authentication(String),
}

pub trait Adapter: SanityChecker {
fn config(&self) -> &Config;

fn validate_channel(&self, channel: &Channel) -> AdapterFuture<bool> {
futures::future::ok(Self::check(&self.config(), &channel).is_ok()).boxed()
}

/// Signs the provided state_root
fn sign(&self, state_root: &str) -> AdapterFuture<String>;

/// Verify, based on the signature & state_root, that the signer is the same
fn verify(&self, signer: &str, state_root: &str, signature: &str) -> AdapterFuture<bool>;

/// Gets authentication for specific validator
fn get_auth(&self, validator: &str) -> AdapterFuture<String>;
}

pub struct Config {
pub identity: String,
pub validators_whitelist: Vec<String>,
pub creators_whitelist: Vec<String>,
pub assets_whitelist: Vec<Asset>,
pub minimal_deposit: BigNum,
pub minimal_fee: BigNum,
}

pub struct ConfigBuilder {
identity: String,
validators_whitelist: Vec<String>,
creators_whitelist: Vec<String>,
assets_whitelist: Vec<Asset>,
minimal_deposit: BigNum,
minimal_fee: BigNum,
}

impl ConfigBuilder {
pub fn new(identity: &str) -> Self {
Self {
identity: identity.to_string(),
validators_whitelist: Vec::new(),
creators_whitelist: Vec::new(),
assets_whitelist: Vec::new(),
minimal_deposit: 0.into(),
minimal_fee: 0.into(),
}
}

pub fn set_validators_whitelist(mut self, validators: &[&str]) -> Self {
self.validators_whitelist = validators.iter().map(|slice| slice.to_string()).collect();
self
}

pub fn set_creators_whitelist(mut self, creators: &[&str]) -> Self {
self.creators_whitelist = creators.iter().map(|slice| slice.to_string()).collect();
self
}

pub fn set_assets_whitelist(mut self, assets: &[Asset]) -> Self {
self.assets_whitelist = assets.to_vec();
self
}

pub fn set_minimum_deposit(mut self, minimum: BigNum) -> Self {
self.minimal_deposit = minimum;
self
}

pub fn set_minimum_fee(mut self, minimum: BigNum) -> Self {
self.minimal_fee = minimum;
self
}

pub fn build(self) -> Config {
Config {
identity: self.identity,
validators_whitelist: self.validators_whitelist,
creators_whitelist: self.creators_whitelist,
assets_whitelist: self.assets_whitelist,
minimal_deposit: self.minimal_deposit,
minimal_fee: self.minimal_fee,
}
}
}
187 changes: 187 additions & 0 deletions adapter/src/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
use std::collections::HashMap;

use hex::encode;

use crate::adapter::{Adapter, AdapterError, AdapterFuture, Config};
use crate::sanity::SanityChecker;
use futures::future::{err, ok, FutureExt};

#[derive(Debug)]
pub struct DummyParticipant {
pub identity: String,
pub token: String,
}

pub struct DummyAdapter<'a> {
pub config: Config,
/// Dummy participants which will be used for
/// Creator, Validator Leader, Validator Follower and etc.
pub participants: HashMap<&'a str, DummyParticipant>,
}

impl SanityChecker for DummyAdapter<'_> {}

impl Adapter for DummyAdapter<'_> {
fn config(&self) -> &Config {
&self.config
}

/// Example:
///
/// ```
/// use adapter::{ConfigBuilder, Adapter};
/// use adapter::dummy::DummyAdapter;
/// use std::collections::HashMap;
///
/// futures::executor::block_on(async {
/// let config = ConfigBuilder::new("identity").build();
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
///
/// let actual = await!(adapter.sign("abcdefghijklmnopqrstuvwxyz012345")).unwrap();
/// let expected = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
/// assert_eq!(expected, &actual);
/// });
/// ```
fn sign(&self, state_root: &str) -> AdapterFuture<String> {
let signature = format!(
"Dummy adapter signature for {} by {}",
encode(&state_root),
&self.config.identity
);
ok(signature).boxed()
}

/// Example:
///
/// ```
/// use adapter::{ConfigBuilder, Adapter};
/// use adapter::dummy::DummyAdapter;
/// use std::collections::HashMap;
///
/// futures::executor::block_on(async {
/// let config = ConfigBuilder::new("identity").build();
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
///
/// let signature = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
/// assert_eq!(Ok(true), await!(adapter.verify("identity", "doesn't matter", signature)));
/// });
/// ```
fn verify(&self, signer: &str, _state_root: &str, signature: &str) -> AdapterFuture<bool> {
// select the `identity` and compare it to the signer
// for empty string this will return array with 1 element - an empty string `[""]`
let is_same = match signature.rsplit(' ').take(1).next() {
Some(from) => from == signer,
None => false,
};

ok(is_same).boxed()
}

/// Finds the auth. token in the HashMap of DummyParticipants if exists
///
/// Example:
///
/// ```
/// use std::collections::HashMap;
/// use adapter::dummy::{DummyParticipant, DummyAdapter};
/// use adapter::{ConfigBuilder, Adapter};
///
/// futures::executor::block_on(async {
/// let mut participants = HashMap::new();
/// participants.insert(
/// "identity_key",
/// DummyParticipant {
/// identity: "identity".to_string(),
/// token: "token".to_string(),
/// },
/// );
///
/// let adapter = DummyAdapter {
/// config: ConfigBuilder::new("identity").build(),
/// participants,
/// };
///
/// assert_eq!(Ok("token".to_string()), await!(adapter.get_auth("identity")));
/// });
/// ```
fn get_auth(&self, validator: &str) -> AdapterFuture<String> {
let participant = self
.participants
.iter()
.find(|&(_, participant)| participant.identity == validator);
let future = match participant {
Some((_, participant)) => ok(participant.token.to_string()),
None => err(AdapterError::Authentication(
"Identity not found".to_string(),
)),
};

future.boxed()
}
}

#[cfg(test)]
mod test {
use crate::adapter::ConfigBuilder;

use super::*;

#[test]
fn dummy_adapter_sings_state_root_and_verifies_it() {
futures::executor::block_on(async {
let config = ConfigBuilder::new("identity").build();
let adapter = DummyAdapter {
config,
participants: HashMap::new(),
};

let expected_signature = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
let actual_signature = await!(adapter.sign("abcdefghijklmnopqrstuvwxyz012345"))
.expect("Signing shouldn't fail");

assert_eq!(expected_signature, &actual_signature);

let is_verified =
await!(adapter.verify("identity", "doesn't matter", &actual_signature));

assert_eq!(Ok(true), is_verified);
});
}

#[test]
fn get_auth_with_empty_participators() {
futures::executor::block_on(async {
let adapter = DummyAdapter {
config: ConfigBuilder::new("identity").build(),
participants: HashMap::new(),
};

assert_eq!(
Err(AdapterError::Authentication(
"Identity not found".to_string()
)),
await!(adapter.get_auth("non-existing"))
);

let mut participants = HashMap::new();
participants.insert(
"identity_key",
DummyParticipant {
identity: "identity".to_string(),
token: "token".to_string(),
},
);
let adapter = DummyAdapter {
config: ConfigBuilder::new("identity").build(),
participants,
};

assert_eq!(
Err(AdapterError::Authentication(
"Identity not found".to_string()
)),
await!(adapter.get_auth("non-existing"))
);
});
}
}
12 changes: 12 additions & 0 deletions adapter/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(async_await, await_macro)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]
#![doc(test(attr(feature(async_await, await_macro))))]
#![doc(test(attr(cfg(feature = "dummy-adapter"))))]
pub use self::adapter::*;
pub use self::sanity::*;

mod adapter;
#[cfg(any(test, feature = "dummy-adapter"))]
pub mod dummy;
mod sanity;
Loading