Skip to content

Commit 77517c6

Browse files
authored
Fix /snapshot/raw_snapshot/{tag} voting_power_cap serialization bug (#142)
Update `voting_power_cap` field serialization for `RawSnapshotInput`. Fixes #98
2 parents 27cb2b3 + 21f50e0 commit 77517c6

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

src/vit-servicing-station/vit-servicing-station-lib/src/utils/serde.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use crate::db::models::vote_options::VoteOptions;
22
use crate::utils::datetime::unix_timestamp_to_datetime;
33
use serde::de::Visitor;
44
use serde::{ser::Error, Deserialize, Deserializer, Serializer};
5+
use snapshot_lib::Fraction;
56
use std::fmt;
7+
use std::str::FromStr;
68
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
79

810
// this warning should be disable here since the interface for this function requires
@@ -113,7 +115,7 @@ pub fn deserialize_vote_options_from_string<'de, D>(
113115
where
114116
D: Deserializer<'de>,
115117
{
116-
struct VoteOptionsDeserializer();
118+
struct VoteOptionsDeserializer;
117119

118120
impl<'de> Visitor<'de> for VoteOptionsDeserializer {
119121
type Value = VoteOptions;
@@ -130,7 +132,7 @@ where
130132
}
131133
}
132134

133-
deserializer.deserialize_str(VoteOptionsDeserializer())
135+
deserializer.deserialize_str(VoteOptionsDeserializer)
134136
}
135137

136138
pub fn serialize_vote_options_to_string<S: Serializer>(
@@ -150,3 +152,39 @@ where
150152
"x" | "1" | "true"
151153
))
152154
}
155+
156+
pub fn serialize_fraction_to_string<S: Serializer>(
157+
fraction: &Fraction,
158+
serializer: S,
159+
) -> Result<S::Ok, S::Error> {
160+
serializer.serialize_str(&fraction.to_string())
161+
}
162+
163+
pub fn deserialize_fraction_from_string<'de, D>(deserializer: D) -> Result<Fraction, D::Error>
164+
where
165+
D: Deserializer<'de>,
166+
{
167+
struct FractionDeserializer;
168+
169+
impl<'de> Visitor<'de> for FractionDeserializer {
170+
type Value = Fraction;
171+
172+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
173+
formatter.write_str("A fraction value e.g. 1.0, 0.56")
174+
}
175+
176+
fn visit_str<E>(self, value: &str) -> Result<Fraction, E>
177+
where
178+
E: serde::de::Error,
179+
{
180+
match value {
181+
"NaN" => Err(E::custom(
182+
"Invalid value format, should be a number e.g. 1.0, 0.56".to_string(),
183+
)),
184+
_ => Ok(Fraction::from_str(value).map_err(|e| E::custom(e.to_string()))?),
185+
}
186+
}
187+
}
188+
189+
deserializer.deserialize_str(FractionDeserializer)
190+
}

src/vit-servicing-station/vit-servicing-station-lib/src/v0/endpoints/snapshot/handlers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ pub struct RawSnapshotInput {
4949
#[serde(serialize_with = "crate::utils::serde::serialize_unix_timestamp_as_rfc3339")]
5050
pub update_timestamp: i64,
5151
pub min_stake_threshold: Value,
52+
#[serde(deserialize_with = "crate::utils::serde::deserialize_fraction_from_string")]
53+
#[serde(serialize_with = "crate::utils::serde::serialize_fraction_to_string")]
5254
pub voting_power_cap: Fraction,
5355
pub direct_voters_group: Option<String>,
5456
pub representatives_group: Option<String>,

src/vit-servicing-station/vit-servicing-station-lib/src/v0/endpoints/snapshot/mod.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -695,10 +695,11 @@ mod test {
695695
const GROUP1: &str = "group1";
696696
const GROUP2: &str = "group2";
697697

698-
let _e = tracing_subscriber::fmt()
698+
tracing_subscriber::fmt()
699699
.with_max_level(Level::TRACE)
700700
.with_writer(tracing_subscriber::fmt::TestWriter::new())
701-
.try_init();
701+
.try_init()
702+
.unwrap();
702703

703704
let keys = [
704705
"0000000000000000000000000000000000000000000000000000000000000000",
@@ -818,10 +819,11 @@ mod test {
818819

819820
#[tokio::test]
820821
async fn test_snapshot_get_tags_2() {
821-
let _e = tracing_subscriber::fmt()
822+
tracing_subscriber::fmt()
822823
.with_max_level(Level::TRACE)
823824
.with_writer(tracing_subscriber::fmt::TestWriter::new())
824-
.try_init();
825+
.try_init()
826+
.unwrap();
825827

826828
let key = "0000000000000000000000000000000000000000000000000000000000000000";
827829

@@ -949,4 +951,27 @@ mod test {
949951

950952
assert_eq!(tags, vec!["tag_a", "tag_b"]);
951953
}
954+
955+
#[tokio::test]
956+
async fn test_put_raw_snapshot() {
957+
let content = r#"{"snapshot":[{"stake_public_key":"0xa6a3c0447aeb9cc54cf6422ba32b294e5e1c3ef6d782f2acff4a70694c4d1663","voting_power":2,"reward_address":"0xa6a3c0447aeb9cc54cf6422ba32b294e5e1c3ef6d782f2acff4a70694c4d1663","delegations":"0x0000000000000000000000000000000000000000000000000000000000000000","voting_purpose":0},{"stake_public_key":"0x00588e8e1d18cba576a4d35758069fe94e53f638b6faf7c07b8abd2bc5c5cdee","voting_power":1,"reward_address":"0x00588e8e1d18cba576a4d35758069fe94e53f638b6faf7c07b8abd2bc5c5cdee","delegations":"0x0000000000000000000000000000000000000000000000000000000000000000","voting_purpose":0}],"update_timestamp":"1970-01-01T00:00:00Z","min_stake_threshold":0,"voting_power_cap": "NaN","direct_voters_group":null,"representatives_group":null}"#;
958+
959+
let context = new_db_test_shared_context();
960+
let db_conn = &context.read().await.db_connection_pool.get().unwrap();
961+
initialize_db_with_migration(db_conn).unwrap();
962+
963+
let snapshot_root = warp::path!("snapshot" / ..).boxed();
964+
let put_filter = snapshot_root.and(update_filter(context));
965+
966+
assert_eq!(
967+
warp::test::request()
968+
.path("/snapshot/raw_snapshot/tag_a")
969+
.method("PUT")
970+
.body(content)
971+
.reply(&put_filter)
972+
.await
973+
.status(),
974+
StatusCode::BAD_REQUEST
975+
);
976+
}
952977
}

0 commit comments

Comments
 (0)