From f30e59adc50a7942e8f745281b801ec19841ea29 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Thu, 7 Mar 2019 11:08:29 -0800 Subject: [PATCH] make error types serializable (#2659) --- Cargo.lock | 9 +- core/src/core/committed.rs | 2 +- core/src/core/transaction.rs | 2 +- core/src/libtx/error.rs | 2 +- core/src/ser.rs | 454 ++++++++++++++++++++++++++++++++++- keychain/src/extkey_bip32.rs | 2 +- keychain/src/mnemonic.rs | 2 +- keychain/src/types.rs | 2 +- util/Cargo.toml | 2 +- 9 files changed, 465 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8a6186611..581020ab1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -859,7 +859,7 @@ dependencies = [ [[package]] name = "grin_secp256k1zkp" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -927,7 +927,7 @@ dependencies = [ "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "grin_secp256k1zkp 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "grin_secp256k1zkp 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log4rs 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1948,6 +1948,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde-value" @@ -2685,7 +2688,7 @@ dependencies = [ "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum grin_secp256k1zkp 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "223095ed6108a42855ab2ce368d2056cfd384705f81c494f6d88ab3383161562" +"checksum grin_secp256k1zkp 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "75e9a265f3eeea4c204470f7262e2c6fe18f3d8ddf5fb24340cb550ac4f909c5" "checksum h2 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ddb2b25a33e231484694267af28fec74ac63b5ccf51ee2065a5e313b834d836e" "checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a" "checksum http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a" diff --git a/core/src/core/committed.rs b/core/src/core/committed.rs index 3aebfab492..2809b0ba6c 100644 --- a/core/src/core/committed.rs +++ b/core/src/core/committed.rs @@ -23,7 +23,7 @@ use crate::util::{secp, secp_static, static_secp_instance}; use failure::Fail; /// Errors from summing and verifying kernel excesses via committed trait. -#[derive(Debug, Clone, PartialEq, Eq, Fail)] +#[derive(Debug, Clone, PartialEq, Eq, Fail, Serialize, Deserialize)] pub enum Error { /// Keychain related error. #[fail(display = "Keychain error {}", _0)] diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 80d7788465..3828a41b2a 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -68,7 +68,7 @@ impl Readable for KernelFeatures { } /// Errors thrown by Transaction validation -#[derive(Clone, Eq, Debug, PartialEq)] +#[derive(Clone, Eq, Debug, PartialEq, Serialize, Deserialize)] pub enum Error { /// Underlying Secp256k1 error (signature validation or invalid public key /// typically) diff --git a/core/src/libtx/error.rs b/core/src/libtx/error.rs index 89a1ecfb57..91071eb3df 100644 --- a/core/src/libtx/error.rs +++ b/core/src/libtx/error.rs @@ -26,7 +26,7 @@ pub struct Error { inner: Context, } -#[derive(Clone, Debug, Eq, Fail, PartialEq)] +#[derive(Clone, Debug, Eq, Fail, PartialEq, Serialize, Deserialize)] /// Libwallet error types pub enum ErrorKind { /// SECP error diff --git a/core/src/ser.rs b/core/src/ser.rs index babebae79a..cb0c1976c7 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -35,10 +35,17 @@ use std::time::Duration; use std::{cmp, error, fmt}; /// Possible errors deriving from serializing or deserializing. -#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum Error { /// Wraps an io error produced when reading or writing - IOErr(String, io::ErrorKind), + IOErr( + String, + #[serde( + serialize_with = "serialize_error_kind", + deserialize_with = "deserialize_error_kind" + )] + io::ErrorKind, + ), /// Expected a given value that wasn't found UnexpectedData { /// What we wanted @@ -813,3 +820,446 @@ impl AsFixedBytes for crate::keychain::Identifier { IDENTIFIER_SIZE } } + +// serializer for io::Errorkind, originally auto-generated by serde-derive +// slightly modified to handle the #[non_exhaustive] tag on io::ErrorKind +fn serialize_error_kind( + kind: &io::ErrorKind, + serializer: S, +) -> serde::export::Result +where + S: serde::Serializer, +{ + match *kind { + io::ErrorKind::NotFound => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 0u32, "NotFound") + } + io::ErrorKind::PermissionDenied => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 1u32, + "PermissionDenied", + ), + io::ErrorKind::ConnectionRefused => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 2u32, + "ConnectionRefused", + ), + io::ErrorKind::ConnectionReset => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 3u32, + "ConnectionReset", + ), + io::ErrorKind::ConnectionAborted => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 4u32, + "ConnectionAborted", + ), + io::ErrorKind::NotConnected => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 5u32, "NotConnected") + } + io::ErrorKind::AddrInUse => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 6u32, "AddrInUse") + } + io::ErrorKind::AddrNotAvailable => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 7u32, + "AddrNotAvailable", + ), + io::ErrorKind::BrokenPipe => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 8u32, "BrokenPipe") + } + io::ErrorKind::AlreadyExists => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 9u32, + "AlreadyExists", + ), + io::ErrorKind::WouldBlock => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 10u32, "WouldBlock") + } + io::ErrorKind::InvalidInput => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 11u32, + "InvalidInput", + ), + io::ErrorKind::InvalidData => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 12u32, "InvalidData") + } + io::ErrorKind::TimedOut => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 13u32, "TimedOut") + } + io::ErrorKind::WriteZero => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 14u32, "WriteZero") + } + io::ErrorKind::Interrupted => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 15u32, "Interrupted") + } + io::ErrorKind::Other => { + serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 16u32, "Other") + } + io::ErrorKind::UnexpectedEof => serde::Serializer::serialize_unit_variant( + serializer, + "ErrorKind", + 17u32, + "UnexpectedEof", + ), + // #[non_exhaustive] is used on the definition of ErrorKind for future compatability + // That means match statements always need to match on _. + // The downside here is that rustc won't be able to warn us if io::ErrorKind another + // field is added to io::ErrorKind + _ => serde::Serializer::serialize_unit_variant(serializer, "ErrorKind", 16u32, "Other"), + } +} + +// deserializer for io::Errorkind, originally auto-generated by serde-derive +fn deserialize_error_kind<'de, D>(deserializer: D) -> serde::export::Result +where + D: serde::Deserializer<'de>, +{ + #[allow(non_camel_case_types)] + enum Field { + field0, + field1, + field2, + field3, + field4, + field5, + field6, + field7, + field8, + field9, + field10, + field11, + field12, + field13, + field14, + field15, + field16, + field17, + } + struct FieldVisitor; + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = Field; + fn expecting( + &self, + formatter: &mut serde::export::Formatter, + ) -> serde::export::fmt::Result { + serde::export::Formatter::write_str(formatter, "variant identifier") + } + fn visit_u64(self, value: u64) -> serde::export::Result + where + E: serde::de::Error, + { + match value { + 0u64 => serde::export::Ok(Field::field0), + 1u64 => serde::export::Ok(Field::field1), + 2u64 => serde::export::Ok(Field::field2), + 3u64 => serde::export::Ok(Field::field3), + 4u64 => serde::export::Ok(Field::field4), + 5u64 => serde::export::Ok(Field::field5), + 6u64 => serde::export::Ok(Field::field6), + 7u64 => serde::export::Ok(Field::field7), + 8u64 => serde::export::Ok(Field::field8), + 9u64 => serde::export::Ok(Field::field9), + 10u64 => serde::export::Ok(Field::field10), + 11u64 => serde::export::Ok(Field::field11), + 12u64 => serde::export::Ok(Field::field12), + 13u64 => serde::export::Ok(Field::field13), + 14u64 => serde::export::Ok(Field::field14), + 15u64 => serde::export::Ok(Field::field15), + 16u64 => serde::export::Ok(Field::field16), + 17u64 => serde::export::Ok(Field::field17), + _ => serde::export::Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(value), + &"variant index 0 <= i < 18", + )), + } + } + fn visit_str(self, value: &str) -> serde::export::Result + where + E: serde::de::Error, + { + match value { + "NotFound" => serde::export::Ok(Field::field0), + "PermissionDenied" => serde::export::Ok(Field::field1), + "ConnectionRefused" => serde::export::Ok(Field::field2), + "ConnectionReset" => serde::export::Ok(Field::field3), + "ConnectionAborted" => serde::export::Ok(Field::field4), + "NotConnected" => serde::export::Ok(Field::field5), + "AddrInUse" => serde::export::Ok(Field::field6), + "AddrNotAvailable" => serde::export::Ok(Field::field7), + "BrokenPipe" => serde::export::Ok(Field::field8), + "AlreadyExists" => serde::export::Ok(Field::field9), + "WouldBlock" => serde::export::Ok(Field::field10), + "InvalidInput" => serde::export::Ok(Field::field11), + "InvalidData" => serde::export::Ok(Field::field12), + "TimedOut" => serde::export::Ok(Field::field13), + "WriteZero" => serde::export::Ok(Field::field14), + "Interrupted" => serde::export::Ok(Field::field15), + "Other" => serde::export::Ok(Field::field16), + "UnexpectedEof" => serde::export::Ok(Field::field17), + _ => serde::export::Err(serde::de::Error::unknown_variant(value, VARIANTS)), + } + } + fn visit_bytes(self, value: &[u8]) -> serde::export::Result + where + E: serde::de::Error, + { + match value { + b"NotFound" => serde::export::Ok(Field::field0), + b"PermissionDenied" => serde::export::Ok(Field::field1), + b"ConnectionRefused" => serde::export::Ok(Field::field2), + b"ConnectionReset" => serde::export::Ok(Field::field3), + b"ConnectionAborted" => serde::export::Ok(Field::field4), + b"NotConnected" => serde::export::Ok(Field::field5), + b"AddrInUse" => serde::export::Ok(Field::field6), + b"AddrNotAvailable" => serde::export::Ok(Field::field7), + b"BrokenPipe" => serde::export::Ok(Field::field8), + b"AlreadyExists" => serde::export::Ok(Field::field9), + b"WouldBlock" => serde::export::Ok(Field::field10), + b"InvalidInput" => serde::export::Ok(Field::field11), + b"InvalidData" => serde::export::Ok(Field::field12), + b"TimedOut" => serde::export::Ok(Field::field13), + b"WriteZero" => serde::export::Ok(Field::field14), + b"Interrupted" => serde::export::Ok(Field::field15), + b"Other" => serde::export::Ok(Field::field16), + b"UnexpectedEof" => serde::export::Ok(Field::field17), + _ => { + let value = &serde::export::from_utf8_lossy(value); + serde::export::Err(serde::de::Error::unknown_variant(value, VARIANTS)) + } + } + } + } + impl<'de> serde::Deserialize<'de> for Field { + #[inline] + fn deserialize(deserializer: D) -> serde::export::Result + where + D: serde::Deserializer<'de>, + { + serde::Deserializer::deserialize_identifier(deserializer, FieldVisitor) + } + } + struct Visitor<'de> { + marker: serde::export::PhantomData, + lifetime: serde::export::PhantomData<&'de ()>, + } + impl<'de> serde::de::Visitor<'de> for Visitor<'de> { + type Value = io::ErrorKind; + fn expecting( + &self, + formatter: &mut serde::export::Formatter, + ) -> serde::export::fmt::Result { + serde::export::Formatter::write_str(formatter, "enum io::ErrorKind") + } + fn visit_enum(self, data: A) -> serde::export::Result + where + A: serde::de::EnumAccess<'de>, + { + match match serde::de::EnumAccess::variant(data) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + } { + (Field::field0, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::NotFound) + } + (Field::field1, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::PermissionDenied) + } + (Field::field2, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::ConnectionRefused) + } + (Field::field3, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::ConnectionReset) + } + (Field::field4, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::ConnectionAborted) + } + (Field::field5, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::NotConnected) + } + (Field::field6, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::AddrInUse) + } + (Field::field7, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::AddrNotAvailable) + } + (Field::field8, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::BrokenPipe) + } + (Field::field9, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::AlreadyExists) + } + (Field::field10, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::WouldBlock) + } + (Field::field11, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::InvalidInput) + } + (Field::field12, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::InvalidData) + } + (Field::field13, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::TimedOut) + } + (Field::field14, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::WriteZero) + } + (Field::field15, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::Interrupted) + } + (Field::field16, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::Other) + } + (Field::field17, variant) => { + match serde::de::VariantAccess::unit_variant(variant) { + serde::export::Ok(val) => val, + serde::export::Err(err) => { + return serde::export::Err(err); + } + }; + serde::export::Ok(io::ErrorKind::UnexpectedEof) + } + } + } + } + const VARIANTS: &'static [&'static str] = &[ + "NotFound", + "PermissionDenied", + "ConnectionRefused", + "ConnectionReset", + "ConnectionAborted", + "NotConnected", + "AddrInUse", + "AddrNotAvailable", + "BrokenPipe", + "AlreadyExists", + "WouldBlock", + "InvalidInput", + "InvalidData", + "TimedOut", + "WriteZero", + "Interrupted", + "Other", + "UnexpectedEof", + ]; + serde::Deserializer::deserialize_enum( + deserializer, + "ErrorKind", + VARIANTS, + Visitor { + marker: serde::export::PhantomData::, + lifetime: serde::export::PhantomData, + }, + ) +} diff --git a/keychain/src/extkey_bip32.rs b/keychain/src/extkey_bip32.rs index 392410cb86..ab4dfdd624 100644 --- a/keychain/src/extkey_bip32.rs +++ b/keychain/src/extkey_bip32.rs @@ -295,7 +295,7 @@ impl serde::Serialize for ChildNumber { } /// A BIP32 error -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] pub enum Error { /// A pk->pk derivation was attempted on a hardened key CannotDeriveFromHardenedKey, diff --git a/keychain/src/mnemonic.rs b/keychain/src/mnemonic.rs index 1dfb66bf49..798af95350 100644 --- a/keychain/src/mnemonic.rs +++ b/keychain/src/mnemonic.rs @@ -29,7 +29,7 @@ lazy_static! { } /// An error that might occur during mnemonic decoding -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub enum Error { /// Invalid word encountered BadWord(String), diff --git a/keychain/src/types.rs b/keychain/src/types.rs index 66e5ba95fd..2eb5de5dc2 100644 --- a/keychain/src/types.rs +++ b/keychain/src/types.rs @@ -38,7 +38,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; // Size of an identifier in bytes pub const IDENTIFIER_SIZE: usize = 17; -#[derive(PartialEq, Eq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub enum Error { Secp(secp::Error), KeyDerivation(extkey_bip32::Error), diff --git a/util/Cargo.toml b/util/Cargo.toml index 96f3f42294..058289d2b4 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -28,5 +28,5 @@ zeroize = "0.5.2" #git = "https://github.com/mimblewimble/rust-secp256k1-zkp" #tag = "grin_integration_29" #path = "../../rust-secp256k1-zkp" -version = "0.7.4" +version = "0.7.5" features = ["bullet-proof-sizing"]