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

Implement Serialize/Deserialize #71

Merged
merged 20 commits into from
May 25, 2017
Merged

Implement Serialize/Deserialize #71

merged 20 commits into from
May 25, 2017

Conversation

vldm
Copy link
Contributor

@vldm vldm commented Apr 26, 2017

In this request merged next commits:

  • Updating serde to "1.0"
  • Implemented exonum json serialization/deserialization aspects
    For now there three new traits:
  1. ExonumJsonSerialize implemented for all types that should be serializable in json.
  2. ExonumJsonDeserialize for types that can be created from json Value
  3. ExonumJsonDeserializeField for types that can deserialize and write Value as Field
  • Additionally there some other objects that should be mentioned there:
  1. WriteBufferWrapper - Trait for all types that can write field in their body
  2. ExonumJsonSerializeWrapper and fn wrap() implemented for ExonumJsonSerialize. This types allows use serde serializer for our purposes.

TODO: This is temporary Serialization/Deserialization implementation for our packed structures.
It should be rewritten after storage_value refactor.

@vldm vldm mentioned this pull request Apr 26, 2017
3 tasks
@@ -1,5 +1,9 @@
#[macro_export]
macro_rules! storage_value {
(@count ) => {0};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, implement this as separate macros:

macro_rules! counter {
    () => { 0 };
    ($head:ident $($tail:ident)*) => { 1 + counter!($($tail)*) }
}

and than just call counter!($($field_name ))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@defuz if I implement it in separate macros, then I should export this macros too, and waste global namespace, is it ok?

@vldm vldm added API labels May 4, 2017
@vldm vldm changed the title WIP: Implement Serialize/Deserialize Implement Serialize/Deserialize May 4, 2017
stanislav-tkach
stanislav-tkach previously approved these changes May 5, 2017
use ::serde::ser::SerializeStruct;
let mut strukt = serializer.serialize_struct(stringify!($name), counter!($($field_name)*))?;
$(strukt.serialize_field(stringify!($field_name), &$crate::serialize::json::wrap(&self.$field_name()))?;)*
strukt.end()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strukt is a funny name. I understand that you cannot use struct name, but maybe a simple val would be better? Nothing better comes to my mind right now.


Ok(())
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a new line at the end of the file.

@@ -133,12 +134,16 @@ struct HexVisitor<T>
_p: PhantomData<T>,
}

impl<T> Visitor for HexVisitor<T>
impl<'a, T> Visitor<'a> for HexVisitor<T>
where T: AsRef<[u8]> + HexValue + Clone
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nitpick: you used 'v lifetime for Visitor in other places. It would be consistent to use it everywhere.

impl<'a> Field<'a> for &'a Signature {
fn field_size() -> usize {
32
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to use std::mem::size_of::<Signature>() instead of the hard-coded value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I will done this with all fields after refactor Fields

}

// Message with current state.
// сообщение о текущем состоянии
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave the English version of the comment.

Copy link
Contributor Author

@vldm vldm May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty, fxd

}

impl WriteBufferWrapper for ::messages::MessageWriter {
fn write<'a, T: Field<'a> >(&'a mut self, from: usize, to: usize, val:T){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason for importing messages::Field and not messages::MessageWriter?

Copy link
Contributor Author

@vldm vldm May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fxd

pub fn from_str<T: ExonumJsonDeserialize>(value: &str) -> Result<T, Box<Error>> {
let value: Value = ::serde_json::from_str(value)?;
from_value(&value)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line. 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -533,4 +523,4 @@ mod tests {
assert_eq!(&bit_vec.repr(), "0111111101");
}

}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line.

macro_rules! counter {
() => (0usize);
($head:ident $($tail:ident)*) => (1usize + counter!($($tail)*))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

#[macro_use]
mod utils;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -11,11 +11,13 @@ use std::fs::File;
pub struct ConfigFile {}

impl ConfigFile {
pub fn load<T: Deserialize>(path: &Path) -> Result<T, Box<Error>> {
pub fn load<T>(path: &Path) -> Result<T, Box<Error>>
where T: for<'r> Deserialize<'r>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also update toml crate and simplify this code.

Copy link
Contributor Author

@vldm vldm May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does toml has new api for working with files?

}

/*
macro_rules! impl_deserialize_float {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this code is commented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because we currently didn't support float fields

serde = "0.8"
serde_derive = "0.8"
serde_json = "0.8"
serde = "1.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Racer does not like version strings without patch. It is better to use "1.0.0" instead of "1.0".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, ty

Copy link
Contributor

@stanislav-tkach stanislav-tkach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still I don't like strukt variable name. If you have no idea for a more specific name, even struct_ will be better, as for me.

@vldm
Copy link
Contributor Author

vldm commented May 15, 2017

Rebased again, review pls @defuz @alekseysidorov @DarkEld3r @gisochre

@vldm vldm mentioned this pull request May 15, 2017
7 tasks
@alekseysidorov alekseysidorov self-requested a review May 18, 2017 14:40
alekseysidorov
alekseysidorov previously approved these changes May 18, 2017
Copy link
Contributor

@dj8yfo dj8yfo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we supposed to use only Exonum-J... traits and available helpers in exonum::serialize::json::{to_value, from_value, to_string, from_str}? https://travis-ci.com/exonum/cryptocurrency/builds/46367602.
A minor issue is this:

error[E0277]: the trait bound `&str: exonum::serialize::json::ExonumJsonSerialize` is not satisfied
  --> src/lib.rs:71:1
   |
71 | message! {
   | ^ the trait `exonum::serialize::json::ExonumJsonSerialize` is not implemented for `&str`
   |
   = note: required because of the requirements on the impl of `serde::Serialize` for `exonum::serialize::json::ExonumJsonSerializeWrapper<'_, &str>`
   = note: this error originates in a macro outside of the current crate

error[E0277]: the trait bound `&str: exonum::serialize::json::ExonumJsonDeserializeField` is not satisfied
  --> src/lib.rs:71:1
   |
71 | message! {
   | ^ the trait `exonum::serialize::json::ExonumJsonDeserializeField` is not implemented for `&str`
   |
   = note: required by `exonum::serialize::json::ExonumJsonDeserializeField::deserialize`
   = note: this error originates in a macro outside of the current crate

The major issue for me is this: serde::Serialize, Deserialize isn't implemented for storage! and message!.

This would be inconvenient, when composing structs with #[derive(Ser/De)]. But we can live without it, probably, or do the following manually:

impl<'de> Deserialize<'de> for Wallet {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>
    {
        let value = <Value>::deserialize(deserializer)?;
        from_value(&value).map_err(|_| D::Error::custom(""))
    }
}
impl Serialize for Wallet {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
        {
            wrap(self).serialize(serializer)
        }
}

@defuz. What approach do we pursue? Why not add default Serialize/Deserialize generation in macros? maybe there's a good reason not to add it now or ever.

use $crate::messages::{RawMessage, MessageWriter};
let mut writer = MessageWriter::new($extension, $id, $body);
// if we could deserialize values, try append signature
<Self as ExonumJsonDeserializeField>::deserialize(value, &mut writer, from, to)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and checking service_idand message_id against the expected $extension and $id.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: $crate::serialize::json::reexport::Serializer {
use $crate::serialize::json::reexport::SerializeStruct;
let mut structure = serializer.serialize_struct(stringify!($name), counter!($($field_name)*) + 1)?;
$(structure.serialize_field(stringify!($field_name), &$crate::serialize::json::wrap(&self.$field_name()))?;)*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no service_id, message_id and network_id here. I guess, they should be added, maybe, binary message length too, not sure about this last one.

Copy link
Contributor Author

@vldm vldm May 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fxd

@vldm vldm added the breaking change ⚠️ API or ABI is changed. label May 19, 2017
@vldm vldm added this to the Release 0.1 milestone May 22, 2017
@vldm vldm dismissed stale reviews from alekseysidorov and stanislav-tkach via 388bfae May 22, 2017 08:42
Copy link
Contributor

@dj8yfo dj8yfo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[derive(Debug, Clone, PartialEq)]
pub struct BlockProof {
    pub block: blockchain::Block,
    pub precommits: Vec<Precommit>,
}

let body = obj.get("body").ok_or("Can't get body from json.")?;

let signature = from_value(obj.get("signature").ok_or("Can't get signature from json")?.clone())?;
let message_type = from_value(obj.get("message_id").ok_or("Can't get message_type from json")?.clone())?;
Copy link
Contributor

@dj8yfo dj8yfo May 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok , so now neither exonum::serialize::json::reexport::from_value nor exonum::serialize::json::from_value can be used on our macrogenerated types, because it results in stack overflow:

running 15 tests
test tests::test_tx_create_wallet ... ok
test tests::generate_simple_scenario_transactions ... ok
test tests::test_tx_issue_serde ... FAILED
test tests::test_tx_transfer_serde ... FAILED
test tests::test_wallet_history_true_status ... ok
test tests::test_wallet_history_txcreate_false_status ... ok
test tests::test_tx_create_wallet_serde ... FAILED
test tests::test_wallet_prefix ... ok
test tx_metarecord::test_tx_meta_record ... ok
test tests::test_wallet_history_txtransfer_false_status_insufficient_balance ... ok
test tests::test_wallet_history_txtransfer_false_status_absent_receiver_wallet ... ok
test wallet::test_amount_transfer ... ok
test wallet::test_wallet ... ok

thread 'tx_metarecord::test_tx_meta_record_serde' has overflowed its stack
fatal runtime error: stack overflow
error: process didn't exit successfully: `/Users/sysmanj/Exonum/cryptocurrency/target/debug/deps/cryptocurrency-99db04e147220907` (signal: 6, SIGABRT: process abort signal)

To learn more, run the command again with --verbose.

I'll try to workaround this, shouldn't be a problem, because i don't need explicit calls of from_value, because you implemented service_id and message_id checks by default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
use $crate::serialize::json::reexport::Error;
let value = <$crate::serialize::json::reexport::Value>::deserialize(deserializer)?;
$crate::serialize::json::reexport::from_value(value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look above. I assume it should be ExomunJsonDeserialize::deserialize_owned here instead of from_value, otherwise you're calling Deserialize till death.

@dj8yfo
Copy link
Contributor

dj8yfo commented May 22, 2017

exonum/cryptocurrency#18

@dj8yfo
Copy link
Contributor

dj8yfo commented May 22, 2017

pub struct ExonumJsonSerializeWrapper<'a, T: ExonumJsonSerialize + 'a>( &'a T);


#[derive(Serialize, Deserialize)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in

   "round": 4,
          "time": {
            "nanos": 804000000,
            "secs": 1486720350
          },
          "validator": 0
        },
        "message_id": 4,
        "network_id": 0,

this should be
"secs": "1486720350" for consistency.

@defuz
Copy link
Contributor

defuz commented May 24, 2017

@vldm Let's rename the methods deserialize and deserialize_owned. I propose to do so:

ExonumJsonDeserializeField::deserialize_field
ExonumJsonDeserialize::deserialize

Rationale:

  • This fits perfectly with the names of the traits.
  • ExonumJsonDeserialize method have a common signature for deserialization and ExonumJsonDeserializeField method signature are specific. It is logical that the specific method should be prefixed/suffixed and not vice versa.
  • Suffix _owned looks confusing, at least for me.

@defuz
Copy link
Contributor

defuz commented May 24, 2017

@vldm If this does not cause conflicts, run rustfmt before the merge.

Copy link
Contributor

@dj8yfo dj8yfo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@defuz exonum/cryptocurrency#18
exonum/exonum-configuration#24.
May need some considerable time to parse it line by line for review.
@vldm took care of all my objections.
I may review later, after merge) At least it works, somehow.

@defuz
Copy link
Contributor

defuz commented May 25, 2017

🎉 🎉 🎉

@gisochre Anyway this is much better than it was before. Refactoring and line-by-line reviews can be done later. Especially since we are now waiting for #103.

@defuz defuz merged commit 8a11f36 into exonum:master May 25, 2017
@defuz defuz deleted the 17-refactor-message-1 branch May 25, 2017 00:16
vldm pushed a commit that referenced this pull request Jul 13, 2017
Implement Serialize/Deserialize

Former-commit-id: 1ab7034c61572e9b7fd2ae4dde67c6b0277ba43e
stanislav-tkach added a commit to stanislav-tkach/exonum that referenced this pull request Feb 10, 2018
* Update clippy version (0.0.169)

* Fix Clippy warnings
stanislav-tkach pushed a commit to stanislav-tkach/exonum that referenced this pull request Apr 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change ⚠️ API or ABI is changed.
Development

Successfully merging this pull request may close these issues.

None yet

5 participants