From 64c0ed0c0585a1b35232ef9231adc845ecedc32c Mon Sep 17 00:00:00 2001 From: tommytrg Date: Tue, 20 May 2025 17:43:06 +0200 Subject: [PATCH 1/2] add support for u64 as string in the jsonrpc inventory --- data_structures/src/chain/mod.rs | 40 +++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/data_structures/src/chain/mod.rs b/data_structures/src/chain/mod.rs index 9b1d8986c..cb22b32d6 100644 --- a/data_structures/src/chain/mod.rs +++ b/data_structures/src/chain/mod.rs @@ -15,7 +15,8 @@ use ethereum_types::U256; use failure::Fail; use futures::future::BoxFuture; use ordered_float::OrderedFloat; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; +use core::fmt::Display; use partial_struct::PartialStruct; use witnet_crypto::{ @@ -1604,6 +1605,10 @@ pub struct ValueTransferOutput { /// Address that will receive the value pub pkh: PublicKeyHash, /// Transferred value in nanowits + #[serde( + serialize_with = "u64_to_string", + deserialize_with = "number_from_string" + )] pub value: u64, /// The value attached to a time-locked output cannot be spent before the specified /// timestamp. That is, they cannot be used as an input in any transaction of a @@ -1611,6 +1616,39 @@ pub struct ValueTransferOutput { pub time_lock: u64, } +pub fn u64_to_string(val: &u64, serializer: S) -> Result +where + S: serde::Serializer, +{ + if serializer.is_human_readable() { + serializer.serialize_str(&val.to_string()) + } else { + serializer.serialize_u64(*val) + } +} + +pub fn number_from_string<'de, T, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: FromStr + serde::Deserialize<'de>, + ::Err: Display, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrInt { + String(String), + Number(T), + } + if deserializer.is_human_readable() { + match StringOrInt::::deserialize(deserializer)? { + StringOrInt::String(s) => s.parse::().map_err(serde::de::Error::custom), + StringOrInt::Number(i) => Ok(i), + } + } else { + T::deserialize(deserializer) + } +} + impl ValueTransferOutput { #[inline] pub fn value(&self) -> u64 { From 471d2a23aabb4faca8514c8c1dd2dfc5c3c4b87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 21 May 2025 09:25:50 +0200 Subject: [PATCH 2/2] feat(json_rpc): accept strings as bigints when parsing raw transactions --- data_structures/src/chain/mod.rs | 22 ++++++---------------- data_structures/src/transaction.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/data_structures/src/chain/mod.rs b/data_structures/src/chain/mod.rs index cb22b32d6..9bce90811 100644 --- a/data_structures/src/chain/mod.rs +++ b/data_structures/src/chain/mod.rs @@ -11,12 +11,12 @@ use std::{ use bech32::{FromBase32, ToBase32}; use bls_signatures_rs::{bn256, bn256::Bn256, MultiSignature}; +use core::fmt::Display; use ethereum_types::U256; use failure::Fail; use futures::future::BoxFuture; use ordered_float::OrderedFloat; use serde::{Deserialize, Deserializer, Serialize}; -use core::fmt::Display; use partial_struct::PartialStruct; use witnet_crypto::{ @@ -1605,10 +1605,7 @@ pub struct ValueTransferOutput { /// Address that will receive the value pub pkh: PublicKeyHash, /// Transferred value in nanowits - #[serde( - serialize_with = "u64_to_string", - deserialize_with = "number_from_string" - )] + #[serde(deserialize_with = "number_from_string")] pub value: u64, /// The value attached to a time-locked output cannot be spent before the specified /// timestamp. That is, they cannot be used as an input in any transaction of a @@ -1616,17 +1613,6 @@ pub struct ValueTransferOutput { pub time_lock: u64, } -pub fn u64_to_string(val: &u64, serializer: S) -> Result -where - S: serde::Serializer, -{ - if serializer.is_human_readable() { - serializer.serialize_str(&val.to_string()) - } else { - serializer.serialize_u64(*val) - } -} - pub fn number_from_string<'de, T, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -1668,10 +1654,12 @@ pub struct DataRequestOutput { /// Data request structure pub data_request: RADRequest, /// Reward in nanowits that will be earned by honest witnesses + #[serde(deserialize_with = "number_from_string")] pub witness_reward: u64, /// Number of witnesses that will resolve this data request pub witnesses: u16, /// This fee will be earn by the miner when include commits and/or reveals in the block + #[serde(deserialize_with = "number_from_string")] pub commit_and_reveal_fee: u64, /// The minimum percentage of non-error reveals required to consider this data request as /// "resolved" instead of as "error". @@ -1683,6 +1671,7 @@ pub struct DataRequestOutput { /// this data request. /// This field must be >= collateral_minimum, or zero. /// If zero, it will be treated as collateral_minimum. + #[serde(deserialize_with = "number_from_string")] pub collateral: u64, } @@ -1732,6 +1721,7 @@ impl DataRequestOutput { pub struct StakeOutput { pub authorization: KeyedSignature, pub key: StakeKey, + #[serde(deserialize_with = "number_from_string")] pub value: u64, } diff --git a/data_structures/src/transaction.rs b/data_structures/src/transaction.rs index b71297f3b..54c0cc662 100644 --- a/data_structures/src/transaction.rs +++ b/data_structures/src/transaction.rs @@ -1,8 +1,9 @@ use std::convert::TryFrom; +use std::str::FromStr; use std::sync::{Arc, RwLock}; use protobuf::Message; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use witnet_crypto::{ hash::calculate_sha256, merkle::FullMerkleTree, secp256k1::Message as Secp256k1Message, @@ -863,7 +864,9 @@ impl UnstakeTransaction { pub struct UnstakeTransactionBody { pub operator: PublicKeyHash, pub withdrawal: ValueTransferOutput, + #[serde(deserialize_with = "number_from_string")] pub fee: u64, + #[serde(deserialize_with = "number_from_string")] pub nonce: u64, #[protobuf_convert(skip)] @@ -871,6 +874,28 @@ pub struct UnstakeTransactionBody { hash: MemoHash, } +pub fn number_from_string<'de, T, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: FromStr + serde::Deserialize<'de>, + ::Err: std::fmt::Display, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrInt { + String(String), + Number(T), + } + if deserializer.is_human_readable() { + match StringOrInt::::deserialize(deserializer)? { + StringOrInt::String(s) => s.parse::().map_err(serde::de::Error::custom), + StringOrInt::Number(i) => Ok(i), + } + } else { + T::deserialize(deserializer) + } +} + impl UnstakeTransactionBody { /// Creates a new stake transaction body. pub fn new(