Skip to content

Commit

Permalink
execution-core: added serde support for MoonlightTransactionEvent in …
Browse files Browse the repository at this point in the history
…execution-core
  • Loading branch information
d-sonuga committed Nov 22, 2024
1 parent ad0ef94 commit 8b9d6ae
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 0 deletions.
7 changes: 7 additions & 0 deletions execution-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ ark-bn254 = { workspace = true, features = ["curve"], optional = true }
ark-relations = { workspace = true, optional = true }
ark-serialize = { workspace = true, optional = true }

# serde support dependencies
serde = { workspace = true, features = ["derive"], optional = true }
bs58 = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }

[dev-dependencies]
rand = { workspace = true, features = ["std", "std_rng"] }

Expand Down Expand Up @@ -57,3 +62,5 @@ groth16 = [

# Enables std feature for dusk-plonk
std = ["dusk-plonk/std"]

serde_support = ["serde", "bs58", "serde_json"]
141 changes: 141 additions & 0 deletions execution-core/src/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ use phoenix::{
};
use withdraw::{Withdraw, WithdrawReceiver};

#[cfg(feature = "serde_support")]
use serde::{Serialize as SerdeSerialize, Deserialize as SerdeDeserialize};

pub mod data;
pub mod moonlight;
pub mod phoenix;
Expand Down Expand Up @@ -505,12 +508,15 @@ pub struct PhoenixTransactionEvent {
}

/// Event data emitted on a moonlight transaction's completion.
#[cfg_attr(feature = "serde_support", derive(SerdeSerialize, SerdeDeserialize))]
#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
pub struct MoonlightTransactionEvent {
/// The account that initiated the transaction.
#[cfg_attr(feature = "serde_support", serde(with = "serde_support::pubk"))]
pub sender: AccountPublicKey,
/// The receiver of the funds if any were transferred.
#[cfg_attr(feature = "serde_support", serde(with = "serde_support::optional_pubk"))]
pub receiver: Option<AccountPublicKey>,
/// Transfer amount
pub value: u64,
Expand All @@ -520,5 +526,140 @@ pub struct MoonlightTransactionEvent {
pub gas_spent: u64,
/// Optional refund-info in the case that the refund-address is different
/// from the sender.
#[cfg_attr(feature = "serde_support", serde(with = "serde_support::pubk_u64_tuple"))]
pub refund_info: Option<(AccountPublicKey, u64)>,
}

#[cfg(feature = "serde_support")]
mod serde_support {
use serde::{Serialize, Deserialize};
use dusk_bytes::Serializable;
use serde::{Serializer, Deserializer, de};
use alloc::format;
use crate::String;
use super::AccountPublicKey;
use serde_json as _;


struct SerializablePublicKey(AccountPublicKey);

impl Serialize for SerializablePublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer {
let s = bs58::encode(self.0.to_bytes()).into_string();
serializer.serialize_str(&s)
}
}

impl<'de> Deserialize<'de> for SerializablePublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de> {
let s = String::deserialize(deserializer)?;
let mut bytes: [u8; AccountPublicKey::SIZE] = [0; AccountPublicKey::SIZE];
match bs58::decode(&s).into(&mut bytes) {
Ok(n) => if n != AccountPublicKey::SIZE {
return Err(de::Error::custom("failed to deserialize AccountPublicKey"))
}
Err(err) => return Err(de::Error::custom(format!("{err:?}")))
}
let pubk = AccountPublicKey::from_bytes(&bytes)
.map_err(|err| de::Error::custom(format!("{err:?}")))?;
Ok(SerializablePublicKey(pubk))
}
}

pub mod pubk {
use super::*;

pub fn serialize<S: Serializer>(value: &AccountPublicKey, serializer: S) -> Result<S::Ok, S::Error> {
let s = SerializablePublicKey(value.clone());
s.serialize(serializer)
}

pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<AccountPublicKey, D::Error> {
SerializablePublicKey::deserialize(deserializer)
.map(|ser_pubk| ser_pubk.0)
}
}

pub mod optional_pubk {
use super::*;

pub fn serialize<S: Serializer>(value: &Option<AccountPublicKey>, serializer: S) -> Result<S::Ok, S::Error> {
let s = value.as_ref()
.map(|pubk| SerializablePublicKey(pubk.clone()));
s.serialize(serializer)
}

pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<AccountPublicKey>, D::Error> {
let s = Option::<SerializablePublicKey>::deserialize(deserializer)?;
Ok(s.map(|ser_pubk| ser_pubk.0))
}
}

pub mod pubk_u64_tuple {
use de::Visitor;
use serde::ser::SerializeTuple;
use super::*;

pub fn serialize<S: Serializer>(value: &Option<(AccountPublicKey, u64)>, serializer: S) -> Result<S::Ok, S::Error> {
match value {
Some((pubk, n)) => {
let mut tup = serializer.serialize_tuple(2)?;
tup.serialize_element(&SerializablePublicKey(pubk.clone()))?;
tup.serialize_element(&n)?;
tup.end()
}
None => serializer.serialize_none()
}
}

pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<(AccountPublicKey, u64)>, D::Error> {
struct OptionalTupleVisitor;

impl<'de> Visitor<'de> for OptionalTupleVisitor {
type Value = Option<(AccountPublicKey, u64)>;

fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
formatter.write_str("an Option<(PublicKey, u64)>")
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error, {
Ok(None)
}

fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>, {
deserializer.deserialize_seq(TupleVisitor)
.map(|t| Some(t))
}
}

struct TupleVisitor;

impl<'de> Visitor<'de> for TupleVisitor {
type Value = (AccountPublicKey, u64);

fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
formatter.write_str("a (PublicKey, u64)")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>, {
let ser_pubk: SerializablePublicKey = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let n = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok((ser_pubk.0, n))
}
}
deserializer.deserialize_option(OptionalTupleVisitor)
}
}
}
44 changes: 44 additions & 0 deletions execution-core/tests/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,47 @@ fn nonsense_bytes_fails() -> Result<(), Error> {
}
Ok(())
}

#[cfg(feature = "serde_support")]
mod serde_serialization {
use rand::{Rng, RngCore};
use execution_core::transfer::MoonlightTransactionEvent;
use super::{AccountSecretKey, AccountPublicKey, StdRng, SeedableRng};


fn moonlight_tx_event(rng: &mut StdRng) -> MoonlightTransactionEvent {
let mut memo = Vec::new();
memo.resize(50, 0);
rng.fill_bytes(&mut memo);
MoonlightTransactionEvent {
sender: AccountPublicKey::from(&AccountSecretKey::random(rng)),
receiver: if rng.gen_bool(0.5) {
Some(AccountPublicKey::from(&AccountSecretKey::random(rng)))
} else {
None
},
value: rng.gen(),
memo,
gas_spent: rng.gen(),
refund_info: if rng.gen_bool(0.5) {
Some((
AccountPublicKey::from(&AccountSecretKey::random(rng)),
rng.gen(),
))
} else {
None
}
}
}

#[test]
fn moonlight_tx_event_serde() {
let mut rng = StdRng::seed_from_u64(42);
let tx_event: MoonlightTransactionEvent = moonlight_tx_event(&mut rng);
let ser = serde_json::to_string(&tx_event);
assert!(ser.is_ok());
let deser = serde_json::from_str(&ser.unwrap());
assert!(deser.is_ok());
assert_eq!(tx_event, deser.unwrap());
}
}

0 comments on commit 8b9d6ae

Please sign in to comment.