Skip to content

Message improvements #48

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

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lazy_static = "1.4.0"
fixed-hash = { version = "0.7.0", default-features = true, features = ["std"] }
bech32 = "0.8.1"
thiserror = "1.0.30"
Expand All @@ -19,3 +20,5 @@ static_assertions = "1.1.0"
generic-array = "0.14.4"
rand = "0.8.4"
anyhow = "1.0.51"
parity-scale-codec-derive = "2.3.1"
parity-scale-codec = {version = "2.3.1", features = ["derive", "chain-error"]}
13 changes: 10 additions & 3 deletions common/src/chain/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use crate::primitives::BlockHeight;
use parity_scale_codec::{Decode, Encode};
use std::collections::BTreeMap;

type HashType = Vec<u8>; // temp type until crypto is ready

#[derive(Debug, Copy, Clone)]
pub const MAGIC_BYTES: [u8; 4] = [0x1a, 0x64, 0xe5, 0xf1];

#[derive(Debug, Copy, Clone, PartialEq, Encode, Decode)]
pub enum ChainType {
Mainnet,
// Testnet,
Expand All @@ -23,7 +26,7 @@ pub struct ChainConfig {
#[allow(dead_code)]
height_checkpoint_data: BTreeMap<BlockHeight, HashType>,
#[allow(dead_code)]
magic_bytes: [u8; 4],
pub magic_bytes: [u8; 4],
}

impl ChainConfig {
Expand All @@ -32,6 +35,10 @@ impl ChainConfig {
}
}

lazy_static::lazy_static! {
pub static ref CHAIN_CONFIG: ChainConfig = create_mainnet();
}

#[allow(dead_code)]
pub fn create_mainnet() -> ChainConfig {
ChainConfig {
Expand All @@ -40,6 +47,6 @@ pub fn create_mainnet() -> ChainConfig {
height_checkpoint_data: BTreeMap::<BlockHeight, HashType>::new(),
rpc_port: 15234,
p2p_port: 8978,
magic_bytes: [0x1a, 0x64, 0xe5, 0xf1],
magic_bytes: MAGIC_BYTES,
}
}
13 changes: 10 additions & 3 deletions p2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ async-trait = "0.1.51"
futures = "0.3.15"
futures-timer = "3.0.2"
rand = "0.8.4"
parity-scale-codec-derive = "2.3.1"
lazy_static = "1.4.0"

[dependencies.common]
path = "../common"

[dependencies.util]
path = "src/util"

[dependencies.tokio]
version = "1"
features = ["full"]

[dependencies.parity-scale-codec]
default-features = false
features = ['derive']
version = '2.0.0'
version = "2.3.1"
features = ["derive", "chain-error"]
14 changes: 10 additions & 4 deletions p2p/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@
// limitations under the License.
//
// Author(s): A. Altonen
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum P2pError {
SocketError(std::io::Error),
SocketError(std::io::ErrorKind),
PeerDisconnected,
DecodeFailure(parity_scale_codec::Error),
DecodeFailure(String),
}

pub type Result<T> = core::result::Result<T, P2pError>;

impl From<std::io::Error> for P2pError {
fn from(e: std::io::Error) -> P2pError {
P2pError::SocketError(e)
P2pError::SocketError(e.kind())
}
}

impl From<parity_scale_codec::Error> for P2pError {
fn from(e: parity_scale_codec::Error) -> P2pError {
P2pError::DecodeFailure(e.to_string())
}
}
74 changes: 60 additions & 14 deletions p2p/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,20 @@
// limitations under the License.
//
// Author(s): A. Altonen
use common::chain::config::CHAIN_CONFIG;
use parity_scale_codec::{Decode, Encode};
use util::Message;

#[allow(unused)]
const MINTLAYER_MAGIC_NUM: u32 = 0x11223344;
#[allow(unused)]
const MINTLAYER_MAINNET_ID: u32 = 0xaabbccdd;
#[allow(unused)]
const MINTLAYER_TESTNET_ID: u32 = 0xeeff1122;

#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, PartialEq, Clone)]
pub enum MessageType {
Hello,
HelloAck,
}

#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, Clone)]
pub struct Message {
/// Magic number identifying Mintlayer P2P messages
magic: u32,
magic: [u8; 4],
/// Type of the message carried in `payload`
msg_type: MessageType,
/// Size of the message
Expand All @@ -41,18 +36,69 @@ pub struct Message {
payload: Vec<u8>,
}

#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, PartialEq, Clone, Message)]
pub struct Hello {
/// Version of the software
version: u32,
network: u32,
/// Services provided by the node
services: u32,
/// Unix timestamp
timestamp: u64,
}

#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, PartialEq, Clone, Message)]
pub struct HelloAck {
/// Version of the software
version: u32,
network: u32,
/// Services provided by the node
services: u32,
/// Unix timestamp
timestamp: u64,
}

#[cfg(test)]
mod tests {
use super::*;
use crate::error::P2pError;
use std::time::SystemTime;

#[test]
fn hello_test() {
let version = 215; // v2.1.5
let services = 0;
let timestamp: u64 =
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();

let hello = Hello::new(version, services, timestamp);
let msg: Message = hello.clone().into();

// Hello message cannot be converted to HelloAck message
// even if the representation of the messages is exactly the same
assert_eq!(
HelloAck::try_from(msg.clone()),
Err(P2pError::DecodeFailure("Invalid message type".to_string()))
);
assert_eq!(Hello::try_from(msg.clone()), Ok(hello));
assert_eq!(msg.msg_type as u8, 0);
}

#[test]
fn hello_ack_test() {
let version = 215; // v2.1.5
let services = 0;
let timestamp: u64 =
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();

let hello_ack = HelloAck::new(version, services, timestamp);
let msg: Message = hello_ack.clone().into();

// HelloAck message cannot be converted to Hello message
// even if the representation of the messages is exactly the same
assert_eq!(
Hello::try_from(msg.clone()),
Err(P2pError::DecodeFailure("Invalid message type".to_string()))
);
assert_eq!(HelloAck::try_from(msg.clone()), Ok(hello_ack));
assert_eq!(msg.msg_type as u8, 1);
}
}
7 changes: 2 additions & 5 deletions p2p/src/net/mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ impl NetworkService for MockService {

async fn connect(&mut self, addr: Self::Address) -> error::Result<Self::Socket> {
if self.addr == addr {
return Err(P2pError::SocketError(Error::new(
ErrorKind::Other,
"Connection to local node prohibited!",
)));
return Err(P2pError::SocketError(ErrorKind::AddrNotAvailable));
}

Ok(MockSocket {
Expand Down Expand Up @@ -112,7 +109,7 @@ impl SocketService for MockSocket {

match self.socket.read(&mut data).await? {
0 => Err(P2pError::PeerDisconnected),
_ => Decode::decode(&mut &data[..]).map_err(|e| P2pError::DecodeFailure(e)),
_ => Decode::decode(&mut &data[..]).map_err(|e| e.into()),
}
}
}
16 changes: 16 additions & 0 deletions p2p/src/util/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "util"
version = "0.1.0"
edition = "2021"
license = "MIT"

[lib]
proc-macro = true

[dependencies]
quote = "1.0"
proc-macro2 = "1.0"

[dependencies.syn]
version = "1.0"
features = ["full"]
89 changes: 89 additions & 0 deletions p2p/src/util/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) 2021 RBB S.r.l
// [email protected]
// SPDX-License-Identifier: MIT
// Licensed under the MIT License;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://spdx.org/licenses/MIT
//
// 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.
//
// Author(s): A. Altonen
use proc_macro::{self, TokenStream};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Message)]
pub fn message(input: TokenStream) -> TokenStream {
let data = parse_macro_input!(input as DeriveInput);
let ident = data.ident;

let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
..
}) = data.data
{
named
} else {
panic!("`#[derive(Message)]` can only be used with structs!");
};

let build_list = fields.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;

quote! {
#name: #ty
}
});

let build_vars = fields.iter().map(|f| {
let name = &f.ident;

quote! {
#name
}
});

quote! {
impl #ident {
pub fn new(#(#build_list,)*) -> Self {
Self {
#(#build_vars,)*
}
}
}

impl std::convert::Into<Message> for #ident {
fn into(self) -> Message {
let encoded = self.encode();
Message {
magic: CHAIN_CONFIG.magic_bytes,
msg_type: MessageType::#ident,
size: encoded.len() as u32,
payload: encoded,
}
}
}

impl std::convert::TryFrom<Message> for #ident {
type Error = crate::error::P2pError;

fn try_from(msg: Message) -> Result<#ident, Self::Error> {
if msg.msg_type != MessageType::#ident {
return Err(
Self::Error::DecodeFailure("Invalid message type".to_string())
);
}

Ok(Decode::decode(&mut &msg.payload[..])?)
}
}
}
.into()
}