diff --git a/Cargo.toml b/Cargo.toml index 360a1dcd769a..49c554d35c7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,6 @@ members = [ "vm", "vm/address", "node", - "crypto" + "crypto", + "encoding" ] diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index c3785d65c4e1..95e3f4416e46 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] blake2 = "0.8.1" address = {path = "../vm/address"} +encoding = {path = "../encoding"} libsecp256k1 = "0.2.1" bls-signatures = "0.2.0" diff --git a/crypto/src/errors.rs b/crypto/src/errors.rs index 0d7ac9e980b1..c0910cfb9d36 100644 --- a/crypto/src/errors.rs +++ b/crypto/src/errors.rs @@ -1,4 +1,5 @@ use address::Error as AddressError; +use encoding::Error as EncodingError; use secp256k1::Error as SecpError; use std::error; use std::fmt; @@ -47,10 +48,9 @@ impl From for Error { } } -// TODO: Remove once cbor marshalling and unmarshalling implemented -impl From for Error { - fn from(err: String) -> Error { +impl From for Error { + fn from(err: EncodingError) -> Error { // Pass error encountered in signer trait as module error type - Error::SigningError(err) + Error::SigningError(err.to_string()) } } diff --git a/crypto/src/signature.rs b/crypto/src/signature.rs index 098449c0529e..775ee6d8f14d 100644 --- a/crypto/src/signature.rs +++ b/crypto/src/signature.rs @@ -1,10 +1,9 @@ use super::errors::Error; use address::{Address, Protocol}; -use blake2::digest::{Input, VariableOutput}; -use blake2::VarBlake2b; use bls_signatures::{ hash as bls_hash, verify, PublicKey as BlsPubKey, Serialize, Signature as BlsSignature, }; +use encoding::blake2b_256; use secp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; @@ -48,8 +47,7 @@ fn verify_bls_sig(data: Vec, pub_k: Vec, sig: Signature) -> bool { /// returns true if a secp256k1 signature is valid fn verify_secp256k1_sig(data: Vec, addr: Address, sig: Signature) -> bool { // blake2b 256 hash - let mut hash = [0u8; 32]; - blake2b_256(data, &mut hash); + let hash = blake2b_256(data); // Ecrecover with hash and signature let mut signature = [0u8; 65]; @@ -86,17 +84,6 @@ fn ecrecover(hash: &[u8; 32], signature: &[u8; 65]) -> Result { Ok(addr) } -/// generates blake2b hash of 32 bytes -fn blake2b_256(ingest: Vec, hash: &mut [u8; 32]) { - let mut hasher = VarBlake2b::new(32).unwrap(); - hasher.input(ingest); - - hasher.variable_result(|res| { - // Copy result slice to vector return - hash[..32].clone_from_slice(res); - }); -} - #[cfg(test)] mod tests { use super::*; diff --git a/encoding/Cargo.toml b/encoding/Cargo.toml new file mode 100644 index 000000000000..8fd50e72ee7e --- /dev/null +++ b/encoding/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "encoding" +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.10.2" diff --git a/encoding/src/cbor.rs b/encoding/src/cbor.rs new file mode 100644 index 000000000000..a365333ded31 --- /dev/null +++ b/encoding/src/cbor.rs @@ -0,0 +1,8 @@ +use super::errors::Error; + +pub trait Cbor { + fn unmarshal_cbor(bz: &[u8]) -> Result + where + Self: Sized; + fn marshal_cbor(&self) -> Result, Error>; +} diff --git a/encoding/src/errors.rs b/encoding/src/errors.rs new file mode 100644 index 000000000000..fbe8fc154367 --- /dev/null +++ b/encoding/src/errors.rs @@ -0,0 +1,80 @@ +use std::fmt; + +/// Error type for encoding and decoding data through any Ferret supported protocol +/// +/// This error will provide any details about the data which was attempted to be +/// encoded or decoded. The +/// +/// Usage: +/// ```no_run +/// use encoding::{Error, CodecProtocol}; +/// +/// Error::Marshalling { +/// description: format!("{:?}", vec![0]), +/// protocol: CodecProtocol::Cbor, +/// }; +/// Error::Unmarshalling { +/// description: format!("{:?}", vec![0]), +/// protocol: CodecProtocol::Cbor, +/// }; +/// ``` +#[derive(Debug, PartialEq)] +pub enum Error { + Unmarshalling { + description: String, + protocol: CodecProtocol, + }, + Marshalling { + description: String, + protocol: CodecProtocol, + }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Unmarshalling { + description, + protocol, + } => write!( + f, + "Could not decode in format {}: {}", + protocol, description + ), + Error::Marshalling { + description, + protocol, + } => write!( + f, + "Could not encode in format {}: {}", + protocol, description + ), + } + } +} + +impl From for Error { + fn from(err: serde_cbor::error::Error) -> Error { + Error::Marshalling { + description: err.to_string(), + protocol: CodecProtocol::Cbor, + } + } +} + +/// CodecProtocol defines the protocol in which the data is encoded or decoded +/// +/// This is used with the encoding errors, to detail the encoding protocol or any other +/// information about how the data was encoded or decoded +#[derive(Debug, PartialEq)] +pub enum CodecProtocol { + Cbor, +} + +impl fmt::Display for CodecProtocol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CodecProtocol::Cbor => write!(f, "Cbor"), + } + } +} diff --git a/encoding/src/hash.rs b/encoding/src/hash.rs new file mode 100644 index 000000000000..92c8cf3affe3 --- /dev/null +++ b/encoding/src/hash.rs @@ -0,0 +1,59 @@ +use blake2b_simd::Params; + +/// generates blake2b hash with provided size +/// +/// # Example +/// ``` +/// use encoding::blake2b_variable; +/// +/// let ingest: Vec = vec![]; +/// let hash = blake2b_variable(ingest, 20); +/// assert_eq!(hash.len(), 20); +/// ``` +pub fn blake2b_variable(ingest: Vec, size: usize) -> Vec { + let hash = Params::new() + .hash_length(size) + .to_state() + .update(&ingest) + .finalize(); + + hash.as_bytes().to_vec() +} + +/// generates blake2b hash of fixed 32 bytes size +/// +/// # Example +/// ``` +/// use encoding::blake2b_256; +/// +/// let ingest: Vec = vec![]; +/// let hash = blake2b_256(ingest); +/// assert_eq!(hash.len(), 32); +/// ``` +pub fn blake2b_256(ingest: Vec) -> [u8; 32] { + let digest = Params::new() + .hash_length(32) + .to_state() + .update(&ingest) + .finalize(); + + let mut ret = [0u8; 32]; + ret.clone_from_slice(digest.as_bytes()); + ret +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hash_length() { + let ingest = vec![1, 4, 2, 3]; + let hash = blake2b_variable(ingest.clone(), 8); + assert_eq!(hash.len(), 8); + let hash = blake2b_variable(ingest.clone(), 20); + assert_eq!(hash.len(), 20); + let hash = blake2b_variable(ingest.clone(), 32); + assert_eq!(hash.len(), 32); + } +} diff --git a/encoding/src/lib.rs b/encoding/src/lib.rs new file mode 100644 index 000000000000..cc359b69cccd --- /dev/null +++ b/encoding/src/lib.rs @@ -0,0 +1,7 @@ +mod cbor; +mod errors; +mod hash; + +pub use cbor::*; +pub use errors::*; +pub use hash::*; diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 15e442a7d9ea..bc93283a0827 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -11,4 +11,6 @@ cid = "0.3.1" num-bigint = "0.2.3" crypto = {path = "../crypto"} address = {path = "./address"} +encoding = {path = "../encoding"} + diff --git a/vm/address/Cargo.toml b/vm/address/Cargo.toml index b30a69c4c5f4..da1fb5008201 100644 --- a/vm/address/Cargo.toml +++ b/vm/address/Cargo.toml @@ -11,5 +11,6 @@ num-traits = "0.2" num-derive = "0.2" data-encoding = "2.1.2" data-encoding-macro = "0.1.7" -blake2 = "0.8.1" leb128 = "0.2.1" +encoding = {path = "../../encoding"} +serde_cbor = "0.10.2" diff --git a/vm/address/src/lib.rs b/vm/address/src/lib.rs index 8da90fb6d332..41055e0b78ba 100644 --- a/vm/address/src/lib.rs +++ b/vm/address/src/lib.rs @@ -5,10 +5,9 @@ pub use self::errors::Error; pub use self::network::Network; pub use self::protocol::Protocol; -use blake2::digest::{Input, VariableOutput}; -use blake2::VarBlake2b; use data_encoding::Encoding; use data_encoding_macro::{internal_new_encoding, new_encoding}; +use encoding::{blake2b_variable, Cbor, CodecProtocol, Error as EncodingError}; use leb128; /// defines the encoder for base32 encoding with the provided string with no padding @@ -26,7 +25,7 @@ const TESTNET_PREFIX: &str = "t"; /// Address is the struct that defines the protocol and data payload conversion from either /// a public key or value -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct Address { protocol: Protocol, payload: Vec, @@ -168,25 +167,22 @@ impl Address { None => encode(self, Network::Testnet), } } +} - // Marshalling and unmarshalling - pub fn unmarshall_cbor(&mut self, _bz: &mut [u8]) -> Result<(), String> { - // TODO - Err("Unmarshall is unimplemented".to_owned()) - } - pub fn marshall_cbor(&self) -> Result, String> { - // TODO - Err("Marshall is unimplemented".to_owned()) - } - - // JSON Marshalling and unmarshalling - pub fn unmarshall_json(&mut self, _bz: &mut [u8]) -> Result<(), String> { +impl Cbor for Address { + fn unmarshal_cbor(_bz: &[u8]) -> Result { // TODO - Err("JSON unmarshall is unimplemented".to_owned()) + Err(EncodingError::Unmarshalling { + description: "Not Implemented".to_string(), + protocol: CodecProtocol::Cbor, + }) } - pub fn marshall_json(&self) -> Result, String> { + fn marshal_cbor(&self) -> Result, EncodingError> { // TODO - Err("JSON marshall is unimplemented".to_owned()) + Err(EncodingError::Marshalling { + description: format!("Not implemented, data: {:?}", self), + protocol: CodecProtocol::Cbor, + }) } } @@ -224,7 +220,7 @@ fn encode(addr: &Address, network: Network) -> String { /// Checksum calculates the 4 byte checksum hash pub fn checksum(ingest: Vec) -> Vec { - hash(ingest, CHECKSUM_HASH_LEN) + blake2b_variable(ingest, CHECKSUM_HASH_LEN) } /// Validates the checksum against the ingest data @@ -235,21 +231,5 @@ pub fn validate_checksum(ingest: Vec, expect: Vec) -> bool { /// Returns an address hash for given data fn address_hash(ingest: Vec) -> Vec { - hash(ingest, PAYLOAD_HASH_LEN) -} - -/// generates blake2b hash with provided size -fn hash(ingest: Vec, size: usize) -> Vec { - let mut hasher = VarBlake2b::new(size).unwrap(); - hasher.input(ingest); - - // allocate hash result vector - let mut result: Vec = vec![0; size]; - - hasher.variable_result(|res| { - // Copy result slice to vector return - result[..size].clone_from_slice(res); - }); - - result + blake2b_variable(ingest, PAYLOAD_HASH_LEN) } diff --git a/vm/address/src/protocol.rs b/vm/address/src/protocol.rs index fb323b74bf9c..160ea74e9336 100644 --- a/vm/address/src/protocol.rs +++ b/vm/address/src/protocol.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; use std::fmt; /// Protocol defines the addressing protocol used to derive data to an address -#[derive(PartialEq, Copy, Clone, FromPrimitive)] +#[derive(PartialEq, Copy, Clone, FromPrimitive, Debug)] pub enum Protocol { // ID protocol addressing ID = 0, diff --git a/vm/address/tests/address_test.rs b/vm/address/tests/address_test.rs index 702b57c20cd1..5d64377a8213 100644 --- a/vm/address/tests/address_test.rs +++ b/vm/address/tests/address_test.rs @@ -47,7 +47,7 @@ fn test_address(addr: Address, protocol: Protocol, expected: &'static str) { let from_bytes = Address::from_bytes(decoded.to_bytes()).unwrap(); assert!(decoded == from_bytes); - // TODO: test JSON/cbor encoding and decoding + // TODO: test cbor encoding and decoding } #[test] diff --git a/vm/src/message/mod.rs b/vm/src/message/mod.rs index c2fc62e3a2f0..d7cae9699d3c 100644 --- a/vm/src/message/mod.rs +++ b/vm/src/message/mod.rs @@ -5,10 +5,11 @@ pub use message_receipt::*; pub use signed_message::*; use address::Address; +use encoding::{Cbor, CodecProtocol, Error as EncodingError}; use num_bigint::BigUint; /// VM message type which includes all data needed for a state transition -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct Message { from: Address, to: Address, @@ -33,13 +34,21 @@ impl Message { pub fn to(&self) -> Address { self.to.clone() } - // Marshalling and unmarshalling - pub fn unmarshall_cbor(&mut self, _bz: &mut [u8]) -> Result<(), String> { +} + +impl Cbor for Message { + fn unmarshal_cbor(_bz: &[u8]) -> Result { // TODO - Err("Unmarshall cbor not implemented".to_owned()) + Err(EncodingError::Unmarshalling { + description: "Not Implemented".to_string(), + protocol: CodecProtocol::Cbor, + }) } - pub fn marshall_cbor(&self) -> Result, String> { + fn marshal_cbor(&self) -> Result, EncodingError> { // TODO - Err("Marshall cbor not implemented".to_owned()) + Err(EncodingError::Marshalling { + description: format!("Not implemented, data: {:?}", self), + protocol: CodecProtocol::Cbor, + }) } } diff --git a/vm/src/message/signed_message.rs b/vm/src/message/signed_message.rs index 7daf36349bee..d06771d3eaaa 100644 --- a/vm/src/message/signed_message.rs +++ b/vm/src/message/signed_message.rs @@ -1,8 +1,9 @@ use super::Message; use crypto::{Error as CryptoError, Signature, Signer}; +use encoding::{Cbor, CodecProtocol, Error as EncodingError}; /// SignedMessage represents a wrapped message with signature bytes -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct SignedMessage { pub(crate) message: Message, pub(crate) signature: Signature, @@ -10,7 +11,7 @@ pub struct SignedMessage { impl SignedMessage { pub fn new(msg: &Message, s: impl Signer) -> Result { - let bz = msg.marshall_cbor()?; + let bz = msg.marshal_cbor()?; let sig = s.sign_bytes(bz, msg.from())?; @@ -19,14 +20,21 @@ impl SignedMessage { signature: sig, }) } +} - // Marshalling and unmarshalling - pub fn unmarshall_cbor(&mut self, _bz: &mut [u8]) -> Result<(), String> { +impl Cbor for SignedMessage { + fn unmarshal_cbor(_bz: &[u8]) -> Result { // TODO - Err("Unmarshall cbor not implemented".to_owned()) + Err(EncodingError::Unmarshalling { + description: "Not Implemented".to_string(), + protocol: CodecProtocol::Cbor, + }) } - pub fn marshall_cbor(&self) -> Result, String> { + fn marshal_cbor(&self) -> Result, EncodingError> { // TODO - Err("Marshall cbor not implemented".to_owned()) + Err(EncodingError::Marshalling { + description: format!("Not implemented, data: {:?}", self), + protocol: CodecProtocol::Cbor, + }) } }