Skip to content
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

UnsignedMessage cbor encoding #174

Merged
merged 9 commits into from
Jan 21, 2020
2 changes: 1 addition & 1 deletion blockchain/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ blocks = { path = "../blocks" }
network = { path = "../../node/network" }
cid = { package = "ferret_cid", path = "../../ipld/cid" }
clock = { path = "../../node/clock" }
num-bigint = "0.2.3"
num-bigint = { git = "https://github.com/austinabell/num-bigint", rev = "e63039f89fa5ba5f3c02f7be9de00652d52e5056" }

[dev-dependencies]
address = { path = "../../vm/address" }
Expand Down
1,302 changes: 1,302 additions & 0 deletions tests/cbor/unsigned_message_vectors.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["ChainSafe Systems <info@chainsafe.io>"]
edition = "2018"

[dependencies]
num-bigint = "0.2.3"
address = {path = "./address"}
encoding = {path = "../encoding"}
serde = {version = "1.0", features = ["derive"]}
num-bigint = { git = "https://github.com/austinabell/num-bigint", rev = "e63039f89fa5ba5f3c02f7be9de00652d52e5056" }
address = { path = "./address" }
encoding = { path = "../encoding" }
serde = { version = "1.0", features = ["derive"] }
2 changes: 1 addition & 1 deletion vm/actor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2018"
vm = { path = "../../vm" }
address = { path = "../address" }
runtime = { path = "../runtime" }
num-bigint = "0.2.3"
num-bigint = { git = "https://github.com/austinabell/num-bigint", rev = "e63039f89fa5ba5f3c02f7be9de00652d52e5056" }
encoding = { path = "../../encoding" }
num-traits = "0.2"
num-derive = "0.2"
Expand Down
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub enum AccountMethod {
impl AccountMethod {
/// from_method_num converts a method number into an AccountMethod enum
fn from_method_num(m: MethodNum) -> Option<AccountMethod> {
FromPrimitive::from_i32(m.into())
FromPrimitive::from_u64(u64::from(m))
}
}

Expand Down
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/cron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub enum CronMethod {
impl CronMethod {
/// from_method_num converts a method number into an CronMethod enum
fn from_method_num(m: MethodNum) -> Option<CronMethod> {
FromPrimitive::from_i32(m.into())
FromPrimitive::from_u64(u64::from(m))
}
}

Expand Down
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub enum InitMethod {
impl InitMethod {
/// from_method_num converts a method number into an InitMethod enum
fn from_method_num(m: MethodNum) -> Option<InitMethod> {
FromPrimitive::from_i32(m.into())
FromPrimitive::from_u64(u64::from(m))
}
}

Expand Down
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/reward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub enum RewardMethod {
impl RewardMethod {
/// from_method_num converts a method number into an RewardMethod enum
fn from_method_num(m: MethodNum) -> Option<RewardMethod> {
FromPrimitive::from_i32(m.into())
FromPrimitive::from_u64(u64::from(m))
}
}

Expand Down
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/storage_power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub enum StoragePowerMethod {
impl StoragePowerMethod {
/// from_method_num converts a method number into an StoragePowerMethod enum
fn from_method_num(m: MethodNum) -> Option<StoragePowerMethod> {
FromPrimitive::from_i32(m.into())
FromPrimitive::from_u64(u64::from(m))
}
}

Expand Down
7 changes: 6 additions & 1 deletion vm/message/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ edition = "2018"
[dependencies]
vm = { path = "../../vm" }
address = { path = "../address" }
num-bigint = { version = "0.2.3", features = ["serde"] }
num-bigint = { git = "https://github.com/austinabell/num-bigint", rev = "e63039f89fa5ba5f3c02f7be9de00652d52e5056", features = ["serde_derive"] }
encoding = { path = "../../encoding" }
crypto = { path = "../../crypto" }
derive_builder = "0.9"
serde = { version = "1.0", features = ["derive"] }

[dev-dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
hex = "0.4.0"
62 changes: 54 additions & 8 deletions vm/message/src/unsigned_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
// SPDX-License-Identifier: Apache-2.0

use super::Message;
use crate::TokenAmount;
use crate::{MethodNum, MethodParams};

use address::Address;
use derive_builder::Builder;
use encoding::Cbor;
use encoding::{de, ser, Cbor};
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use vm::{MethodNum, MethodParams, TokenAmount};

/// Default Unsigned VM message type which includes all data needed for a state transition
///
Expand Down Expand Up @@ -41,7 +39,7 @@ use serde::{Deserialize, Serialize};
/// let msg = message_builder.build().unwrap();
/// assert_eq!(msg.sequence(), 1);
/// ```
#[derive(PartialEq, Clone, Debug, Builder, Serialize, Deserialize)]
#[derive(PartialEq, Clone, Debug, Builder)]
#[builder(name = "MessageBuilder")]
pub struct UnsignedMessage {
from: Address,
Expand All @@ -60,15 +58,63 @@ pub struct UnsignedMessage {
gas_limit: BigUint,
}

// TODO verify format or implement custom serialize/deserialize function (if necessary):
// https://github.com/ChainSafe/ferret/issues/143

impl UnsignedMessage {
pub fn builder() -> MessageBuilder {
MessageBuilder::default()
}
}

/// Structure defines how the fields are cbor encoded as an unsigned message
#[derive(Serialize, Deserialize)]
struct CborUnsignedMessage(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be public, ya?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that was refactored 4 hours ago lol how did you even review that? And no, didn't need to be since it was just used in that file for consistent ser/de

Address, // To
Address, // from
u64, // Sequence
TokenAmount, // Value
BigUint, // GasPrice
BigUint, // GasLimit
MethodNum, // Method
MethodParams, // Params
);

impl ser::Serialize for UnsignedMessage {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let value: CborUnsignedMessage = CborUnsignedMessage(
self.to.clone(),
self.from.clone(),
self.sequence,
self.value.clone(),
self.gas_price.clone(),
self.gas_limit.clone(),
self.method_num,
self.params.clone(),
);
CborUnsignedMessage::serialize(&value, s)
}
}

impl<'de> de::Deserialize<'de> for UnsignedMessage {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let cm = CborUnsignedMessage::deserialize(deserializer)?;
Ok(Self {
to: cm.0,
from: cm.1,
sequence: cm.2,
value: cm.3,
gas_price: cm.4,
gas_limit: cm.5,
method_num: cm.6,
params: cm.7,
})
}
}

impl Message for UnsignedMessage {
/// from returns the from address of the message
fn from(&self) -> &Address {
Expand Down
73 changes: 73 additions & 0 deletions vm/message/tests/u_message_cbor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0

use address::Address;
// use encoding::{from_slice, to_vec};
use encoding::to_vec;
use hex::decode;
use message::UnsignedMessage;
use num_bigint::BigUint;
use serde::Deserialize;
use std::fs::File;
use std::io::prelude::*;
use std::str::FromStr;
use vm::{MethodNum, TokenAmount};

#[derive(Debug, Deserialize)]
struct MessageVector {
to: String,
from: String,
nonce: u64,
value: u64,
gas_price: u128,
gas_limit: u128,
method: u64,
params: String,
}

impl From<MessageVector> for UnsignedMessage {
fn from(vector: MessageVector) -> UnsignedMessage {
UnsignedMessage::builder()
.to(Address::from_str(&vector.to).unwrap())
.from(Address::from_str(&vector.from).unwrap())
.sequence(vector.nonce)
.value(TokenAmount::new(vector.value))
.method_num(MethodNum::new(vector.method))
// .params(
// from_slice(&decode(vector.params).unwrap()).expect("bytes to MethodParams failed"),
// )
.gas_limit(BigUint::from(vector.gas_limit))
.gas_price(BigUint::from(vector.gas_price))
.build()
.unwrap()
}
}

#[derive(Deserialize)]
struct TestVector {
message: MessageVector,
hex_cbor: String,
}

fn encode_assert_cbor(message: &UnsignedMessage, expected: &[u8]) {
let enc_bz: Vec<u8> = to_vec(message).expect("cbor serialization failed");

// TODO remove only checking first bytes (after method param encoding format)
assert_eq!(&enc_bz[..81], &expected[..81]);
}

#[test]
fn unsigned_message_cbor_vectors() {
let mut file = File::open("../../tests/cbor/unsigned_message_vectors.json").unwrap();
let mut string = String::new();
file.read_to_string(&mut string).unwrap();

let vectors: Vec<TestVector> =
serde_json::from_str(&string).expect("test vector deserialization failed");
for tv in vectors {
encode_assert_cbor(
&UnsignedMessage::from(tv.message),
&decode(tv.hex_cbor).expect("decoding cbor bytes failed"),
)
}
}
44 changes: 35 additions & 9 deletions vm/src/method.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0

use encoding::{ser, to_vec, Error as EncodingError};
use encoding::{de, ser, serde_bytes, 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, Serialize, Deserialize)]
pub struct MethodNum(i32); // TODO: add constraints to this
#[derive(Default, Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub struct MethodNum(u64); // TODO: add constraints to this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be an enum? Then you can impl the From traits to convert from a number type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah, needs to be an integer because methods vary based on actor and needs to be future proofed for when you can initialize own actor with custom code


// TODO verify format or implement custom serialize/deserialize function (if necessary):
// https://github.com/ChainSafe/ferret/issues/143

impl MethodNum {
/// Constructor for new MethodNum
pub fn new(num: i32) -> Self {
pub fn new(num: u64) -> Self {
Self(num)
}
}

impl From<MethodNum> for i32 {
fn from(method_num: MethodNum) -> i32 {
impl From<MethodNum> for u64 {
fn from(method_num: MethodNum) -> u64 {
method_num.0
}
}
Expand Down Expand Up @@ -72,13 +72,39 @@ impl Serialized {
}

/// Method parameters used in Actor execution
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
#[derive(Default, Clone, PartialEq, Debug)]
pub struct MethodParams {
params: Vec<Serialized>,
}

// TODO verify format or implement custom serialize/deserialize function (if necessary):
// https://github.com/ChainSafe/ferret/issues/143
impl MethodParams {
/// Constructor for new MethodParams
pub fn new(params: Vec<Serialized>) -> Self {
Self { params }
}
}

impl ser::Serialize for MethodParams {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let bz = [0]; // TODO
let value = serde_bytes::Bytes::new(&bz);
serde_bytes::Serialize::serialize(value, s)
}
}

impl<'de> de::Deserialize<'de> for MethodParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let bz: Vec<u8> = serde_bytes::Deserialize::deserialize(deserializer)?;
// TODO
Ok(MethodParams::new(vec![Serialized::new(bz)]))
}
}

impl Deref for MethodParams {
type Target = Vec<Serialized>;
Expand Down
5 changes: 3 additions & 2 deletions vm/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ impl ser::Serialize for TokenAmount {
where
S: ser::Serializer,
{
let bz = self.0.to_bytes_be();
let mut bz = self.0.to_bytes_be();
bz.insert(0, 0); // Sign byte (filecoin spec)
let value = serde_bytes::Bytes::new(&bz);
serde_bytes::Serialize::serialize(value, s)
}
Expand All @@ -32,6 +33,6 @@ impl<'de> de::Deserialize<'de> for TokenAmount {
D: de::Deserializer<'de>,
{
let bz: &[u8] = serde_bytes::Deserialize::deserialize(deserializer)?;
Ok(TokenAmount(BigUint::from_bytes_be(bz)))
Ok(TokenAmount(BigUint::from_bytes_be(&bz[1..])))
}
}
2 changes: 1 addition & 1 deletion vm/state_tree/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ vm = { path = "../../vm" }
cid = { package = "ferret_cid", path = "../../ipld/cid" }

[dev-dependencies]
num-bigint = "0.2.3"
num-bigint = { git = "https://github.com/austinabell/num-bigint", rev = "e63039f89fa5ba5f3c02f7be9de00652d52e5056" }
2 changes: 1 addition & 1 deletion vm/tests/params_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ fn cbor_params() {
fn method_num() {
// Test constructor available publicly
let method = MethodNum::new(1);
assert_eq!(1, method.into());
assert_eq!(1 as u64, u64::from(method));
}