Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

execution-core: added serde support for MoonlightTransactionEvent #3046

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
185 changes: 185 additions & 0 deletions execution-core/src/serde_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

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());
}
}
Loading