diff --git a/Cargo.toml b/Cargo.toml index 160143bf5a94..a9d510b058cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,6 @@ members = [ "node/clock", "crypto", "encoding", - "ipld/cid" + "ipld/cid", + "ipld", ] diff --git a/Makefile b/Makefile index 024cde0f74d5..621e423ac7b1 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,8 @@ clean: @cargo clean -p interpreter @cargo clean -p crypto @cargo clean -p encoding + @cargo clean -p ferret_cid + @cargo clean -p ferret_ipld @echo "Done cleaning." lint: clean license diff --git a/blockchain/blocks/Cargo.toml b/blockchain/blocks/Cargo.toml index 8bba5f661d2e..09494f89b96e 100644 --- a/blockchain/blocks/Cargo.toml +++ b/blockchain/blocks/Cargo.toml @@ -5,11 +5,11 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -address = {path = "../../vm/address"} -crypto = {path = "../../crypto"} -message = {path = "../../vm/message"} -clock = {path = "../../node/clock"} -cid = {path = "../../ipld/cid"} -multihash = "0.8.0" +address = { path = "../../vm/address" } +crypto = { path = "../../crypto" } +message = { path = "../../vm/message" } +clock = { path = "../../node/clock" } +cid = { package = "ferret_cid", path = "../../ipld/cid" } +multihash = "0.9.3" derive_builder = "0.9" serde_cbor = "0.11.0" diff --git a/blockchain/blocks/src/block.rs b/blockchain/blocks/src/block.rs index 9f7c57c76a02..6bb6ccf7e62d 100644 --- a/blockchain/blocks/src/block.rs +++ b/blockchain/blocks/src/block.rs @@ -6,7 +6,7 @@ use super::ticket::Ticket; use super::TipSetKeys; use address::Address; -use cid::{Cid, Codec, Prefix, Version}; +use cid::Cid; use clock::ChainEpoch; use crypto::Signature; use derive_builder::Builder; @@ -112,13 +112,7 @@ impl BlockHeader { // Change DEFAULT_HASH_FUNCTION to utilize blake2b // // Currently content id for headers will be incomplete until encoding and supporting libraries are completed - let c = Prefix { - version: Version::V1, - codec: Codec::DagCBOR, - mh_type: DEFAULT_HASH_FUNCTION, - mh_len: 8, - }; - let new_cid = Cid::new_from_prefix(&c, &self.cached_bytes); + let new_cid = Cid::from_bytes_default(&self.cached_bytes).unwrap(); self.cached_cid = new_cid; self.cached_cid.clone() } diff --git a/blockchain/blocks/src/tipset.rs b/blockchain/blocks/src/tipset.rs index 7897edfb8a67..15ca9eeb030e 100644 --- a/blockchain/blocks/src/tipset.rs +++ b/blockchain/blocks/src/tipset.rs @@ -106,7 +106,7 @@ impl Tipset { // break ticket ties with the header CIDs, which are distinct sorted_headers.sort_by_key(|header| { let mut h = header.clone(); - (h.ticket.vrfproof.clone(), h.cid().hash.clone()) + (h.ticket.vrfproof.clone(), h.cid().to_bytes()) }); // TODO @@ -175,7 +175,7 @@ mod tests { use super::*; use crate::block::TxMeta; use address::Address; - use cid::{Cid, Codec}; + use cid::Cid; use clock::ChainEpoch; use crypto::VRFResult; @@ -183,9 +183,7 @@ mod tests { const CACHED_BYTES: [u8; 1] = [0]; fn template_key(data: &[u8]) -> Cid { - let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); - let cid = Cid::from_bytes_v1(Codec::DagCBOR, h); - return cid; + Cid::from_bytes_default(data).unwrap() } // key_setup returns a vec of 4 distinct CIDs diff --git a/blockchain/chain/Cargo.toml b/blockchain/chain/Cargo.toml index 81a5f2e806c6..ada47e0299f8 100644 --- a/blockchain/chain/Cargo.toml +++ b/blockchain/chain/Cargo.toml @@ -5,13 +5,13 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -blocks = {path = "../blocks"} -network = {path = "../../node/network"} -cid = {path = "../../ipld/cid"} -clock = {path = "../../node/clock"} +blocks = { path = "../blocks" } +network = { path = "../../node/network" } +cid = { package = "ferret_cid", path = "../../ipld/cid" } +clock = { path = "../../node/clock" } num-bigint = "0.2.3" [dev-dependencies] -address = {path = "../../vm/address"} -crypto = {path = "../../crypto"} -multihash = "0.8.0" \ No newline at end of file +address = { path = "../../vm/address" } +crypto = { path = "../../crypto" } +multihash = "0.9.3" diff --git a/blockchain/chain/src/store/tip_index.rs b/blockchain/chain/src/store/tip_index.rs index 9577dd9d4eb2..64a8e57a0cb7 100644 --- a/blockchain/chain/src/store/tip_index.rs +++ b/blockchain/chain/src/store/tip_index.rs @@ -102,7 +102,7 @@ mod tests { use super::*; use address::Address; use blocks::{BlockHeader, Ticket, Tipset, TxMeta}; - use cid::{Cid, Codec}; + use cid::Cid; use clock::ChainEpoch; use crypto::VRFResult; @@ -110,8 +110,7 @@ mod tests { const CACHED_BYTES: [u8; 1] = [0]; fn template_key(data: &[u8]) -> Cid { - let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); - Cid::from_bytes_v1(Codec::DagCBOR, &h) + Cid::from_bytes_default(data).unwrap() } // key_setup returns a vec of distinct CIDs diff --git a/blockchain/sync_manager/Cargo.toml b/blockchain/sync_manager/Cargo.toml index 9cfefd2640f4..effdf9597128 100644 --- a/blockchain/sync_manager/Cargo.toml +++ b/blockchain/sync_manager/Cargo.toml @@ -5,8 +5,8 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -address = {path = "../../vm/address"} -blocks = {path = "../blocks"} +address = { path = "../../vm/address" } +blocks = { path = "../blocks" } [dev-dependencies] -cid = {path = "../../ipld/cid"} +cid = { package = "ferret_cid", path = "../../ipld/cid" } diff --git a/blockchain/sync_manager/src/bucket.rs b/blockchain/sync_manager/src/bucket.rs index d6b296ef7f2f..377cd8300109 100644 --- a/blockchain/sync_manager/src/bucket.rs +++ b/blockchain/sync_manager/src/bucket.rs @@ -74,11 +74,11 @@ mod tests { use super::*; use address::Address; use blocks::{BlockHeader, TipSetKeys}; - use cid::{Cid, Codec}; + use cid::Cid; fn create_header(weight: u64, parent_bz: &[u8], cached_bytes: &[u8]) -> BlockHeader { let x = TipSetKeys { - cids: vec![Cid::from_bytes_v1(Codec::DagCBOR, parent_bz)], + cids: vec![Cid::from_bytes_default(parent_bz).unwrap()], }; BlockHeader::builder() .parents(x) diff --git a/blockchain/sync_manager/tests/manager_test.rs b/blockchain/sync_manager/tests/manager_test.rs index b06272d436aa..a1a257ba037c 100644 --- a/blockchain/sync_manager/tests/manager_test.rs +++ b/blockchain/sync_manager/tests/manager_test.rs @@ -3,12 +3,12 @@ use address::Address; use blocks::{BlockHeader, TipSetKeys, Tipset}; -use cid::{Cid, Codec}; +use cid::Cid; use sync_manager::SyncManager; fn create_header(weight: u64, parent_bz: &[u8], cached_bytes: &[u8]) -> BlockHeader { let x = TipSetKeys { - cids: vec![Cid::from_bytes_v1(Codec::DagCBOR, parent_bz)], + cids: vec![Cid::from_bytes_default(parent_bz).unwrap()], }; BlockHeader::builder() .parents(x) diff --git a/encoding/Cargo.toml b/encoding/Cargo.toml index 5ef9e483e93f..1ba79802456e 100644 --- a/encoding/Cargo.toml +++ b/encoding/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" authors = ["ChainSafe Systems "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] blake2b_simd = "0.5.9" -serde_cbor = "0.11.0" +serde = { version = "1.0", features = ["derive"] } +serde_bytes = "0.11.3" +serde_cbor = { version = "0.11.0", features = ["tags"] } diff --git a/encoding/src/cbor.rs b/encoding/src/cbor.rs index 05ce0405f4f9..52683f6a7413 100644 --- a/encoding/src/cbor.rs +++ b/encoding/src/cbor.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use super::errors::Error; +use crate::{ser, to_vec}; /// Implemented for types that are CBOR encodable -pub trait Cbor { - fn unmarshal_cbor(bz: &[u8]) -> Result - where - Self: Sized; - fn marshal_cbor(&self) -> Result, Error>; +pub trait Cbor: ser::Serialize { + fn marshal_cbor(&self) -> Result, Error> { + Ok(to_vec(&self)?) + } } diff --git a/encoding/src/lib.rs b/encoding/src/lib.rs index 0a59fd821139..6579eea1f06a 100644 --- a/encoding/src/lib.rs +++ b/encoding/src/lib.rs @@ -5,6 +5,10 @@ mod cbor; mod errors; mod hash; +pub use serde::{de, ser}; +pub use serde_bytes; +pub use serde_cbor::{from_reader, from_slice, tags, to_vec, to_writer}; + pub use self::cbor::*; pub use self::errors::*; pub use self::hash::*; diff --git a/ipld/Cargo.toml b/ipld/Cargo.toml new file mode 100644 index 000000000000..82c7b6044eb3 --- /dev/null +++ b/ipld/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ferret_ipld" +version = "0.1.0" +authors = ["ChainSafe Systems "] +edition = "2018" + +[dependencies] +encoding = { path = "../encoding" } +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +cid = { package = "ferret_cid", path = "../ipld/cid" } +encoding = { path = "../encoding" } diff --git a/ipld/cid/Cargo.toml b/ipld/cid/Cargo.toml index ebe680c35ab1..9f13ae95574a 100644 --- a/ipld/cid/Cargo.toml +++ b/ipld/cid/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "cid" +name = "ferret_cid" version = "0.1.0" authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -dep_cid = {package = "cid", version = "0.3.1"} -multihash = "0.8.0" +multihash = "0.9.3" multibase = "0.6.0" integer-encoding = "1.0.3" +encoding = { path = "../../encoding" } +serde = { version = "1.0", features = ["derive"] } diff --git a/ipld/cid/src/codec.rs b/ipld/cid/src/codec.rs new file mode 100644 index 000000000000..1823faf0fe02 --- /dev/null +++ b/ipld/cid/src/codec.rs @@ -0,0 +1,56 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use crate::Error; + +macro_rules! build_codec_enum { + {$( $val:expr => $var:ident, )*} => { + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + pub enum Codec { + $( $var, )* + } + + use Codec::*; + + impl Codec { + /// Convert a number to the matching codec + pub fn from(raw: u64) -> Result { + match raw { + $( $val => Ok($var), )* + _ => Err(Error::UnknownCodec), + } + } + } + + impl From for u64 { + /// Convert to the matching integer code + fn from(codec: Codec) -> u64 { + match codec { + $( $var => $val, )* + + } + } + } + } +} + +build_codec_enum! { + 0x55 => Raw, + 0x70 => DagProtobuf, + 0x71 => DagCBOR, + 0x78 => GitRaw, + 0x90 => EthereumBlock, + 0x91 => EthereumBlockList, + 0x92 => EthereumTxTrie, + 0x93 => EthereumTx, + 0x94 => EthereumTxReceiptTrie, + 0x95 => EthereumTxReceipt, + 0x96 => EthereumStateTrie, + 0x97 => EthereumAccountSnapshot, + 0x98 => EthereumStorageTrie, + 0xb0 => BitcoinBlock, + 0xb1 => BitcoinTx, + 0xc0 => ZcashBlock, + 0xc1 => ZcashTx, + 0x0129 => DagJSON, +} diff --git a/ipld/cid/src/error.rs b/ipld/cid/src/error.rs new file mode 100644 index 000000000000..b8319633bca0 --- /dev/null +++ b/ipld/cid/src/error.rs @@ -0,0 +1,70 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use multibase; +use multihash; +use std::{error, fmt, io}; + +/// Error types +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Error { + UnknownCodec, + InputTooShort, + ParsingError, + InvalidCidVersion, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(error::Error::description(self)) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + use self::Error::*; + + match *self { + UnknownCodec => "Unknown codec", + InputTooShort => "Input too short", + ParsingError => "Failed to parse multihash", + InvalidCidVersion => "Unrecognized CID version", + } + } +} + +impl From for Error { + fn from(_: io::Error) -> Error { + Error::ParsingError + } +} + +impl From for Error { + fn from(_: multibase::Error) -> Error { + Error::ParsingError + } +} + +impl From for Error { + fn from(_: multihash::DecodeOwnedError) -> Error { + Error::ParsingError + } +} + +impl From for Error { + fn from(_: multihash::EncodeError) -> Error { + Error::ParsingError + } +} + +impl From for Error { + fn from(_: multihash::DecodeError) -> Error { + Error::ParsingError + } +} + +impl From for fmt::Error { + fn from(_: Error) -> fmt::Error { + fmt::Error {} + } +} diff --git a/ipld/cid/src/lib.rs b/ipld/cid/src/lib.rs index c8a62aa810d2..782d373b3ebf 100644 --- a/ipld/cid/src/lib.rs +++ b/ipld/cid/src/lib.rs @@ -1,83 +1,230 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 +mod codec; +mod error; mod to_cid; +mod version; +pub use self::codec::Codec; +pub use self::error::Error; pub use self::to_cid::ToCid; -pub use dep_cid::{Cid as BaseCid, Codec, Error, Prefix, Version}; -use std::ops::{Deref, DerefMut}; +pub use self::version::Version; +use encoding::{de, ser, serde_bytes, tags::Tagged, Cbor}; +use integer_encoding::{VarIntReader, VarIntWriter}; +use multihash::{Hash, Multihash}; +use std::fmt; +use std::io::Cursor; -/// Representation of an IPLD Cid -#[derive(PartialEq, Eq, Clone, Debug, Hash)] -pub struct Cid { - cid: BaseCid, +const CBOR_TAG_CID: u64 = 42; + +/// Prefix represents all metadata of a CID, without the actual content. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Prefix { + pub version: Version, + pub codec: Codec, + pub mh_type: Hash, + pub mh_len: usize, } -impl From for Cid { - fn from(cid: BaseCid) -> Self { - Self { cid } - } +/// Representation of a IPLD CID. +#[derive(Eq, Clone, Debug)] +pub struct Cid { + pub version: Version, + pub codec: Codec, + pub hash: Multihash, } impl Default for Cid { fn default() -> Self { - Self { - cid: BaseCid::new(Codec::Raw, Version::V0, &[]), - } + Self::new( + Codec::Raw, + Version::V1, + multihash::encode(Hash::Blake2b512, &[]).unwrap(), + ) } } -impl Deref for Cid { - type Target = BaseCid; - fn deref(&self) -> &Self::Target { - &self.cid - } -} +impl Cbor for Cid {} -impl DerefMut for Cid { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cid +impl ser::Serialize for Cid { + fn serialize(&self, s: S) -> Result + where + S: ser::Serializer, + { + let cid_bytes = self.to_bytes(); + let value = serde_bytes::Bytes::new(&cid_bytes); + Tagged::new(Some(CBOR_TAG_CID), &value).serialize(s) } } -impl Cid { - /// Cid constructor - pub fn new(cid: BaseCid) -> Self { - Self { cid } - } - /// Constructs a v0 cid with a given codec and bytes - pub fn from_bytes_v0(codec: Codec, bz: B) -> Self +impl<'de> de::Deserialize<'de> for Cid { + fn deserialize(deserializer: D) -> Result where - B: AsRef<[u8]>, + D: de::Deserializer<'de>, { - Self { - cid: BaseCid::new(codec, Version::V0, bz.as_ref()), + let tagged = Tagged::::deserialize(deserializer)?; + match tagged.tag { + // TODO verify this + Some(CBOR_TAG_CID) | None => Ok(tagged + .value + .to_vec() + .to_cid() + .map_err(|e| de::Error::custom(e.to_string()))?), + Some(_) => Err(de::Error::custom("unexpected tag")), } } - /// Constructs a v1 cid with a given codec and bytes - pub fn from_bytes_v1(codec: Codec, bz: B) -> Self - where - B: AsRef<[u8]>, - { - Self { - cid: BaseCid::new(codec, Version::V1, bz.as_ref()), +} + +impl Cid { + /// Create a new CID. + pub fn new(codec: Codec, version: Version, hash: Multihash) -> Cid { + Cid { + version, + codec, + hash, } } + /// Constructs a cid with bytes using default version and codec + pub fn from_bytes_default(bz: &[u8]) -> Result { + let prefix = Prefix { + version: Version::V1, + codec: Codec::DagCBOR, + mh_type: Hash::Blake2b512, + mh_len: 64 - 1, // TODO verify cid hash length and type + }; + Ok(Self::new_from_prefix(&prefix, bz)?) + } + + /// Constructs a cid with a CBOR encodable structure + pub fn from_cbor_default(bz: B) -> Result { + Ok(Self::from_bytes_default( + &bz.marshal_cbor().map_err(|_| Error::ParsingError)?, + )?) + } + /// Create a new CID from raw data (binary or multibase encoded string) - pub fn from(data: T) -> Result { + pub fn from_raw_cid(data: T) -> Result { data.to_cid() } /// Create a new CID from a prefix and some data. - pub fn new_from_prefix(prefix: &Prefix, data: &[u8]) -> Cid { - let mut hash = multihash::encode(prefix.mh_type.to_owned(), data).unwrap(); - hash.truncate(prefix.mh_len + 2); - BaseCid { + pub fn new_from_prefix(prefix: &Prefix, data: &[u8]) -> Result { + let hash = multihash::encode(prefix.mh_type.to_owned(), data)?; + Ok(Cid { version: prefix.version, codec: prefix.codec.to_owned(), hash, + }) + } + + fn to_string_v0(&self) -> String { + use multibase::{encode, Base}; + + let mut string = encode(Base::Base58btc, self.hash.clone()); + + // Drop the first character as v0 does not know + // about multibase + string.remove(0); + + string + } + + fn to_string_v1(&self) -> String { + use multibase::{encode, Base}; + + encode(Base::Base58btc, self.to_bytes().as_slice()) + } + + fn to_bytes_v0(&self) -> Vec { + self.hash.clone().into_bytes() + } + + fn to_bytes_v1(&self) -> Vec { + let mut res = Vec::with_capacity(16); + res.write_varint(u64::from(self.version)).unwrap(); + res.write_varint(u64::from(self.codec)).unwrap(); + res.extend_from_slice(self.hash.as_bytes()); + + res + } + + /// Returns encoded bytes of a cid + pub fn to_bytes(&self) -> Vec { + match self.version { + Version::V0 => self.to_bytes_v0(), + Version::V1 => self.to_bytes_v1(), } - .into() + } + + /// Returns prefix for Cid format + pub fn prefix(&self) -> Prefix { + Prefix { + version: self.version, + codec: self.codec.to_owned(), + mh_type: self.hash.algorithm(), + mh_len: self.hash.as_bytes().len(), + } + } +} + +impl std::hash::Hash for Cid { + fn hash(&self, state: &mut H) { + self.to_bytes().hash(state); + } +} + +impl PartialEq for Cid { + fn eq(&self, other: &Self) -> bool { + self.to_bytes() == other.to_bytes() + } +} + +impl fmt::Display for Cid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let encoded = match self.version { + Version::V0 => self.to_string_v0(), + Version::V1 => self.to_string_v1(), + }; + write!(f, "{}", encoded) + } +} + +impl Prefix { + /// Generate new prefix from encoded bytes + pub fn new_from_bytes(data: &[u8]) -> Result { + let mut cur = Cursor::new(data); + + let raw_version = cur.read_varint()?; + let raw_codec = cur.read_varint()?; + let raw_mh_type: u64 = cur.read_varint()?; + + let version = Version::from(raw_version)?; + let codec = Codec::from(raw_codec)?; + + let mh_type = Hash::from_code(raw_mh_type as u16).ok_or(Error::ParsingError)?; + + let mh_len = cur.read_varint()?; + + Ok(Prefix { + version, + codec, + mh_type, + mh_len, + }) + } + + /// Encodes prefix to bytes + pub fn as_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(4); + + // io can't fail on Vec + res.write_varint(u64::from(self.version)).unwrap(); + res.write_varint(u64::from(self.codec)).unwrap(); + res.write_varint(self.mh_type.code() as u64).unwrap(); + res.write_varint(self.mh_len as u64).unwrap(); + + res } } diff --git a/ipld/cid/src/to_cid.rs b/ipld/cid/src/to_cid.rs index 4bc7458ac708..bca9107e2a3d 100644 --- a/ipld/cid/src/to_cid.rs +++ b/ipld/cid/src/to_cid.rs @@ -1,14 +1,14 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 +use crate::{Cid, Codec, Error, Version}; use integer_encoding::VarIntReader; use multibase; -use multihash; +use multihash::Multihash; use std::io::Cursor; use std::str::FromStr; -use crate::{BaseCid, Cid, Codec, Error, Version}; - +/// Trait used to convert objects to Cid (Currently not necessary, but keeping in line with dep) pub trait ToCid { fn to_cid(&self) -> Result; } @@ -82,9 +82,9 @@ impl ToCid for [u8] { fn to_cid(&self) -> Result { if Version::is_v0_binary(self) { // Verify that hash can be decoded, this is very cheap - multihash::decode(self)?; + let hash = Multihash::from_bytes(self.to_vec())?; - Ok(BaseCid::new(Codec::DagCBOR, Version::V0, self).into()) + Ok(Cid::new(Codec::DagCBOR, Version::V0, hash)) } else { let mut cur = Cursor::new(self); let raw_version = cur.read_varint()?; @@ -96,9 +96,10 @@ impl ToCid for [u8] { let hash = &self[cur.position() as usize..]; // Verify that hash can be decoded, this is very cheap - multihash::decode(hash)?; + // TODO verify this (was previously using all bytes) + let hash = Multihash::from_bytes(hash.to_vec())?; - Ok(BaseCid::new(codec, version, hash).into()) + Ok(Cid::new(codec, version, hash)) } } } diff --git a/ipld/cid/src/version.rs b/ipld/cid/src/version.rs new file mode 100644 index 000000000000..b8e4beab2ff0 --- /dev/null +++ b/ipld/cid/src/version.rs @@ -0,0 +1,45 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use crate::Error; + +/// Cid protocol version +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Version { + V0, + V1, +} + +use Version::*; + +impl Version { + /// Generates version from integer value + pub fn from(raw: u64) -> Result { + match raw { + 0 => Ok(V0), + 1 => Ok(V1), + _ => Err(Error::InvalidCidVersion), + } + } + + /// Returns true if string is version 0 + pub fn is_v0_str(data: &str) -> bool { + // v0 is a base58btc encoded sha hash, so it has + // fixed length and always begins with "Qm" + data.len() == 46 && data.starts_with("Qm") + } + + /// Returns true if bytes is version 0 + pub fn is_v0_binary(data: &[u8]) -> bool { + data.len() == 34 && data.starts_with(&[0x12, 0x20]) + } +} + +impl From for u64 { + fn from(ver: Version) -> u64 { + match ver { + V0 => 0, + V1 => 1, + } + } +} diff --git a/ipld/cid/tests/base_cid_tests.rs b/ipld/cid/tests/base_cid_tests.rs new file mode 100644 index 000000000000..b18e2df76e06 --- /dev/null +++ b/ipld/cid/tests/base_cid_tests.rs @@ -0,0 +1,156 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use encoding::{from_slice, to_vec, Cbor}; +use ferret_cid::{Cid, Codec, Error, Prefix, Version}; +use multihash; +use multihash::Hash::Blake2b512; +use serde::Serialize; +use std::collections::HashMap; + +#[test] +fn basic_marshalling() { + let h = multihash::encode(multihash::Hash::SHA2256, b"beep boop").unwrap(); + + let cid = Cid::new(Codec::DagProtobuf, Version::V1, h); + + let data = cid.to_bytes(); + let out = Cid::from_raw_cid(data).unwrap(); + + assert_eq!(cid, out); + + let s = cid.to_string(); + let out2 = Cid::from_raw_cid(&s[..]).unwrap(); + + assert_eq!(cid, out2); +} + +#[test] +fn empty_string() { + assert_eq!(Cid::from_raw_cid(""), Err(Error::InputTooShort)); +} + +#[test] +fn v0_handling() { + let old = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"; + let cid = Cid::from_raw_cid(old).unwrap(); + + assert_eq!(cid.version, Version::V0); + assert_eq!(cid.to_string(), old); +} + +#[test] +fn from_str() { + let cid: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" + .parse() + .unwrap(); + assert_eq!(cid.version, Version::V0); + + let bad = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII".parse::(); + assert_eq!(bad, Err(Error::ParsingError)); +} + +#[test] +fn v0_error() { + let bad = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII"; + assert_eq!(Cid::from_raw_cid(bad), Err(Error::ParsingError)); +} + +#[test] +fn prefix_roundtrip() { + let data = b"awesome test content"; + let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); + + let cid = Cid::new(Codec::DagProtobuf, Version::V1, h); + let prefix = cid.prefix(); + + let cid2 = Cid::new_from_prefix(&prefix, data).unwrap(); + + assert_eq!(cid, cid2); + + let prefix_bytes = prefix.as_bytes(); + let prefix2 = Prefix::new_from_bytes(&prefix_bytes).unwrap(); + + assert_eq!(prefix, prefix2); +} + +#[test] +fn from() { + let the_hash = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"; + + let cases = vec![ + format!("/ipfs/{:}", &the_hash), + format!("https://ipfs.io/ipfs/{:}", &the_hash), + format!("http://localhost:8080/ipfs/{:}", &the_hash), + ]; + + for case in cases { + let cid = Cid::from_raw_cid(case).unwrap(); + assert_eq!(cid.version, Version::V0); + assert_eq!(cid.to_string(), the_hash); + } +} + +#[test] +fn test_hash() { + let data: Vec = vec![1, 2, 3]; + let prefix = Prefix { + version: Version::V0, + codec: Codec::DagProtobuf, + mh_type: multihash::Hash::SHA2256, + mh_len: 32, + }; + let mut map = HashMap::new(); + let cid = Cid::new_from_prefix(&prefix, &data).unwrap(); + map.insert(cid.clone(), data.clone()); + assert_eq!(&data, map.get(&cid).unwrap()); +} + +#[test] +fn test_default() { + let data: Vec = vec![1, 2, 3]; + + let cid = Cid::from_bytes_default(&data).unwrap(); + + let prefix = cid.prefix(); + assert_eq!(prefix.version, Version::V1); + assert_eq!(prefix.codec, Codec::DagCBOR); + assert_eq!(prefix.mh_type, Blake2b512); + assert_eq!( + prefix.mh_len, + // 4 is Blake2b512 code length (3) + 1, change if default changes + (Blake2b512.size() + 4) as usize + ); +} + +#[derive(Serialize, Copy, Clone)] +struct TestCborStruct { + name: &'static str, +} +impl Cbor for TestCborStruct {} + +#[test] +fn test_cbor_to_cid() { + let obj = TestCborStruct { name: "test" }; + + let enc = Cid::from_cbor_default(obj).unwrap(); + let bz_enc = Cid::from_bytes_default(&obj.marshal_cbor().unwrap()).unwrap(); + assert_eq!(enc, bz_enc); +} + +#[test] +fn vector_cid_serialize_round() { + let cids = vec![ + Cid::from_bytes_default(&[0, 1]).unwrap(), + Cid::from_bytes_default(&[1, 2]).unwrap(), + Cid::from_bytes_default(&[3, 2]).unwrap(), + ]; + + // Serialize cids with cbor + let enc = to_vec(&cids).unwrap(); + + // decode cbor bytes to vector again + let dec: Vec = from_slice(&enc).unwrap(); + + assert_eq!(cids, dec); +} diff --git a/ipld/src/lib.rs b/ipld/src/lib.rs new file mode 100644 index 000000000000..7c7cf3e00177 --- /dev/null +++ b/ipld/src/lib.rs @@ -0,0 +1,162 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use encoding::de; +use encoding::tags::current_cbor_tag; +use serde::Deserialize; +use std::collections::BTreeMap; +use std::fmt; + +/// Represents IPLD data structure used when serializing and deserializing data +#[derive(Debug, Clone, PartialEq)] +pub enum Ipld { + Null, + Bool(bool), + Integer(i128), + Float(f64), + String(String), + Bytes(Vec), + List(Vec), + Map(BTreeMap), + Link(Vec), +} + +/// Struct used in deserialization to decode cbor encoded data (including Cid tagged) +/// values to Ipld data type +pub struct IpldVisitor; + +impl<'de> de::Visitor<'de> for IpldVisitor { + type Value = Ipld; + + fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str("any valid CBOR value") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(String::from(value)) + } + + fn visit_string(self, value: String) -> Result + where + E: de::Error, + { + Ok(Ipld::String(value)) + } + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + self.visit_byte_buf(v.to_owned()) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: de::Error, + { + Ok(Ipld::Bytes(v)) + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(Ipld::Integer(v.into())) + } + + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + Ok(Ipld::Integer(v.into())) + } + + fn visit_i128(self, v: i128) -> Result + where + E: de::Error, + { + Ok(Ipld::Integer(v)) + } + + fn visit_bool(self, v: bool) -> Result + where + E: de::Error, + { + Ok(Ipld::Bool(v)) + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + self.visit_unit() + } + + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(Ipld::Null) + } + + fn visit_seq(self, mut visitor: V) -> Result + where + V: de::SeqAccess<'de>, + { + let mut vec = Vec::new(); + + while let Some(elem) = visitor.next_element()? { + vec.push(elem); + } + + Ok(Ipld::List(vec)) + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: de::MapAccess<'de>, + { + let mut values = BTreeMap::new(); + + while let Some((key, value)) = visitor.next_entry()? { + values.insert(key, value); + } + + Ok(Ipld::Map(values)) + } + + fn visit_f64(self, v: f64) -> Result + where + E: de::Error, + { + Ok(Ipld::Float(v)) + } + + fn visit_newtype_struct(self, deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + match current_cbor_tag() { + Some(42) => { + let link = match Ipld::deserialize(deserializer) { + Ok(Ipld::Bytes(link)) => link, + _ => return Err(de::Error::custom("bytes expected")), + }; + Ok(Ipld::Link(link)) + } + Some(tag) => Err(de::Error::custom(format!("unexpected tag ({})", tag))), + _ => Err(de::Error::custom("tag expected")), + } + } +} + +impl<'de> de::Deserialize<'de> for Ipld { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_any(IpldVisitor) + } +} diff --git a/ipld/tests/ipld_test.rs b/ipld/tests/ipld_test.rs new file mode 100644 index 000000000000..16b0b1dbf49e --- /dev/null +++ b/ipld/tests/ipld_test.rs @@ -0,0 +1,37 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use cid::Cid; +use encoding::{from_slice, to_vec}; +use ferret_ipld::Ipld; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize, Clone)] +struct TestStruct { + name: String, + details: Cid, +} + +#[test] +fn encode_new_type() { + let details = Cid::from_bytes_default(&[1, 2, 3]).unwrap(); + let name = "Test".to_string(); + let t_struct = TestStruct { + name: name.clone(), + details: details.clone(), + }; + let struct_encoded = to_vec(&t_struct).unwrap(); + + // Test to make sure struct can be encoded and decoded without IPLD + let struct_decoded: TestStruct = from_slice(&struct_encoded).unwrap(); + assert_eq!(&struct_decoded.name, &name); + assert_eq!(&struct_decoded.details, &details.clone()); + + // Test ipld decoding + let ipld_decoded: Ipld = from_slice(&struct_encoded).unwrap(); + let mut e_map = BTreeMap::::new(); + e_map.insert("details".to_string(), Ipld::Link(details.to_bytes())); + e_map.insert("name".to_string(), Ipld::String(name)); + assert_eq!(&ipld_decoded, &Ipld::Map(e_map)); +} diff --git a/node/Cargo.toml b/node/Cargo.toml index fa981a25c31a..fc9bd9f5be91 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -6,17 +6,15 @@ edition = "2018" [dependencies] network = { path = "network" } -ferret-libp2p = { path = "ferret-libp2p"} +ferret-libp2p = { path = "ferret-libp2p" } utils = { path = "utils" } db = { path = "db" } - libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "776d13ef046358964c7d64cda3295a3a3cb24743" } tokio = "0.1.22" futures = "0.1.29" clap = "2.33.0" -serde = "1.0" log = "0.4.8" slog = "2.5.2" slog-async = "2.3.0" slog-term = "2.4.2" -serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"] } diff --git a/node/ferret-libp2p/Cargo.toml b/node/ferret-libp2p/Cargo.toml index 28b6287a6495..071228d2d5c1 100644 --- a/node/ferret-libp2p/Cargo.toml +++ b/node/ferret-libp2p/Cargo.toml @@ -11,5 +11,4 @@ tokio = "0.1.22" futures = "0.1.29" log = "0.4.8" slog = "2.5.2" -serde = "1.0" -serde_derive = "1.0" +serde = {version = "1.0", features = ["derive"]} diff --git a/node/ferret-libp2p/src/config.rs b/node/ferret-libp2p/src/config.rs index 97862efb749f..f767637cb496 100644 --- a/node/ferret-libp2p/src/config.rs +++ b/node/ferret-libp2p/src/config.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use libp2p::gossipsub::Topic; -use serde_derive::Deserialize; +use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(default)] diff --git a/node/src/cli/config.rs b/node/src/cli/config.rs index 76326eaa1416..b74efcfadc78 100644 --- a/node/src/cli/config.rs +++ b/node/src/cli/config.rs @@ -1,9 +1,8 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 -use serde_derive::Deserialize; - use ferret_libp2p::config::Libp2pConfig; +use serde::Deserialize; #[derive(Debug, Deserialize, Default)] #[serde(default)] diff --git a/node/utils/Cargo.toml b/node/utils/Cargo.toml index 0a8d8877daa7..437c073561c3 100644 --- a/node/utils/Cargo.toml +++ b/node/utils/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" authors = ["ChainSafe Systems "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] dirs = "2.0.2" toml = "0.5.5" diff --git a/node/utils/src/lib.rs b/node/utils/src/lib.rs index cc0d275fb29c..b7d84c58a67c 100644 --- a/node/utils/src/lib.rs +++ b/node/utils/src/lib.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use dirs::home_dir; -use serde; use std::fs::{create_dir_all, File}; use std::io::{prelude::*, Result}; use std::path::Path; @@ -47,8 +46,8 @@ pub fn get_home_dir() -> String { /// Converts a toml file represented as a string to `S` /// /// # Example -///``` -/// use serde_derive::Deserialize; +/// ``` +/// use serde::Deserialize; /// use utils::read_toml; /// /// #[derive(Deserialize)] diff --git a/vm/Cargo.toml b/vm/Cargo.toml index f5b6583f920f..f4678d9de1fd 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" num-bigint = "0.2.3" address = {path = "./address"} encoding = {path = "../encoding"} +serde = {version = "1.0", features = ["derive"]} diff --git a/vm/actor/Cargo.toml b/vm/actor/Cargo.toml index ef4d13472b07..f347519f17d0 100644 --- a/vm/actor/Cargo.toml +++ b/vm/actor/Cargo.toml @@ -5,12 +5,13 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -vm = {path = "../../vm"} -address = {path = "../address"} -runtime = {path = "../runtime"} +vm = { path = "../../vm" } +address = { path = "../address" } +runtime = { path = "../runtime" } num-bigint = "0.2.3" -encoding = {path = "../../encoding"} +encoding = { path = "../../encoding" } num-traits = "0.2" num-derive = "0.2" -clock = {path = "../../node/clock"} -cid = {path = "../../ipld/cid"} +clock = { path = "../../node/clock" } +cid = { package = "ferret_cid", path = "../../ipld/cid" } +serde = { version = "1.0", features = ["derive"] } diff --git a/vm/actor/src/builtin/init.rs b/vm/actor/src/builtin/init.rs index 11401e312013..d674b78fe620 100644 --- a/vm/actor/src/builtin/init.rs +++ b/vm/actor/src/builtin/init.rs @@ -7,7 +7,7 @@ use vm::{ }; use address::Address; -use encoding::Cbor; +use encoding::{from_slice, Cbor}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use runtime::{arg_end, arg_pop, check_args, ActorCode, Runtime}; @@ -87,7 +87,7 @@ impl ActorCode for InitActorCode { } Some(InitMethod::GetActorIDForAddress) => { // Pop and unmarshall address parameter - let addr_res = Address::unmarshal_cbor(&arg_pop(params, rt).bytes()); + let addr_res = from_slice(&arg_pop(params, rt).bytes()); // validate addr deserialization and parameters check_args(params, rt, addr_res.is_ok()); diff --git a/vm/actor/src/lib.rs b/vm/actor/src/lib.rs index f667c4159189..5868cac2c46c 100644 --- a/vm/actor/src/lib.rs +++ b/vm/actor/src/lib.rs @@ -8,28 +8,15 @@ pub use self::builtin::*; pub use self::code::*; use cid::Cid; -use encoding::{Cbor, CodecProtocol, Error as EncodingError}; +use encoding::Cbor; use num_bigint::BigUint; +use serde::{Deserialize, Serialize}; -#[derive(PartialEq, Eq, Copy, Clone, Debug, Default)] +/// Identifier for Actors, includes builtin and initialized actors +#[derive(PartialEq, Eq, Copy, Clone, Debug, Default, Serialize, Deserialize)] pub struct ActorID(u64); -impl Cbor for ActorID { - fn unmarshal_cbor(_bz: &[u8]) -> Result { - // TODO - Err(EncodingError::Unmarshalling { - description: "Not Implemented".to_string(), - protocol: CodecProtocol::Cbor, - }) - } - fn marshal_cbor(&self) -> Result, EncodingError> { - // TODO - Err(EncodingError::Marshalling { - description: format!("Not implemented, data: {:?}", self), - protocol: CodecProtocol::Cbor, - }) - } -} +impl Cbor for ActorID {} /// State of all actor implementations #[derive(PartialEq, Eq, Clone, Debug)] diff --git a/vm/address/Cargo.toml b/vm/address/Cargo.toml index e178fdc93445..c959d2a1630a 100644 --- a/vm/address/Cargo.toml +++ b/vm/address/Cargo.toml @@ -10,5 +10,4 @@ num-derive = "0.2" data-encoding = "2.1.2" data-encoding-macro = "0.1.7" leb128 = "0.2.1" -encoding = {path = "../../encoding"} -serde_cbor = "0.11.0" +encoding = { path = "../../encoding" } diff --git a/vm/address/src/lib.rs b/vm/address/src/lib.rs index 331667ddb04c..5843b09844e5 100644 --- a/vm/address/src/lib.rs +++ b/vm/address/src/lib.rs @@ -10,10 +10,10 @@ pub use self::protocol::Protocol; use data_encoding::Encoding; use data_encoding_macro::{internal_new_encoding, new_encoding}; -use encoding::{blake2b_variable, Cbor, CodecProtocol, Error as EncodingError}; +use encoding::{ + blake2b_variable, de, ser, serde_bytes, Cbor, CodecProtocol, Error as EncodingError, +}; use leb128; -use serde_cbor::Value::Bytes; -use serde_cbor::{from_slice, to_vec}; use std::fmt; use std::hash::Hash; use std::str::FromStr; @@ -184,32 +184,37 @@ impl FromStr for Address { } } -impl Cbor for Address { - fn unmarshal_cbor(bz: &[u8]) -> Result { - // Convert cbor encoded to bytes - let mut vec = match from_slice(bz) { - Ok(Bytes(v)) => v, - _ => { - return Err(EncodingError::Unmarshalling { - description: "Could not decode as bytes".to_owned(), - protocol: CodecProtocol::Cbor, - }); - } - }; +impl ser::Serialize for Address { + fn serialize(&self, s: S) -> Result + where + S: ser::Serializer, + { + let address_bytes = self.to_bytes(); + let value = serde_bytes::Bytes::new(&address_bytes); + serde_bytes::Serialize::serialize(value, s) + } +} + +impl<'de> de::Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let mut bz: Vec = serde_bytes::Deserialize::deserialize(deserializer)?; // Remove protocol byte - let protocol = Protocol::from_byte(vec.remove(0)).ok_or(EncodingError::Unmarshalling { - description: format!("Invalid protocol byte: {}", bz[0]), - protocol: CodecProtocol::Cbor, - })?; + let protocol = Protocol::from_byte(bz.remove(0)) + .ok_or(EncodingError::Unmarshalling { + description: format!("Invalid protocol byte: {}", bz[0]), + protocol: CodecProtocol::Cbor, + }) + .map_err(de::Error::custom)?; // Create and return created address of unmarshalled bytes - Ok(Address::new(NETWORK_DEFAULT, protocol, vec)?) - } - fn marshal_cbor(&self) -> Result, EncodingError> { - // encode bytes - Ok(to_vec(&Bytes(self.to_bytes()))?) + Ok(Address::new(NETWORK_DEFAULT, protocol, bz).map_err(de::Error::custom)?) } } +impl Cbor for Address {} + impl From for EncodingError { fn from(err: Error) -> EncodingError { EncodingError::Marshalling { diff --git a/vm/address/tests/address_test.rs b/vm/address/tests/address_test.rs index 53455471591d..da2270c5c51d 100644 --- a/vm/address/tests/address_test.rs +++ b/vm/address/tests/address_test.rs @@ -4,7 +4,7 @@ use address::{ checksum, validate_checksum, Address, Error, Protocol, BLS_PUB_LEN, PAYLOAD_HASH_LEN, }; -use encoding::Cbor; +use encoding::{from_slice, Cbor}; use std::str::FromStr; #[test] @@ -496,7 +496,7 @@ fn cbor_encoding() { let encoded = res.marshal_cbor().unwrap(); // assert intermediate value is correct assert_eq!(encoded.clone(), t.encoded); - let rec = Address::unmarshal_cbor(encoded.as_ref()).unwrap(); + let rec: Address = from_slice(&encoded).unwrap(); // assert decoded Address is equal to initial one assert_eq!(rec, res); } diff --git a/vm/message/Cargo.toml b/vm/message/Cargo.toml index 73600a785150..b492f6f622fa 100644 --- a/vm/message/Cargo.toml +++ b/vm/message/Cargo.toml @@ -5,10 +5,10 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -vm = {path = "../../vm"} -address = {path = "../address"} -num-bigint = "0.2.3" -encoding = {path = "../../encoding"} -crypto = {path = "../../crypto"} +vm = { path = "../../vm" } +address = { path = "../address" } +num-bigint = { version = "0.2.3", features = ["serde"] } +encoding = { path = "../../encoding" } +crypto = { path = "../../crypto" } derive_builder = "0.9" - +serde = { version = "1.0", features = ["derive"] } diff --git a/vm/message/src/signed_message.rs b/vm/message/src/signed_message.rs index e5bd2ff6cc32..1db8003552c5 100644 --- a/vm/message/src/signed_message.rs +++ b/vm/message/src/signed_message.rs @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Message, UnsignedMessage}; -use vm::{MethodNum, MethodParams, TokenAmount}; - use address::Address; use crypto::{Error as CryptoError, Signature, Signer}; -use encoding::{Cbor, CodecProtocol, Error as EncodingError}; +use encoding::Cbor; use num_bigint::BigUint; +use serde::{Deserialize, Serialize}; +use vm::{MethodNum, MethodParams, TokenAmount}; /// SignedMessage represents a wrapped message with signature bytes -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub struct SignedMessage { message: UnsignedMessage, signature: Signature, @@ -70,19 +70,5 @@ impl Message for SignedMessage { } } -impl Cbor for SignedMessage { - fn unmarshal_cbor(_bz: &[u8]) -> Result { - // TODO - Err(EncodingError::Unmarshalling { - description: "Not Implemented".to_string(), - protocol: CodecProtocol::Cbor, - }) - } - fn marshal_cbor(&self) -> Result, EncodingError> { - // TODO - Err(EncodingError::Marshalling { - description: format!("Not implemented, data: {:?}", self), - protocol: CodecProtocol::Cbor, - }) - } -} +// TODO modify signed message encoding format when needed +impl Cbor for SignedMessage {} diff --git a/vm/message/src/unsigned_message.rs b/vm/message/src/unsigned_message.rs index bd5975392c63..9ea09da1e0fd 100644 --- a/vm/message/src/unsigned_message.rs +++ b/vm/message/src/unsigned_message.rs @@ -7,8 +7,9 @@ use crate::{MethodNum, MethodParams}; use address::Address; use derive_builder::Builder; -use encoding::{Cbor, CodecProtocol, Error as EncodingError}; +use encoding::Cbor; use num_bigint::BigUint; +use serde::{Deserialize, Serialize}; /// Default Unsigned VM message type which includes all data needed for a state transition /// @@ -40,7 +41,7 @@ use num_bigint::BigUint; /// let msg = message_builder.build().unwrap(); /// assert_eq!(msg.sequence(), 1); /// ``` -#[derive(PartialEq, Clone, Debug, Builder)] +#[derive(PartialEq, Clone, Debug, Builder, Serialize, Deserialize)] #[builder(name = "MessageBuilder")] pub struct UnsignedMessage { from: Address, @@ -100,19 +101,5 @@ impl Message for UnsignedMessage { } } -impl Cbor for UnsignedMessage { - fn unmarshal_cbor(_bz: &[u8]) -> Result { - // TODO - Err(EncodingError::Unmarshalling { - description: "Not Implemented".to_string(), - protocol: CodecProtocol::Cbor, - }) - } - fn marshal_cbor(&self) -> Result, EncodingError> { - // TODO - Err(EncodingError::Marshalling { - description: format!("Not implemented, data: {:?}", self), - protocol: CodecProtocol::Cbor, - }) - } -} +// TODO modify unsigned message encoding format when needed +impl Cbor for UnsignedMessage {} diff --git a/vm/message/tests/builder_test.rs b/vm/message/tests/builder_test.rs index 22d956696ccc..34f4eb700fa6 100644 --- a/vm/message/tests/builder_test.rs +++ b/vm/message/tests/builder_test.rs @@ -56,8 +56,6 @@ fn unsigned_message_builder() { } #[test] -// TODO remove should_panic once cbor encoding unsigned message complete -#[should_panic] fn generate_signed_message() { let unsigned_msg = UnsignedMessage::builder() .to(Address::new_id(1).unwrap()) diff --git a/vm/runtime/Cargo.toml b/vm/runtime/Cargo.toml index 571d76e099e3..1c47d98ac6a7 100644 --- a/vm/runtime/Cargo.toml +++ b/vm/runtime/Cargo.toml @@ -5,9 +5,9 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -vm = {path = "../../vm"} -crypto = {path = "../../crypto"} -address = {path = "../address"} -message = {path = "../message"} -cid = {path = "../../ipld/cid"} -clock = {path = "../../node/clock"} +vm = { path = "../../vm" } +crypto = { path = "../../crypto" } +address = { path = "../address" } +message = { path = "../message" } +cid = { package = "ferret_cid", path = "../../ipld/cid" } +clock = { path = "../../node/clock" } diff --git a/vm/src/method.rs b/vm/src/method.rs index b6dc9dfbf6c8..fffcd11e0efe 100644 --- a/vm/src/method.rs +++ b/vm/src/method.rs @@ -1,11 +1,12 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 -use encoding::{Cbor, Error as EncodingError}; +use encoding::{ser, to_vec, Error as EncodingError}; +use serde::{Deserialize, Serialize}; use std::ops::{Deref, DerefMut}; /// Method number indicator for calling actor methods -#[derive(Default, Clone, PartialEq, Debug)] +#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct MethodNum(i32); // TODO: add constraints to this impl MethodNum { @@ -33,7 +34,7 @@ pub const METHOD_CRON: isize = 2; pub const METHOD_PLACEHOLDER: isize = 3; /// Serialized bytes to be used as individual parameters into actor methods -#[derive(Default, Clone, PartialEq, Debug)] +#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct Serialized { bytes: Vec, } @@ -47,21 +48,17 @@ impl Deref for Serialized { impl Serialized { /// Constructor if data is encoded already - /// - /// ### Arguments - /// * `bytes` - vector of bytes to use as serialized data pub fn new(bytes: Vec) -> Self { Self { bytes } } + /// Contructor for encoding Cbor encodable structure - /// - /// ### Arguments - /// * `obj` - Cbor encodable type - pub fn serialize(obj: impl Cbor) -> Result { + pub fn serialize(obj: O) -> Result { Ok(Self { - bytes: obj.marshal_cbor()?, + bytes: to_vec(&obj)?, }) } + /// Returns serialized bytes pub fn bytes(&self) -> Vec { self.bytes.clone() @@ -69,7 +66,7 @@ impl Serialized { } /// Method parameters used in Actor execution -#[derive(Default, Clone, PartialEq, Debug)] +#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct MethodParams { params: Vec, } diff --git a/vm/src/token.rs b/vm/src/token.rs index c690f52bcc4a..1df6ec640761 100644 --- a/vm/src/token.rs +++ b/vm/src/token.rs @@ -1,6 +1,7 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 +use encoding::{de, ser, serde_bytes}; use num_bigint::BigUint; /// Wrapper around a big int variable to handle token specific functionality @@ -13,3 +14,24 @@ impl TokenAmount { TokenAmount(BigUint::from(val)) } } + +impl ser::Serialize for TokenAmount { + fn serialize(&self, s: S) -> Result + where + S: ser::Serializer, + { + let bz = self.0.to_bytes_be(); + let value = serde_bytes::Bytes::new(&bz); + serde_bytes::Serialize::serialize(value, s) + } +} + +impl<'de> de::Deserialize<'de> for TokenAmount { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let bz: &[u8] = serde_bytes::Deserialize::deserialize(deserializer)?; + Ok(TokenAmount(BigUint::from_bytes_be(bz))) + } +} diff --git a/vm/state_tree/Cargo.toml b/vm/state_tree/Cargo.toml index ef7748660c47..b39e65e44b79 100644 --- a/vm/state_tree/Cargo.toml +++ b/vm/state_tree/Cargo.toml @@ -5,10 +5,10 @@ authors = ["ChainSafe Systems "] edition = "2018" [dependencies] -actor = {path = "../actor"} -address = {path = "../address"} -vm = {path = "../../vm"} -cid = {path = "../../ipld/cid"} +actor = { path = "../actor" } +address = { path = "../address" } +vm = { path = "../../vm" } +cid = { package = "ferret_cid", path = "../../ipld/cid" } [dev-dependencies] num-bigint = "0.2.3" diff --git a/vm/tests/params_test.rs b/vm/tests/params_test.rs index 8ba8a628d2a4..af2ff7894ddd 100644 --- a/vm/tests/params_test.rs +++ b/vm/tests/params_test.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use address::Address; -use encoding::Cbor; +use encoding::from_slice; use vm::{MethodNum, MethodParams, Serialized}; #[test] @@ -20,7 +20,7 @@ fn cbor_params() { let addr = Address::new_id(1).unwrap(); params.insert(0, Serialized::serialize(addr.clone()).unwrap()); let encoded_addr = params.remove(0); - assert_eq!(Address::unmarshal_cbor(&encoded_addr).unwrap(), addr); + assert_eq!(from_slice::
(&encoded_addr).unwrap(), addr); } #[test]