Skip to content

Commit 0c216c9

Browse files
authored
Merge pull request #271 from samparsky/fix-route-discrepancies
Fix route discrepancies
2 parents e8dd5c0 + 2a17bd5 commit 0c216c9

File tree

13 files changed

+281
-68
lines changed

13 files changed

+281
-68
lines changed

adapter/src/ethereum.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,8 +472,8 @@ mod test {
472472
let expected_response =
473473
"0x625fd46f82c4cfd135ea6a8534e85dbf50beb157046dce59d2e97aacdf4e38381d1513c0e6f002b2f05c05458038b187754ff38cc0658dfc9ba854cccfb6e13e1b";
474474
let message = "2bdeafae53940669daa6f519373f686c";
475-
let response = eth_adapter.sign(message).expect("failed to sign message");
476-
assert_eq!(expected_response, response, "invalid signature");
475+
let signature = eth_adapter.sign(message).expect("failed to sign message");
476+
assert_eq!(expected_response, signature, "invalid signature");
477477

478478
// Verify
479479
let signature =
@@ -487,6 +487,18 @@ mod test {
487487
)
488488
.expect("Failed to verify signatures");
489489

490+
let sig1 = "0x9fa5852041b9818021323aff8260624fd6998c52c95d9ad5036e0db6f2bf2b2d48a188ec1d638581ff56b0a2ecceca6d3880fc65030558bd8f68b154e7ebf80f1b";
491+
let msg = "1648231285e69677531ffe70719f67a07f3d4393b8425a5a1c84b0c72434c77b";
492+
493+
let verify2 = eth_adapter
494+
.verify(
495+
&ValidatorId::try_from("ce07CbB7e054514D590a0262C93070D838bFBA2e")
496+
.expect("Failed to parse id"),
497+
msg,
498+
&sig1,
499+
)
500+
.expect("Failed to verify signatures");
501+
490502
assert_eq!(verify, true, "invalid signature verification");
491503
}
492504

primitives/src/adapter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl fmt::Display for AdapterError {
3131
AdapterError::Authorization(error) => write!(f, "Authorization error: {}", error),
3232
AdapterError::Configuration(error) => write!(f, "Configuration error: {}", error),
3333
AdapterError::Signature(error) => write!(f, "Signature error: {}", error),
34-
AdapterError::InvalidChannel(error) => write!(f, "Invalid Channel error: {}", error),
34+
AdapterError::InvalidChannel(error) => write!(f, "{}", error),
3535
AdapterError::Failed(error) => write!(f, "error: {}", error),
3636
}
3737
}

primitives/src/channel.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33

44
use chrono::serde::{ts_milliseconds, ts_milliseconds_option, ts_seconds};
55
use chrono::{DateTime, Utc};
6-
use serde::{Deserialize, Serialize};
6+
use serde::{Deserialize, Deserializer, Serialize};
77
use serde_hex::{SerHex, StrictPfx};
88

99
use crate::big_num::BigNum;
@@ -13,7 +13,25 @@ use std::ops::Deref;
1313

1414
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Copy, Clone, Hash)]
1515
#[serde(transparent)]
16-
pub struct ChannelId(#[serde(with = "SerHex::<StrictPfx>")] [u8; 32]);
16+
pub struct ChannelId(
17+
#[serde(
18+
deserialize_with = "channel_id_from_str",
19+
serialize_with = "SerHex::<StrictPfx>::serialize"
20+
)]
21+
[u8; 32],
22+
);
23+
24+
fn channel_id_from_str<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
25+
where
26+
D: Deserializer<'de>,
27+
{
28+
let channel_id = String::deserialize(deserializer)?;
29+
if channel_id.is_empty() || channel_id.len() != 66 {
30+
return Err(serde::de::Error::custom("invalid channel id".to_string()));
31+
}
32+
33+
<[u8; 32] as FromHex>::from_hex(&channel_id[2..]).map_err(serde::de::Error::custom)
34+
}
1735

1836
impl Deref for ChannelId {
1937
type Target = [u8];
@@ -72,7 +90,9 @@ pub struct Pricing {
7290
#[derive(Serialize, Deserialize, Debug, Clone)]
7391
#[serde(rename_all = "UPPERCASE")]
7492
pub struct PricingBounds {
93+
#[serde(default, skip_serializing_if = "Option::is_none")]
7594
pub impression: Option<Pricing>,
95+
#[serde(default, skip_serializing_if = "Option::is_none")]
7696
pub click: Option<Pricing>,
7797
}
7898

@@ -86,7 +106,8 @@ pub struct ChannelSpec {
86106
pub max_per_impression: BigNum,
87107
/// Minimum payment offered per impression
88108
pub min_per_impression: BigNum,
89-
// Event pricing bounds
109+
/// Event pricing bounds
110+
#[serde(default, skip_serializing_if = "Option::is_none")]
90111
pub pricing_bounds: Option<PricingBounds>,
91112
/// An array of TargetingTag (optional)
92113
#[serde(default, skip_serializing_if = "Vec::is_empty")]
@@ -95,6 +116,7 @@ pub struct ChannelSpec {
95116
#[serde(default, skip_serializing_if = "Option::is_none")]
96117
pub min_targeting_score: Option<f64>,
97118
/// EventSubmission object, applies to event submission (POST /channel/:id/events)
119+
#[serde(default, skip_serializing_if = "Option::is_none")]
98120
pub event_submission: Option<EventSubmission>,
99121
/// A millisecond timestamp of when the campaign was created
100122
#[serde(
@@ -112,6 +134,7 @@ pub struct ChannelSpec {
112134
)]
113135
pub active_from: Option<DateTime<Utc>>,
114136
/// A random number to ensure the campaignSpec hash is unique
137+
#[serde(default, skip_serializing_if = "Option::is_none")]
115138
pub nonce: Option<BigNum>,
116139
/// A millisecond timestamp of when the campaign should enter a withdraw period
117140
/// (no longer accept any events other than CHANNEL_CLOSE)
@@ -232,17 +255,34 @@ pub enum ChannelError {
232255
/// which in terms means, that the adapter shouldn't handle this Channel
233256
AdapterNotIncluded,
234257
/// when `channel.valid_until` has passed (< now), the channel should be handled
235-
PassedValidUntil,
258+
InvalidValidUntil(String),
236259
UnlistedValidator,
237260
UnlistedCreator,
238261
UnlistedAsset,
239262
MinimumDepositNotMet,
240263
MinimumValidatorFeeNotMet,
264+
FeeConstraintViolated,
241265
}
242266

243267
impl fmt::Display for ChannelError {
244268
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245-
write!(f, "Channel error",)
269+
match self {
270+
ChannelError::InvalidArgument(error) => write!(f, "{}", error),
271+
ChannelError::AdapterNotIncluded => write!(f, "channel is not validated by us"),
272+
ChannelError::InvalidValidUntil(error) => write!(f, "{}", error),
273+
ChannelError::UnlistedValidator => write!(f, "validators are not in the whitelist"),
274+
ChannelError::UnlistedCreator => write!(f, "channel.creator is not whitelisted"),
275+
ChannelError::UnlistedAsset => write!(f, "channel.depositAsset is not whitelisted"),
276+
ChannelError::MinimumDepositNotMet => {
277+
write!(f, "channel.depositAmount is less than MINIMAL_DEPOSIT")
278+
}
279+
ChannelError::MinimumValidatorFeeNotMet => {
280+
write!(f, "channel validator fee is less than MINIMAL_FEE")
281+
}
282+
ChannelError::FeeConstraintViolated => {
283+
write!(f, "total fees <= deposit: fee constraint violated")
284+
}
285+
}
246286
}
247287
}
248288

primitives/src/channel_validator.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use crate::channel::{Channel, ChannelError, SpecValidator, SpecValidators};
22
use crate::config::Config;
3+
use crate::BigNum;
34
use crate::ValidatorId;
45
use chrono::Utc;
56
use std::cmp::PartialEq;
7+
use time::Duration;
68

79
pub trait ChannelValidator {
810
fn is_channel_valid(
@@ -17,7 +19,21 @@ pub trait ChannelValidator {
1719
};
1820

1921
if channel.valid_until < Utc::now() {
20-
return Err(ChannelError::PassedValidUntil);
22+
return Err(ChannelError::InvalidValidUntil(
23+
"channel.validUntil has passed".to_string(),
24+
));
25+
}
26+
27+
if channel.valid_until > (Utc::now() + Duration::days(365)) {
28+
return Err(ChannelError::InvalidValidUntil(
29+
"channel.validUntil should not be greater than one year".to_string(),
30+
));
31+
}
32+
33+
if channel.spec.withdraw_period_start > channel.valid_until {
34+
return Err(ChannelError::InvalidValidUntil(
35+
"channel withdrawPeriodStart is invalid".to_string(),
36+
));
2137
}
2238

2339
if !all_validators_listed(&channel.spec.validators, &config.validators_whitelist) {
@@ -40,6 +56,17 @@ pub trait ChannelValidator {
4056
return Err(ChannelError::MinimumValidatorFeeNotMet);
4157
}
4258

59+
let total_validator_fee: BigNum = channel
60+
.spec
61+
.validators
62+
.iter()
63+
.map(|v| v.fee.clone())
64+
.fold(BigNum::from(0), |acc, x| acc + x);
65+
66+
if total_validator_fee >= channel.deposit_amount {
67+
return Err(ChannelError::FeeConstraintViolated);
68+
}
69+
4370
Ok(())
4471
}
4572
}

primitives/src/sentry.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ pub struct EventAggregateResponse {
160160
pub events: Vec<EventAggregate>,
161161
}
162162

163+
#[derive(Serialize, Deserialize, Debug)]
164+
#[serde(rename_all = "camelCase")]
165+
pub struct ValidationErrorResponse {
166+
pub status_code: u64,
167+
pub message: String,
168+
pub validation: Vec<String>,
169+
}
170+
163171
#[derive(Serialize, Deserialize)]
164172
#[serde(rename_all = "camelCase")]
165173
pub struct AdvancedAnalyticsResponse {

primitives/src/util/tests/prep_db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ lazy_static! {
3636
auth.insert("user".into(), "x8c9v1b2".into());
3737
auth.insert("publisher".into(), "testing".into());
3838
auth.insert("publisher2".into(), "testing2".into());
39-
auth.insert("creator".into(), "0x033ed90e0fec3f3ea1c9b005c724d704501e0196".into());
39+
auth.insert("creator".into(), "0x033Ed90e0FeC3F3ea1C9b005C724D704501e0196".into());
4040
auth.insert("tester".into(), "AUTH_awesomeTester".into());
4141

4242
auth

primitives/src/validator.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use chrono::{DateTime, Utc};
2-
use serde::{Deserialize, Serialize, Serializer};
3-
use serde_hex::{SerHex, StrictPfx};
2+
use hex::FromHex;
3+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
44
use std::fmt;
55

66
use crate::{BalancesMap, BigNum, DomainError, ToETHChecksum};
@@ -16,7 +16,27 @@ pub enum ValidatorError {
1616

1717
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1818
#[serde(transparent)]
19-
pub struct ValidatorId(#[serde(with = "SerHex::<StrictPfx>")] [u8; 20]);
19+
pub struct ValidatorId(
20+
#[serde(
21+
deserialize_with = "validator_id_from_str",
22+
serialize_with = "SerHex::<StrictPfx>::serialize"
23+
)]
24+
[u8; 20],
25+
);
26+
27+
fn validator_id_from_str<'de, D>(deserializer: D) -> Result<[u8; 20], D::Error>
28+
where
29+
D: Deserializer<'de>,
30+
{
31+
let validator_id = String::deserialize(deserializer)?;
32+
if validator_id.is_empty() || validator_id.len() != 42 {
33+
return Err(serde::de::Error::custom(
34+
"invalid validator id length".to_string(),
35+
));
36+
}
37+
38+
<[u8; 20] as FromHex>::from_hex(&validator_id[2..]).map_err(serde::de::Error::custom)
39+
}
2040

2141
impl ValidatorId {
2242
pub fn inner(&self) -> &[u8; 20] {
@@ -61,11 +81,13 @@ impl AsRef<[u8]> for ValidatorId {
6181
impl TryFrom<&str> for ValidatorId {
6282
type Error = DomainError;
6383
fn try_from(value: &str) -> Result<Self, Self::Error> {
64-
let hex_value = if value.len() == 42 {
65-
&value[2..]
66-
} else {
67-
value
68-
};
84+
let hex_value = match value {
85+
value if value.len() == 42 => Ok(&value[2..]),
86+
value if value.len() == 40 => Ok(value),
87+
_ => Err(DomainError::InvalidArgument(
88+
"invalid validator id length".to_string(),
89+
)),
90+
}?;
6991

7092
let result = hex::decode(hex_value).map_err(|_| {
7193
DomainError::InvalidArgument("Failed to deserialize validator id".to_string())
@@ -101,6 +123,7 @@ impl fmt::Display for ValidatorId {
101123
#[serde(rename_all = "camelCase")]
102124
pub struct ValidatorDesc {
103125
pub id: ValidatorId,
126+
#[serde(default, skip_serializing_if = "Option::is_none")]
104127
pub fee_addr: Option<ValidatorId>,
105128
pub url: String,
106129
pub fee: BigNum,

sentry/migrations/20190806011140_initial-tables/seed/up.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ VALUES
66
'0x033ed90e0fec3f3ea1c9b005c724d704501e0196',
77
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
88
'1000',
9-
to_timestamp(4102444800000),
9+
to_timestamp(4102444800),
1010
'{"minPerImpression":"1","maxPerImpression":"10","pricingBounds":{"CLICK":{"min":"0","max":"0"}},"withdrawPeriodStart":4073414400000,"validators":[{"id":"0xce07CbB7e054514D590a0262C93070D838bFBA2e","url":"http://localhost:8005","fee":"100"},{"id":"0xC91763D7F14ac5c5dDfBCD012e0D2A61ab9bDED3","url":"http://localhost:8006","fee":"100"}]}'
1111
);

0 commit comments

Comments
 (0)