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 25, 2024
1 parent ad0ef94 commit e329820
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 0 deletions.
8 changes: 8 additions & 0 deletions execution-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ 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 }
hex = { workspace = true, optional = true }

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

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

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

serde = ["dep:serde", "bs58", "serde_json", "hex"]
3 changes: 3 additions & 0 deletions execution-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub use error::Error;
mod dusk;
pub use dusk::{dusk, from_dusk, Dusk, LUX};

#[cfg(feature = "serde")]
mod serde_support;

// elliptic curve types
pub use dusk_bls12_381::BlsScalar;
pub use dusk_jubjub::{
Expand Down
179 changes: 179 additions & 0 deletions execution-core/src/serde_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use crate::signatures::bls::PublicKey as AccountPublicKey;
use crate::String;
use alloc::format;
use dusk_bytes::Serializable;
use serde::{de, Deserializer, Serializer};
use serde::{Deserialize, Serialize};
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 super::*;
use de::Visitor;

pub fn serialize<S: Serializer>(
value: &Option<(AccountPublicKey, u64)>,
serializer: S,
) -> Result<S::Ok, S::Error> {
match value {
Some((pubk, n)) => serializer
.serialize_some(&(SerializablePublicKey(pubk.clone()), n)),
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)
}
}

pub mod hex_serde {
use super::*;
use alloc::vec::Vec;

pub fn serialize<S: Serializer>(
value: &[u8],
serializer: S,
) -> Result<S::Ok, S::Error> {
hex::encode(value).serialize(serializer)
}

pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<u8>, D::Error> {
let s = String::deserialize(deserializer)?;
hex::decode(&s).map_err(|err| de::Error::custom(format!("{err:?}")))
}
}
16 changes: 16 additions & 0 deletions execution-core/src/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ use phoenix::{
};
use withdraw::{Withdraw, WithdrawReceiver};

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

pub mod data;
pub mod moonlight;
pub mod phoenix;
Expand Down Expand Up @@ -505,20 +510,31 @@ pub struct PhoenixTransactionEvent {
}

/// Event data emitted on a moonlight transaction's completion.
#[cfg_attr(feature = "serde", 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", serde(with = "serde_support::pubk"))]
pub sender: AccountPublicKey,
/// The receiver of the funds if any were transferred.
#[cfg_attr(
feature = "serde",
serde(with = "serde_support::optional_pubk")
)]
pub receiver: Option<AccountPublicKey>,
/// Transfer amount
pub value: u64,
/// The memo included in the transaction.
#[cfg_attr(feature = "serde", serde(with = "serde_support::hex_serde"))]
pub memo: Vec<u8>,
/// Gas spent by the transaction.
pub gas_spent: u64,
/// Optional refund-info in the case that the refund-address is different
/// from the sender.
#[cfg_attr(
feature = "serde",
serde(with = "serde_support::pubk_u64_tuple")
)]
pub refund_info: Option<(AccountPublicKey, u64)>,
}
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")]
mod serde_serialization {
use super::{AccountPublicKey, AccountSecretKey, SeedableRng, StdRng};
use execution_core::transfer::MoonlightTransactionEvent;
use rand::{Rng, RngCore};

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);
println!("{:?}", ser);
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 e329820

Please sign in to comment.