Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Introduced two new traits for spec's serialization and deserialization #190

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fuel-asm = { version = "0.9", default-features = false }
fuel-crypto = { version = "0.6", default-features = false }
fuel-merkle = { version = "0.4", default-features = false }
fuel-types = { version = "0.5", default-features = false }
fuel-tx-derive = { path = "derive" }
itertools = { version = "0.10", default-features = false }
rand = { version = "0.8", default-features = false, features = ["std_rng"], optional = true }
num-integer = { version = "0.1", default-features = false }
Expand Down
21 changes: 21 additions & 0 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "fuel-tx-derive"
version = "0.20.0"
authors = ["Fuel Labs <contact@fuel.sh>"]
categories = ["cryptography::cryptocurrencies", "data-structures"]
edition = "2021"
homepage = "https://fuel.network/"
keywords = ["blockchain", "cryptocurrencies", "fuel-vm", "vm"]
license = "Apache-2.0"
repository = "https://github.com/FuelLabs/fuel-tx"
description = "FuelVM derive for `fuel-tx`."

[lib]
proc-macro = true

[dependencies]
quote = "1"
syn = { version = "1", features = ["full"] }
proc-macro2 = "1"
synstructure = "0.12.4"

100 changes: 100 additions & 0 deletions derive/src/deserialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

fn deserialize_struct(s: &synstructure::Structure) -> TokenStream2 {
assert_eq!(s.variants().len(), 1, "structs must have one variant");

let variant: &synstructure::VariantInfo = &s.variants()[0];
let decode_main = variant.construct(|field, _| {
let ty = &field.ty;
quote! {
<#ty as fuel_tx::io::Deserialize>::decode(buffer)?
}
});

let decode_extra = variant.each(|binding| {
quote! {
fuel_tx::io::Deserialize::decode_extra(#binding, buffer)?;
}
});

s.gen_impl(quote! {
gen impl fuel_tx::io::Deserialize for @Self {
fn decode<I: fuel_tx::io::Input + ?Sized>(buffer: &mut I) -> ::core::result::Result<Self, fuel_tx::io::Error> {
let mut object = #decode_main;

match object {
#decode_extra,
};

::core::result::Result::Ok(object)
}
}
})
}

fn deserialize_enum(s: &synstructure::Structure) -> TokenStream2 {
assert!(!s.variants().is_empty(), "got invalid empty enum");
let decode = s
.variants()
.iter()
.map(|variant| {
let decode_main = variant.construct(|field, _| {
let ty = &field.ty;
quote! {
<#ty as fuel_tx::io::Deserialize>::decode(buffer)?
}
});

let decode_extra = variant.each(|binding| {
quote! {
fuel_tx::io::Deserialize::decode_extra(#binding, buffer)?;
}
});

quote! {
{
let mut object = #decode_main;

match object {
#decode_extra,
// It is not possible, because we created `object` on previous iteration.
_ => panic!("unexpected variant of the enum"),
};

::core::result::Result::Ok(object)
}
}
})
.enumerate()
.fold(quote! {}, |acc, (i, v)| {
let index = i as u64;
quote! {
#acc
#index => #v,
}
});

s.gen_impl(quote! {
gen impl fuel_tx::io::Deserialize for @Self {
fn decode<I: fuel_tx::io::Input + ?Sized>(buffer: &mut I) -> ::core::result::Result<Self, fuel_tx::io::Error> {
match <::core::primitive::u64 as fuel_tx::io::Deserialize>::decode(buffer)? {
#decode
_ => return ::core::result::Result::Err(fuel_tx::io::Error::UnknownDiscriminant),
}
}
}
})
}

/// Derives `Deserialize` trait for the given `struct` or `enum`.
pub fn deserialize_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.bind_with(|_| synstructure::BindStyle::RefMut)
.add_bounds(synstructure::AddBounds::Fields)
.underscore_const(true);
match s.ast().data {
syn::Data::Struct(_) => deserialize_struct(&s),
syn::Data::Enum(_) => deserialize_enum(&s),
_ => panic!("Can't derive `Deserialize` for `union`s"),
}
}
16 changes: 16 additions & 0 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
extern crate proc_macro;

mod deserialize;
mod serialize;

use self::{deserialize::deserialize_derive, serialize::serialize_derive};
synstructure::decl_derive!(
[Deserialize] =>
/// Derives `Deserialize` trait for the given `struct` or `enum`.
deserialize_derive
);
synstructure::decl_derive!(
[Serialize] =>
/// Derives `Serialize` trait for the given `struct` or `enum`.
serialize_derive
);
94 changes: 94 additions & 0 deletions derive/src/serialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

fn serialize_struct(s: &synstructure::Structure) -> TokenStream2 {
assert_eq!(s.variants().len(), 1, "structs must have one variant");

let variant: &synstructure::VariantInfo = &s.variants()[0];
let encode = variant.each(|binding| {
quote! {
if fuel_tx::io::Serialize::size(#binding) % fuel_tx::io::ALIGN > 0 {
return ::core::result::Result::Err(fuel_tx::io::Error::WrongAlign)
}
fuel_tx::io::Serialize::encode(#binding, buffer)?;
}
});

let encode_extra = variant.each(|binding| {
quote! {
fuel_tx::io::Serialize::encode_extra(#binding, buffer)?;
}
});

s.gen_impl(quote! {
gen impl fuel_tx::io::Serialize for @Self {
fn encode<O: fuel_tx::io::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), fuel_tx::io::Error> {
match self {
#encode
};
match self {
#encode_extra
};

::core::result::Result::Ok(())
}
}
})
}

fn serialize_enum(s: &synstructure::Structure) -> TokenStream2 {
assert!(!s.variants().is_empty(), "got invalid empty enum");
let encode_body = s.variants().iter().enumerate().map(|(i, v)| {
let pat = v.pat();
let index = i as u64;
let encode_iter = v.bindings().iter().map(|binding| {
quote! {
if fuel_tx::io::Serialize::size(#binding) % fuel_tx::io::ALIGN > 0 {
return ::core::result::Result::Err(fuel_tx::io::Error::WrongAlign)
}
fuel_tx::io::Serialize::encode(#binding, buffer)?;
}
});
let encode_extra_iter = v.bindings().iter().map(|binding| {
quote! {
fuel_tx::io::Serialize::encode_extra(#binding, buffer)?;
}
});
quote! {
#pat => {
{ <::core::primitive::u64 as fuel_tx::io::Serialize>::encode(&#index, buffer)?; }
#(
{ #encode_iter }
)*
#(
{ #encode_extra_iter }
)*
}
}
});
s.gen_impl(quote! {
gen impl fuel_tx::io::Serialize for @Self {
fn encode<O: fuel_tx::io::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), fuel_tx::io::Error> {
match self {
#(
#encode_body
)*,
_ => return ::core::result::Result::Err(fuel_tx::io::Error::UnknownDiscriminant),
};

::core::result::Result::Ok(())
}
}
})
}

/// Derives `Serialize` trait for the given `struct` or `enum`.
pub fn serialize_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.add_bounds(synstructure::AddBounds::Fields)
.underscore_const(true);
match s.ast().data {
syn::Data::Struct(_) => serialize_struct(&s),
syn::Data::Enum(_) => serialize_enum(&s),
_ => panic!("Can't derive `Serialize` for `union`s"),
}
}
13 changes: 8 additions & 5 deletions src/checked_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
//! This allows the VM to accept transactions that have been already verified upstream,
//! and consolidates logic around fee calculations and free balances.

use crate::io::{Error, Serialize};
use crate::{
ConsensusParameters, Input, Metadata, Output, Transaction, TransactionFee, ValidationError,
};
use fuel_asm::PanicReason;
use fuel_types::bytes::SerializableVec;
use fuel_types::{Address, AssetId, Bytes32, Word};

use alloc::collections::BTreeMap;
use core::{borrow::Borrow, ops::Index};

use core::mem;
use std::io::{self, Read};
use std::io;

const BASE_ASSET: AssetId = AssetId::zeroed();

Expand Down Expand Up @@ -121,12 +121,15 @@ impl CheckedTransaction {
self.transaction.to_bytes()
}

pub fn tx_output_to_mem(&mut self, idx: usize, buf: &mut [u8]) -> io::Result<usize> {
pub fn tx_output_to_mem(&mut self, idx: usize, mut buf: &mut [u8]) -> Result<usize, Error> {
self.transaction
._outputs_mut()
.get_mut(idx)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid output idx"))
.and_then(|o| o.read(buf))
.ok_or(Error::Unknown("Invalid output idx"))
.and_then(|o| {
o.encode(&mut buf)?;
Ok(o.size())
})
}

pub fn tx_set_receipts_root(&mut self, root: Bytes32) -> Option<Bytes32> {
Expand Down
Loading