Skip to content

Commit

Permalink
add string_or_u64 serialization (#2698)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeastplume authored Mar 21, 2019
1 parent bd6824c commit 6949a0d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
2 changes: 2 additions & 0 deletions core/src/core/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,11 @@ pub struct TxKernel {
/// Options for a kernel's structure or use
pub features: KernelFeatures,
/// Fee originally included in the transaction this proof is for.
#[serde(with = "secp_ser::string_or_u64")]
pub fee: u64,
/// This kernel is not valid earlier than lock_height blocks
/// The max lock_height of all *inputs* to this transaction
#[serde(with = "secp_ser::string_or_u64")]
pub lock_height: u64,
/// Remainder of the sum of all transaction commitments. If the transaction
/// is well formed, amounts components should sum to zero and the excess
Expand Down
104 changes: 104 additions & 0 deletions core/src/libtx/secp_ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,104 @@ where
serializer.serialize_str(&to_hex(bytes.as_ref().to_vec()))
}

/// Used to ensure u64s are serialised in json
/// as strings by default, since it can't be guaranteed that consumers
/// will know what to do with u64 literals (e.g. Javascript). However,
/// fields using this tag can be deserialized from literals or strings.
/// From solutions on:
/// https://github.com/serde-rs/json/issues/329
pub mod string_or_u64 {
use std::fmt;

use serde::{de, Deserializer, Serializer};

/// serialize into a string
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: fmt::Display,
S: Serializer,
{
serializer.collect_str(value)
}

/// deserialize from either literal or string
pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor;
impl<'a> de::Visitor<'a> for Visitor {
type Value = u64;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"a string containing digits or an int fitting into u64"
)
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
Ok(v)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
s.parse().map_err(de::Error::custom)
}
}
deserializer.deserialize_any(Visitor)
}
}

/// As above, for Options
pub mod opt_string_or_u64 {
use std::fmt;

use serde::{de, Deserializer, Serializer};

/// serialize into string or none
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
T: fmt::Display,
S: Serializer,
{
match value {
Some(v) => serializer.collect_str(v),
None => serializer.serialize_none(),
}
}

/// deser from 'null', literal or string
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor;
impl<'a> de::Visitor<'a> for Visitor {
type Value = Option<u64>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"null, a string containing digits or an int fitting into u64"
)
}
fn visit_unit<E>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
Ok(Some(v))
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let val: u64 = s.parse().map_err(de::Error::custom)?;
Ok(Some(val))
}
}
deserializer.deserialize_any(Visitor)
}
}

// Test serialization methods of components that are being used
#[cfg(test)]
mod test {
Expand All @@ -185,6 +283,10 @@ mod test {
pub opt_sig: Option<Signature>,
#[serde(with = "sig_serde")]
pub sig: Signature,
#[serde(with = "string_or_u64")]
pub num: u64,
#[serde(with = "opt_string_or_u64")]
pub opt_num: Option<u64>,
}

impl SerTest {
Expand All @@ -200,6 +302,8 @@ mod test {
pub_key: PublicKey::from_secret_key(&secp, &sk).unwrap(),
opt_sig: Some(sig.clone()),
sig: sig.clone(),
num: 30,
opt_num: Some(33),
}
}
}
Expand Down

0 comments on commit 6949a0d

Please sign in to comment.