diff --git a/core/src/lib.rs b/core/src/lib.rs index d471822bb..4bd824c0a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,6 +30,7 @@ use grin_util as util; extern crate lazy_static; #[macro_use] extern crate serde_derive; +extern crate serde; #[macro_use] extern crate log; use failure; diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index 106800f32..319d85438 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -26,6 +26,7 @@ pub mod build; mod error; pub mod proof; pub mod reward; +pub mod serialization; pub mod slate; use crate::consensus; diff --git a/core/src/libtx/serialization.rs b/core/src/libtx/serialization.rs new file mode 100644 index 000000000..969be3640 --- /dev/null +++ b/core/src/libtx/serialization.rs @@ -0,0 +1,175 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Sane serialization & deserialization of cryptographic structs into hex + +use crate::keychain::BlindingFactor; +use crate::serde::{Deserialize, Deserializer, Serializer}; +use crate::util::secp::pedersen::{Commitment, RangeProof}; +use crate::util::{from_hex, to_hex}; + +/// Serializes a secp PublicKey to and from hex +pub mod pubkey_serde { + use crate::serde::{Deserialize, Deserializer, Serializer}; + use crate::util::secp::key::PublicKey; + use crate::util::{from_hex, static_secp_instance, to_hex}; + + /// + pub fn serialize(key: &PublicKey, serializer: S) -> Result + where + S: Serializer, + { + let static_secp = static_secp_instance(); + let static_secp = static_secp.lock(); + serializer.serialize_str(&to_hex(key.serialize_vec(&static_secp, false).to_vec())) + } + + /// + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let static_secp = static_secp_instance(); + let static_secp = static_secp.lock(); + String::deserialize(deserializer) + .and_then(|string| from_hex(string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|bytes: Vec| { + PublicKey::from_slice(&static_secp, &bytes) + .map_err(|err| Error::custom(err.to_string())) + }) + } +} + +/// Serializes an Option to and from hex +pub mod option_sig_serde { + use crate::serde::{Deserialize, Deserializer, Serializer}; + use crate::util::secp; + use crate::util::static_secp_instance; + use crate::util::{from_hex, to_hex}; + use serde::de::Error; + + /// + pub fn serialize(sig: &Option, serializer: S) -> Result + where + S: Serializer, + { + match sig { + Some(sig) => { + let static_secp = static_secp_instance(); + let static_secp = static_secp.lock(); + serializer.serialize_str(&to_hex(sig.serialize_der(&static_secp))) + } + None => serializer.serialize_none(), + } + } + + /// + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let static_secp = static_secp_instance(); + let static_secp = static_secp.lock(); + + Option::<&str>::deserialize(deserializer).and_then(|res| match res { + Some(string) => from_hex(string.to_string()) + .map_err(|err| Error::custom(err.to_string())) + .and_then(|bytes: Vec| { + secp::Signature::from_der(&static_secp, &bytes) + .map(|val| Some(val)) + .map_err(|err| Error::custom(err.to_string())) + }), + None => Ok(None), + }) + } + +} + +/// Serializes a secp::Signature to and from hex +pub mod sig_serde { + use crate::serde::{Deserialize, Deserializer, Serializer}; + use crate::util::secp; + use crate::util::static_secp_instance; + use crate::util::{from_hex, to_hex}; + use serde::de::Error; + + /// + pub fn serialize(sig: &secp::Signature, serializer: S) -> Result + where + S: Serializer, + { + let static_secp = static_secp_instance(); + let static_secp = static_secp.lock(); + serializer.serialize_str(&to_hex(sig.serialize_der(&static_secp))) + } + + /// + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let static_secp = static_secp_instance(); + let static_secp = static_secp.lock(); + String::deserialize(deserializer) + .and_then(|string| from_hex(string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|bytes: Vec| { + secp::Signature::from_der(&static_secp, &bytes) + .map_err(|err| Error::custom(err.to_string())) + }) + } +} + +/// Creates a BlindingFactor from a hex string +pub fn blind_from_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + use serde::de::Error; + String::deserialize(deserializer).and_then(|string| { + BlindingFactor::from_hex(&string).map_err(|err| Error::custom(err.to_string())) + }) +} + +/// Creates a RangeProof from a hex string +pub fn rangeproof_from_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + use serde::de::{Error, IntoDeserializer}; + + let val = String::deserialize(deserializer) + .and_then(|string| from_hex(string).map_err(|err| Error::custom(err.to_string())))?; + RangeProof::deserialize(val.into_deserializer()) +} + +/// Creates a Pedersen Commitment from a hex string +pub fn commitment_from_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + use serde::de::Error; + String::deserialize(deserializer) + .and_then(|string| from_hex(string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|bytes: Vec| Ok(Commitment::from_vec(bytes.to_vec()))) +} + +/// Seralizes a byte string into hex +pub fn as_hex(bytes: T, serializer: S) -> Result +where + T: AsRef<[u8]>, + S: Serializer, +{ + serializer.serialize_str(&to_hex(bytes.as_ref().to_vec())) +} diff --git a/wallet/src/adapters/file.rs b/wallet/src/adapters/file.rs index 729b3c33c..41c8aaaff 100644 --- a/wallet/src/adapters/file.rs +++ b/wallet/src/adapters/file.rs @@ -52,7 +52,7 @@ impl WalletCommAdapter for FileWalletCommAdapter { let mut pub_tx_f = File::open(params)?; let mut content = String::new(); pub_tx_f.read_to_string(&mut content)?; - Ok(json::from_str(&content).map_err(|_| ErrorKind::Format)?) + Ok(json::from_str(&content).map_err(|err| ErrorKind::Format(err.to_string()))?) } fn listen( diff --git a/wallet/src/libwallet/error.rs b/wallet/src/libwallet/error.rs index 3dd56d171..9016d763b 100644 --- a/wallet/src/libwallet/error.rs +++ b/wallet/src/libwallet/error.rs @@ -112,8 +112,8 @@ pub enum ErrorKind { Restore, /// An error in the format of the JSON structures exchanged by the wallet - #[fail(display = "JSON format error")] - Format, + #[fail(display = "JSON format error: {}", _0)] + Format(String), /// Other serialization errors #[fail(display = "Ser/Deserialization error")]