Skip to content

Commit 0c0e322

Browse files
authored
Issue #78 heartbeat service (#89)
* domain - re-export ValidatorId & impl Error for RepositoryError * validator - `allow(clippy::needless_lifetimes)` * domain - validator - impl `TryFrom<&str>` for `ValidatorId` * validator - application - heartbeat - make it compile with most of the things done * validator - application - HeartbeatSender: - HeartbeatError::User - remove panic! and use the User error * adapter - Cargo.toml - `dummy-adapter` feature: - add `serde` - move `hex` inside * adapter - signable_state_root: - ChannelId, BalanceRoot and signable_state_root assoc fn - Dummy - DummySignature & DummyStateRoot - Dummy - Hexable trait - Dummy - impl Hexable for ChannelId, BalanceRoot & DummyStateRoot - * validator - State - Singature & StateRoot: - use Display instead of Debug * domain - channel - ChannelId - `id` to `bytes` field * validator - applicaiton - heartbeat - use `signable_state_root` * validator - application - heartbeat - handle if it's time for Heartbeat
1 parent 9c37e21 commit 0c0e322

File tree

12 files changed

+232
-58
lines changed

12 files changed

+232
-58
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

adapter/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ edition = "2018"
66

77
[features]
88
# Allows you to use a Dummy implementation of the Adapter for testing purposes
9-
dummy-adapter = []
9+
dummy-adapter = ["serde", "hex"]
1010

1111
[dependencies]
1212
domain = {path = "../domain"}
1313
# Futures
14-
futures-preview = { version = "=0.3.0-alpha.16" }
14+
futures-preview = {version = "=0.3.0-alpha.16"}
1515
# Time handling
1616
chrono = "0.4"
1717
time = "0.1.42"
1818
# To/From Hex
19-
# @TODO: Move this to the `dummy-adapter` feature if we use it only there!
20-
hex = "0.3.2"
19+
hex = {version = "0.3.2", optional = true}
20+
serde = {version = "^1.0", features = ['derive'], optional = true}
2121
[dev-dependencies]
2222
domain = {path = "../domain", features = ["fixtures"]}

adapter/src/adapter.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ use crate::sanity::SanityChecker;
99
use std::error::Error;
1010
use std::fmt;
1111

12+
pub struct ChannelId(pub [u8; 32]);
13+
impl AsRef<[u8]> for ChannelId {
14+
fn as_ref(&self) -> &[u8] {
15+
&self.0
16+
}
17+
}
18+
19+
pub struct BalanceRoot(pub [u8; 32]);
20+
impl AsRef<[u8]> for BalanceRoot {
21+
fn as_ref(&self) -> &[u8] {
22+
&self.0
23+
}
24+
}
25+
26+
pub struct SignableStateRoot<T: fmt::Display>(pub T);
27+
1228
pub type AdapterFuture<T> = Pin<Box<dyn Future<Output = Result<T, AdapterError>> + Send>>;
1329

1430
#[derive(Debug, Eq, PartialEq)]
@@ -46,6 +62,11 @@ pub trait Adapter: SanityChecker + State {
4662

4763
/// Gets authentication for specific validator
4864
fn get_auth(&self, validator: &str) -> AdapterFuture<String>;
65+
66+
fn signable_state_root(
67+
channel_id: ChannelId,
68+
balance_root: BalanceRoot,
69+
) -> SignableStateRoot<Self::StateRoot>;
4970
}
5071

5172
pub struct Config {

adapter/src/dummy.rs

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,68 @@
1-
use std::collections::HashMap;
1+
use crate::adapter::{
2+
Adapter, AdapterError, AdapterFuture, BalanceRoot, ChannelId, Config, SignableStateRoot,
3+
};
4+
use crate::sanity::SanityChecker;
25

36
use futures::future::{err, ok, FutureExt};
47
use hex::encode;
8+
use serde::{Deserialize, Serialize};
9+
use std::collections::HashMap;
10+
use std::fmt;
511

612
use domain::validator::message::State;
713

8-
use crate::adapter::{Adapter, AdapterError, AdapterFuture, Config};
9-
use crate::sanity::SanityChecker;
10-
1114
#[derive(Debug)]
1215
pub struct DummyParticipant {
1316
pub identity: String,
1417
pub token: String,
1518
}
1619

20+
pub trait Hexable: AsRef<[u8]> {
21+
fn to_hex(&self) -> String {
22+
format!("0x{}", encode(&self))
23+
}
24+
}
25+
26+
impl Hexable for ChannelId {}
27+
impl Hexable for BalanceRoot {}
28+
29+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
30+
pub struct DummySignature(pub String);
31+
32+
impl fmt::Display for DummySignature {
33+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34+
write!(f, "{}", self.0)
35+
}
36+
}
37+
38+
impl<S: Into<String>> From<S> for DummySignature {
39+
fn from(value: S) -> Self {
40+
Self(value.into())
41+
}
42+
}
43+
44+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
45+
pub struct DummyStateRoot(pub String);
46+
47+
impl fmt::Display for DummyStateRoot {
48+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49+
write!(f, "{}", self.0)
50+
}
51+
}
52+
53+
impl<S: Into<String>> From<S> for DummyStateRoot {
54+
fn from(value: S) -> Self {
55+
Self(value.into())
56+
}
57+
}
58+
59+
impl AsRef<[u8]> for DummyStateRoot {
60+
fn as_ref(&self) -> &[u8] {
61+
&self.0.as_ref()
62+
}
63+
}
64+
impl Hexable for DummyStateRoot {}
65+
1766
pub struct DummyAdapter<'a> {
1867
pub config: Config,
1968
/// Dummy participants which will be used for
@@ -24,8 +73,8 @@ pub struct DummyAdapter<'a> {
2473
impl SanityChecker for DummyAdapter<'_> {}
2574

2675
impl State for DummyAdapter<'_> {
27-
type Signature = String;
28-
type StateRoot = String;
76+
type Signature = DummySignature;
77+
type StateRoot = DummyStateRoot;
2978
}
3079

3180
impl<'a> Adapter for DummyAdapter<'a> {
@@ -38,24 +87,24 @@ impl<'a> Adapter for DummyAdapter<'a> {
3887
/// ```
3988
/// # futures::executor::block_on(async {
4089
/// use adapter::{ConfigBuilder, Adapter};
41-
/// use adapter::dummy::DummyAdapter;
90+
/// use adapter::dummy::{DummyAdapter, DummySignature};
4291
/// use std::collections::HashMap;
4392
///
4493
/// let config = ConfigBuilder::new("identity").build();
4594
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
4695
///
47-
/// let actual = await!(adapter.sign(&"abcdefghijklmnopqrstuvwxyz012345".to_string())).unwrap();
48-
/// let expected = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
49-
/// assert_eq!(expected, &actual);
96+
/// let actual = await!(adapter.sign(&"abcdefghijklmnopqrstuvwxyz012345".into())).unwrap();
97+
/// let expected = "Dummy adapter signature for 0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
98+
/// assert_eq!(DummySignature::from(expected), actual);
5099
/// # });
51100
/// ```
52101
fn sign(&self, state_root: &Self::StateRoot) -> AdapterFuture<Self::Signature> {
53102
let signature = format!(
54103
"Dummy adapter signature for {} by {}",
55-
encode(&state_root),
104+
state_root.to_hex(),
56105
&self.config.identity
57106
);
58-
ok(signature).boxed()
107+
ok(signature.into()).boxed()
59108
}
60109

61110
/// Example:
@@ -69,8 +118,8 @@ impl<'a> Adapter for DummyAdapter<'a> {
69118
/// let config = ConfigBuilder::new("identity").build();
70119
/// let adapter = DummyAdapter { config, participants: HashMap::new() };
71120
///
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())));
121+
/// let signature = "Dummy adapter signature for 0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
122+
/// assert_eq!(Ok(true), await!(adapter.verify("identity", &"doesn't matter".into(), &signature.into())));
74123
/// # });
75124
/// ```
76125
fn verify(
@@ -81,7 +130,7 @@ impl<'a> Adapter for DummyAdapter<'a> {
81130
) -> AdapterFuture<bool> {
82131
// select the `identity` and compare it to the signer
83132
// for empty string this will return array with 1 element - an empty string `[""]`
84-
let is_same = match signature.rsplit(' ').take(1).next() {
133+
let is_same = match signature.0.rsplit(' ').take(1).next() {
85134
Some(from) => from == signer,
86135
None => false,
87136
};
@@ -130,6 +179,19 @@ impl<'a> Adapter for DummyAdapter<'a> {
130179

131180
future.boxed()
132181
}
182+
183+
fn signable_state_root(
184+
channel_id: ChannelId,
185+
balance_root: BalanceRoot,
186+
) -> SignableStateRoot<Self::StateRoot> {
187+
let state_root = format!(
188+
"Signable State Root for Adapter channel id {} with balance root {}",
189+
channel_id.to_hex(),
190+
balance_root.to_hex()
191+
);
192+
193+
SignableStateRoot(state_root.into())
194+
}
133195
}
134196

135197
#[cfg(test)]
@@ -147,17 +209,16 @@ mod test {
147209
participants: HashMap::new(),
148210
};
149211

150-
let expected_signature = "Dummy adapter signature for 6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
151-
let actual_signature =
152-
await!(adapter.sign(&"abcdefghijklmnopqrstuvwxyz012345".to_string()))
153-
.expect("Signing shouldn't fail");
212+
let expected_signature = "Dummy adapter signature for 0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435 by identity";
213+
let actual_signature = await!(adapter.sign(&"abcdefghijklmnopqrstuvwxyz012345".into()))
214+
.expect("Signing shouldn't fail");
154215

155-
assert_eq!(expected_signature, &actual_signature);
216+
assert_eq!(DummySignature::from(expected_signature), actual_signature);
156217

157218
let is_verified = await!(adapter.verify(
158219
"identity",
159-
&"doesn't matter".to_string(),
160-
&actual_signature.to_string()
220+
&"doesn't matter".into(),
221+
&actual_signature.into()
161222
));
162223

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

domain/src/channel.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{AdUnit, Asset, DomainError, EventSubmission, TargetingTag, Validator
1515
#[serde(transparent)]
1616
pub struct ChannelId {
1717
#[serde(with = "SerHex::<StrictPfx>")]
18-
pub id: [u8; 32],
18+
pub bytes: [u8; 32],
1919
}
2020

2121
impl fmt::Display for ChannelId {
@@ -31,7 +31,7 @@ impl fmt::Display for ChannelId {
3131
/// assert_eq!("0x061d5e2a67d0a9a10f1c732bca12a676d83f79663a396f7d87b3e30b9b411088", &channel_hex_string);
3232
/// ```
3333
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
34-
let hex_string = SerHex::<StrictPfx>::into_hex(&self.id).unwrap();
34+
let hex_string = SerHex::<StrictPfx>::into_hex(&self.bytes).unwrap();
3535
write!(f, "{}", hex_string)
3636
}
3737
}
@@ -47,9 +47,9 @@ impl TryFrom<&str> for ChannelId {
4747
/// use std::convert::TryFrom;
4848
/// use domain::channel::ChannelId;
4949
///
50-
/// let bytes: [u8; 32] = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50];
50+
/// let bytes: [u8; 32] = *b"12345678901234567890123456789012";
5151
///
52-
/// assert_eq!(ChannelId { id: bytes }, ChannelId::try_from("12345678901234567890123456789012").unwrap())
52+
/// assert_eq!(ChannelId { bytes }, ChannelId::try_from("12345678901234567890123456789012").unwrap())
5353
/// ```
5454
fn try_from(value: &str) -> Result<Self, Self::Error> {
5555
let bytes = value.as_bytes();
@@ -61,7 +61,7 @@ impl TryFrom<&str> for ChannelId {
6161
let mut id = [0; 32];
6262
id.copy_from_slice(&bytes[..32]);
6363

64-
Ok(Self { id })
64+
Ok(Self { bytes: id })
6565
}
6666
}
6767

@@ -78,7 +78,7 @@ impl ChannelId {
7878
///
7979
/// let from_hex = domain::ChannelId::try_from_hex(hex_string).expect("This should be valid hex string");
8080
///
81-
/// let expected_channel_id = ChannelId{ id: [6, 29, 94, 42, 103, 208, 169, 161, 15, 28, 115, 43, 202, 18, 166, 118, 216, 63, 121, 102, 58, 57, 111, 125, 135, 179, 227, 11, 155, 65, 16, 136]};
81+
/// let expected_channel_id = ChannelId{ bytes: [6, 29, 94, 42, 103, 208, 169, 161, 15, 28, 115, 43, 202, 18, 166, 118, 216, 63, 121, 102, 58, 57, 111, 125, 135, 179, 227, 11, 155, 65, 16, 136]};
8282
/// assert_eq!(expected_channel_id, from_hex)
8383
/// ```
8484
pub fn try_from_hex(hex: &str) -> Result<Self, DomainError> {
@@ -95,13 +95,13 @@ impl ChannelId {
9595
let mut id = [0; 32];
9696
id.copy_from_slice(&bytes[..32]);
9797

98-
Ok(Self { id })
98+
Ok(Self { bytes: id })
9999
}
100100
}
101101

102102
impl PartialEq<ChannelId> for &str {
103103
fn eq(&self, channel_id: &ChannelId) -> bool {
104-
self.as_bytes() == channel_id.id
104+
self.as_bytes() == channel_id.bytes
105105
}
106106
}
107107

domain/src/channel_fixtures.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub fn get_channel_id(channel_id: &str) -> ChannelId {
2222
*byte = channel_id_bytes[index];
2323
}
2424

25-
ChannelId { id }
25+
ChannelId { bytes: id }
2626
}
2727

2828
pub fn get_channel(

domain/src/channel_test.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ fn coverts_str_to_channel_id() {
77
let channel_id = ChannelId::try_from("12345678901234567890123456789012")
88
.expect("Should create ChannelId from &str with 32 len numeric value");
99

10-
assert_eq!(
11-
"12345678901234567890123456789012".as_bytes(),
12-
&channel_id.id
13-
);
10+
assert_eq!(b"12345678901234567890123456789012", &channel_id.bytes);
1411

1512
assert!(
1613
ChannelId::try_from("1234567890123456789012345678901").is_err(),

domain/src/lib.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub use self::event_submission::EventSubmission;
1515
#[cfg(feature = "repositories")]
1616
pub use self::repository::*;
1717
pub use self::targeting_tag::TargetingTag;
18-
pub use self::validator::ValidatorDesc;
18+
pub use self::validator::{ValidatorDesc, ValidatorId};
1919

2020
pub mod ad_unit;
2121
pub mod asset;
@@ -58,6 +58,8 @@ pub mod repository {
5858
use std::pin::Pin;
5959

6060
use futures::Future;
61+
use std::error::Error;
62+
use std::fmt;
6163

6264
pub trait IOError: std::error::Error + Send {}
6365

@@ -70,5 +72,16 @@ pub mod repository {
7072
User,
7173
}
7274

75+
impl Error for RepositoryError {}
76+
77+
impl fmt::Display for RepositoryError {
78+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79+
match self {
80+
RepositoryError::User => write!(f, "User error: TODO"),
81+
RepositoryError::IO(error) => write!(f, "IO error: {}", error),
82+
}
83+
}
84+
}
85+
7386
pub type RepositoryFuture<T> = Pin<Box<dyn Future<Output = Result<T, RepositoryError>> + Send>>;
7487
}

domain/src/validator.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1+
use std::convert::TryFrom;
2+
13
use serde::{Deserialize, Serialize};
24

35
pub use message::Message;
46

5-
use crate::BigNum;
7+
use crate::{BigNum, DomainError};
68

79
pub mod message;
810

911
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
1012
#[serde(transparent)]
1113
pub struct ValidatorId(String);
1214

15+
impl TryFrom<&str> for ValidatorId {
16+
type Error = DomainError;
17+
18+
fn try_from(value: &str) -> Result<Self, Self::Error> {
19+
// @TODO: Should we have some constrains(like valid hex string starting with `0x`)? If not this should be just `From`.
20+
Ok(Self(value.to_string()))
21+
}
22+
}
23+
1324
#[derive(Serialize, Deserialize, Debug, Clone)]
1425
#[serde(rename_all = "camelCase")]
1526
pub struct ValidatorDesc {

0 commit comments

Comments
 (0)