Skip to content

Commit cb8bf4f

Browse files
authored
Issue #72 ValidationMessage (#76)
* validator - infra - sentry - fix some comments * domain - BalanceMap * domain - make `big_num` module consistent and register `balance_map` module * domain - validator - `Message` & `State` * adapter - `Adapter` should impl `State` as well + impl for `DummyAdapter` * adapter - dummy - hide doc-tests' `futures::executor::block_on` * adapter - references associated types for `verify` * domain - util - tests - past_datetime * domain - validator - message: - State - constraints - Message - move enum types to structs - `fixtures` module - add unimplemented fixtures * domain - validator - message - move to separate file + add RejectState * domain - util - tests - time - use `datetime_between` instead of `Faker` directly * domain - validator - message - fixtures - add simple fixtures * domain - validator - message - fix `serde` `rename_all` & add `RejectState` to `Message` * fix `clippy` warning
1 parent dba3051 commit cb8bf4f

File tree

10 files changed

+224
-44
lines changed

10 files changed

+224
-44
lines changed

adapter/src/adapter.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
use std::pin::Pin;
2+
3+
use futures::{Future, FutureExt};
4+
5+
use domain::validator::message::State;
16
use domain::{Asset, BigNum, Channel};
27

38
use crate::sanity::SanityChecker;
4-
use futures::{Future, FutureExt};
5-
use std::pin::Pin;
69

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

@@ -11,7 +14,7 @@ pub enum AdapterError {
1114
Authentication(String),
1215
}
1316

14-
pub trait Adapter: SanityChecker {
17+
pub trait Adapter: SanityChecker + State {
1518
fn config(&self) -> &Config;
1619

1720
fn validate_channel(&self, channel: &Channel) -> AdapterFuture<bool> {
@@ -22,7 +25,12 @@ pub trait Adapter: SanityChecker {
2225
fn sign(&self, state_root: &str) -> AdapterFuture<String>;
2326

2427
/// Verify, based on the signature & state_root, that the signer is the same
25-
fn verify(&self, signer: &str, state_root: &str, signature: &str) -> AdapterFuture<bool>;
28+
fn verify(
29+
&self,
30+
signer: &str,
31+
state_root: &Self::StateRoot,
32+
signature: &Self::Signature,
33+
) -> AdapterFuture<bool>;
2634

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

adapter/src/dummy.rs

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::collections::HashMap;
22

3+
use futures::future::{err, ok, FutureExt};
34
use hex::encode;
45

6+
use domain::validator::message::State;
7+
58
use crate::adapter::{Adapter, AdapterError, AdapterFuture, Config};
69
use crate::sanity::SanityChecker;
7-
use futures::future::{err, ok, FutureExt};
810

911
#[derive(Debug)]
1012
pub struct DummyParticipant {
@@ -21,26 +23,31 @@ pub struct DummyAdapter<'a> {
2123

2224
impl SanityChecker for DummyAdapter<'_> {}
2325

24-
impl Adapter for DummyAdapter<'_> {
26+
impl State for DummyAdapter<'_> {
27+
type Signature = String;
28+
type StateRoot = String;
29+
}
30+
31+
impl<'a> Adapter for DummyAdapter<'a> {
2532
fn config(&self) -> &Config {
2633
&self.config
2734
}
2835

2936
/// Example:
3037
///
3138
/// ```
39+
/// # futures::executor::block_on(async {
3240
/// use adapter::{ConfigBuilder, Adapter};
3341
/// use adapter::dummy::DummyAdapter;
3442
/// use std::collections::HashMap;
3543
///
36-
/// futures::executor::block_on(async {
37-
/// let config = ConfigBuilder::new("identity").build();
38-
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
44+
/// let config = ConfigBuilder::new("identity").build();
45+
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
3946
///
40-
/// let actual = await!(adapter.sign("abcdefghijklmnopqrstuvwxyz012345")).unwrap();
41-
/// let expected = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
42-
/// assert_eq!(expected, &actual);
43-
/// });
47+
/// let actual = await!(adapter.sign("abcdefghijklmnopqrstuvwxyz012345")).unwrap();
48+
/// let expected = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
49+
/// assert_eq!(expected, &actual);
50+
/// # });
4451
/// ```
4552
fn sign(&self, state_root: &str) -> AdapterFuture<String> {
4653
let signature = format!(
@@ -54,19 +61,24 @@ impl Adapter for DummyAdapter<'_> {
5461
/// Example:
5562
///
5663
/// ```
64+
/// # futures::executor::block_on(async {
5765
/// use adapter::{ConfigBuilder, Adapter};
5866
/// use adapter::dummy::DummyAdapter;
5967
/// use std::collections::HashMap;
6068
///
61-
/// futures::executor::block_on(async {
62-
/// let config = ConfigBuilder::new("identity").build();
63-
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
69+
/// let config = ConfigBuilder::new("identity").build();
70+
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
6471
///
65-
/// let signature = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
66-
/// assert_eq!(Ok(true), await!(adapter.verify("identity", "doesn't matter", signature)));
67-
/// });
72+
/// let signature = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
73+
/// assert_eq!(Ok(true), await!(adapter.verify("identity", &"doesn't matter".to_string(), &signature.to_string())));
74+
/// # });
6875
/// ```
69-
fn verify(&self, signer: &str, _state_root: &str, signature: &str) -> AdapterFuture<bool> {
76+
fn verify(
77+
&self,
78+
signer: &str,
79+
_state_root: &Self::StateRoot,
80+
signature: &Self::Signature,
81+
) -> AdapterFuture<bool> {
7082
// select the `identity` and compare it to the signer
7183
// for empty string this will return array with 1 element - an empty string `[""]`
7284
let is_same = match signature.rsplit(' ').take(1).next() {
@@ -82,27 +94,27 @@ impl Adapter for DummyAdapter<'_> {
8294
/// Example:
8395
///
8496
/// ```
97+
/// # futures::executor::block_on(async {
8598
/// use std::collections::HashMap;
8699
/// use adapter::dummy::{DummyParticipant, DummyAdapter};
87100
/// use adapter::{ConfigBuilder, Adapter};
88101
///
89-
/// futures::executor::block_on(async {
90-
/// let mut participants = HashMap::new();
91-
/// participants.insert(
92-
/// "identity_key",
93-
/// DummyParticipant {
94-
/// identity: "identity".to_string(),
95-
/// token: "token".to_string(),
96-
/// },
97-
/// );
102+
/// let mut participants = HashMap::new();
103+
/// participants.insert(
104+
/// "identity_key",
105+
/// DummyParticipant {
106+
/// identity: "identity".to_string(),
107+
/// token: "token".to_string(),
108+
/// },
109+
/// );
98110
///
99-
/// let adapter = DummyAdapter {
100-
/// config: ConfigBuilder::new("identity").build(),
101-
/// participants,
102-
/// };
111+
/// let adapter = DummyAdapter {
112+
/// config: ConfigBuilder::new("identity").build(),
113+
/// participants,
114+
/// };
103115
///
104-
/// assert_eq!(Ok("token".to_string()), await!(adapter.get_auth("identity")));
105-
/// });
116+
/// assert_eq!(Ok("token".to_string()), await!(adapter.get_auth("identity")));
117+
/// # });
106118
/// ```
107119
fn get_auth(&self, validator: &str) -> AdapterFuture<String> {
108120
let participant = self
@@ -141,8 +153,11 @@ mod test {
141153

142154
assert_eq!(expected_signature, &actual_signature);
143155

144-
let is_verified =
145-
await!(adapter.verify("identity", "doesn't matter", &actual_signature));
156+
let is_verified = await!(adapter.verify(
157+
"identity",
158+
&"doesn't matter".to_string(),
159+
&actual_signature.to_string()
160+
));
146161

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

domain/src/balances_map.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use std::collections::BTreeMap;
2+
3+
use crate::BigNum;
4+
5+
pub type BalancesMap = BTreeMap<String, BigNum>;
File renamed without changes.

domain/src/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use hex::FromHex;
77
use serde::{Deserialize, Serialize};
88
use serde_hex::{SerHex, StrictPfx};
99

10-
use crate::bignum::BigNum;
10+
use crate::big_num::BigNum;
1111
use crate::util::serde::ts_milliseconds_option;
1212
use crate::{AdUnit, Asset, DomainError, EventSubmission, TargetingTag, ValidatorDesc};
1313

domain/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ pub use util::tests as test_util;
88

99
pub use self::ad_unit::AdUnit;
1010
pub use self::asset::Asset;
11-
pub use self::bignum::BigNum;
11+
pub use self::balances_map::BalancesMap;
12+
pub use self::big_num::BigNum;
1213
pub use self::channel::{Channel, ChannelId, ChannelSpec, SpecValidator, SpecValidators};
1314
pub use self::event_submission::EventSubmission;
1415
#[cfg(feature = "repositories")]
@@ -18,7 +19,8 @@ pub use self::validator::ValidatorDesc;
1819

1920
pub mod ad_unit;
2021
pub mod asset;
21-
pub mod bignum;
22+
pub mod balances_map;
23+
pub mod big_num;
2224
pub mod channel;
2325
pub mod event_submission;
2426
pub mod targeting_tag;

domain/src/util/tests/time.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use fake::faker::Chrono;
33
use fake::Faker;
44
use time::Duration;
55

6-
/// Creates a DatTime<Utc> between two dates. If `to` is not provided it will use
6+
/// Creates a DateTime<Utc> between two dates. If `to` is not provided it will use
77
/// `Now + 365 days`.
88
///
99
pub fn datetime_between(from: &DateTime<Utc>, to: Option<&DateTime<Utc>>) -> DateTime<Utc> {
@@ -13,3 +13,17 @@ pub fn datetime_between(from: &DateTime<Utc>, to: Option<&DateTime<Utc>>) -> Dat
1313
.parse()
1414
.expect("Whoops, DateTime<Utc> should be created from Fake...")
1515
}
16+
17+
/// Creates a DateTime<Utc> in the past between `from` and `Now - 1 sec`.
18+
/// If `from` is not provided it will use `-1 week`
19+
///
20+
pub fn past_datetime(from: Option<&DateTime<Utc>>) -> DateTime<Utc> {
21+
// make sure that we always generate DateTimes in the past, so use `Now - 1 sec`
22+
let to = Utc::now() - Duration::seconds(1);
23+
24+
let default_from = Utc::now() - Duration::weeks(1);
25+
26+
let from = from.unwrap_or(&default_from);
27+
28+
datetime_between(&from, Some(&to))
29+
}

domain/src/validator.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use serde::{Deserialize, Serialize};
22

3+
pub use message::Message;
4+
35
use crate::BigNum;
46

7+
pub mod message;
8+
59
#[derive(Serialize, Deserialize, Debug, Clone)]
610
#[serde(rename_all = "camelCase")]
711
pub struct ValidatorDesc {

domain/src/validator/message.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use chrono::{DateTime, Utc};
2+
use serde::de::DeserializeOwned;
3+
use serde::export::fmt::Debug;
4+
use serde::{Deserialize, Serialize};
5+
6+
use crate::BalancesMap;
7+
8+
pub trait State {
9+
type Signature: DeserializeOwned + Serialize + Debug;
10+
type StateRoot: DeserializeOwned + Serialize + Debug;
11+
}
12+
13+
#[derive(Serialize, Deserialize, Debug)]
14+
#[serde(tag = "type")]
15+
pub enum Message<S: State> {
16+
ApproveState(ApproveState<S>),
17+
NewState(NewState<S>),
18+
RejectState(RejectState),
19+
Heartbeat(Heartbeat<S>),
20+
Accounting(Accounting),
21+
}
22+
23+
#[derive(Serialize, Deserialize, Debug)]
24+
#[serde(rename_all = "camelCase")]
25+
pub struct ApproveState<S: State> {
26+
state_root: S::StateRoot,
27+
signature: S::Signature,
28+
is_healthy: bool,
29+
}
30+
31+
#[derive(Serialize, Deserialize, Debug)]
32+
#[serde(rename_all = "camelCase")]
33+
pub struct NewState<S: State> {
34+
state_root: S::StateRoot,
35+
signature: S::Signature,
36+
balances: BalancesMap,
37+
}
38+
39+
#[derive(Serialize, Deserialize, Debug)]
40+
#[serde(rename_all = "camelCase")]
41+
pub struct RejectState {
42+
reason: String,
43+
}
44+
45+
#[derive(Serialize, Deserialize, Debug)]
46+
#[serde(rename_all = "camelCase")]
47+
pub struct Heartbeat<S: State> {
48+
signature: S::Signature,
49+
timestamp: DateTime<Utc>,
50+
}
51+
52+
#[derive(Serialize, Deserialize, Debug)]
53+
#[serde(rename_all = "camelCase")]
54+
pub struct Accounting {
55+
#[serde(rename = "last_ev_aggr")]
56+
last_event_aggregate: DateTime<Utc>,
57+
#[serde(rename = "balances_pre_fees")]
58+
pre_fees: BalancesMap,
59+
balances: BalancesMap,
60+
}
61+
62+
#[cfg(any(test, feature = "fixtures"))]
63+
pub mod fixtures {
64+
use fake::faker::*;
65+
66+
use crate::test_util::time::past_datetime;
67+
68+
use super::*;
69+
70+
#[derive(Serialize, Deserialize, Debug)]
71+
pub struct DummyState {}
72+
73+
impl State for DummyState {
74+
type Signature = String;
75+
type StateRoot = String;
76+
}
77+
78+
pub fn get_approve_state<S: State>(
79+
state_root: S::StateRoot,
80+
signature: S::Signature,
81+
is_healthy: bool,
82+
) -> ApproveState<S> {
83+
ApproveState {
84+
state_root,
85+
signature,
86+
is_healthy,
87+
}
88+
}
89+
90+
pub fn get_new_state<S: State>(
91+
state_root: S::StateRoot,
92+
signature: S::Signature,
93+
balances: BalancesMap,
94+
) -> NewState<S> {
95+
NewState {
96+
state_root,
97+
signature,
98+
balances,
99+
}
100+
}
101+
102+
pub fn get_reject_state(reason: Option<String>) -> RejectState {
103+
RejectState {
104+
reason: reason.unwrap_or_else(|| <Faker as Lorem>::sentence(5, 4)),
105+
}
106+
}
107+
108+
pub fn get_heartbeat<S: State>(signature: S::Signature) -> Heartbeat<S> {
109+
Heartbeat {
110+
signature,
111+
timestamp: past_datetime(None),
112+
}
113+
}
114+
115+
pub fn get_accounting(
116+
balances: BalancesMap,
117+
pre_fees: Option<BalancesMap>,
118+
last_ev_aggr: Option<DateTime<Utc>>,
119+
) -> Accounting {
120+
let last_event_aggregate = last_ev_aggr.unwrap_or_else(|| past_datetime(None));
121+
assert!(
122+
last_event_aggregate < Utc::now(),
123+
"You cannot have a last_event_aggregate < Now"
124+
);
125+
126+
Accounting {
127+
last_event_aggregate,
128+
pre_fees: pre_fees.unwrap_or_else(BalancesMap::default),
129+
balances,
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)