Skip to content

Commit

Permalink
Change SDK::send's params to take new type IpldBlock.
Browse files Browse the repository at this point in the history
IpldBlock supports multiple codecs, including IPLD_RAW.

Serializing byte vectors will correctly serialize anything that serializes to "raw bytes".
However, it _won't_ serialize "byte lists", so `Vec<u8>` and `[u8]`
won't work. But that shouldn't be an issue in practice.

Users _should_ serialize to/from `ByteBuf`.
  • Loading branch information
arajasek committed Dec 12, 2022
1 parent 70b320e commit 3fc2662
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 23 deletions.
2 changes: 0 additions & 2 deletions ipld/encoding/src/cbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ use serde::{Deserialize, Serialize};
use super::errors::Error;
use crate::{de, from_slice, ser, to_vec};

pub const DAG_CBOR: u64 = 0x71;

/// Cbor utility functions for serializable objects
pub trait Cbor: ser::Serialize + de::DeserializeOwned {
/// Marshalls cbor encodable object into cbor bytes
Expand Down
7 changes: 6 additions & 1 deletion ipld/encoding/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,20 @@ impl From<Error> for io::Error {
///
/// 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)]
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum CodecProtocol {
Unsupported,
Cbor,
Raw,
}

impl fmt::Display for CodecProtocol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
CodecProtocol::Unsupported => write!(f, "Unsupported"),
CodecProtocol::Cbor => write!(f, "Cbor"),
CodecProtocol::Raw => write!(f, "Raw"),
}
}
}
59 changes: 59 additions & 0 deletions ipld/encoding/src/ipld_block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use serde::de::value;
use {serde, serde_ipld_dagcbor};

use crate::{CodecProtocol, Error, RawBytes, DAG_CBOR, IPLD_RAW};

#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct IpldBlock {
pub codec: u64,
pub data: Vec<u8>,
}

impl IpldBlock {
pub fn deserialize<'de, T>(&'de self) -> Result<T, Error>
where
T: serde::Deserialize<'de>,
{
match self.codec {
IPLD_RAW => T::deserialize(value::BytesDeserializer::<value::Error>::new(
self.data.as_slice(),
))
.map_err(|e| Error {
description: e.to_string(),
protocol: CodecProtocol::Raw,
}),
DAG_CBOR => Ok(serde_ipld_dagcbor::from_slice(self.data.as_slice())?),
_ => Err(Error {
description: "unsupported protocol".to_string(),
protocol: CodecProtocol::Unsupported,
}),
}
}
pub fn serialize<T: serde::Serialize + ?Sized>(codec: u64, value: &T) -> Result<Self, Error> {
let data = match codec {
IPLD_RAW => crate::raw::to_vec(value)?,
DAG_CBOR => crate::to_vec(value)?,
_ => {
return Err(Error {
description: "unsupported protocol".to_string(),
protocol: CodecProtocol::Unsupported,
});
}
};
Ok(IpldBlock { codec, data })
}
pub fn serialize_cbor<T: serde::Serialize + ?Sized>(value: &T) -> Result<Option<Self>, Error> {
Ok(Some(IpldBlock::serialize(DAG_CBOR, value)?))
}
}

impl From<RawBytes> for Option<IpldBlock> {
fn from(other: RawBytes) -> Self {
(!other.is_empty()).then(|| IpldBlock {
codec: DAG_CBOR,
data: other.into(),
})
}
}
5 changes: 5 additions & 0 deletions ipld/encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod bytes;
mod cbor;
mod cbor_store;
mod errors;
pub mod ipld_block;
mod raw;
mod vec;
use std::io;

Expand All @@ -17,6 +19,9 @@ pub use self::cbor_store::CborStore;
pub use self::errors::*;
pub use self::vec::*;

pub const DAG_CBOR: u64 = 0x71;
pub const IPLD_RAW: u64 = 0x55;

// TODO: these really don't work all that well in a shared context like this as anyone importing
// them also need to _explicitly_ import the serde_tuple & serde_repr crates. These are _macros_,
// not normal items.
Expand Down
169 changes: 169 additions & 0 deletions ipld/encoding/src/raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use thiserror::Error;

/// Serialize the given value to a vec. This method rejects all types except "raw bytes".
pub fn to_vec<T: serde::Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, super::Error> {
serde::Serialize::serialize(value, Serializer).map_err(|e| super::Error {
description: e.to_string(),
protocol: crate::CodecProtocol::Raw,
})
}

#[derive(Error, Debug)]
enum Error {
#[error("IPLD kind not supported by the raw codec")]
KindNotSupported,
#[error("i/o error when serializing: {0}")]
Other(String),
}

impl serde::ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error::Other(msg.to_string())
}
}

struct Serializer;

macro_rules! reject {
($($method:ident $t:ty),*) => {
$(
fn $method(self, _: $t) -> Result<Self::Ok, Self::Error> {
Err(Error::KindNotSupported)
}
)*
};
}

impl serde::ser::Serializer for Serializer {
type Ok = Vec<u8>;
type Error = Error;
type SerializeSeq = serde::ser::Impossible<Vec<u8>, Error>;
type SerializeTuple = serde::ser::Impossible<Vec<u8>, Error>;
type SerializeTupleStruct = serde::ser::Impossible<Vec<u8>, Error>;
type SerializeTupleVariant = serde::ser::Impossible<Vec<u8>, Error>;
type SerializeMap = serde::ser::Impossible<Vec<u8>, Error>;
type SerializeStruct = serde::ser::Impossible<Vec<u8>, Error>;
type SerializeStructVariant = serde::ser::Impossible<Vec<u8>, Error>;

fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
Ok(v.to_owned())
}

reject! {
serialize_bool bool,
serialize_i8 i8,
serialize_i16 i16,
serialize_i32 i32,
serialize_i64 i64,
serialize_u8 u8,
serialize_u16 u16,
serialize_u32 u32,
serialize_u64 u64,
serialize_f32 f32,
serialize_f64 f64,
serialize_char char,
serialize_str &str,
serialize_unit_struct &'static str
}

fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_some<T: ?Sized>(self, _: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::Serialize,
{
Err(Error::KindNotSupported)
}

fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_unit_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
) -> Result<Self::Ok, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_newtype_struct<T: ?Sized>(
self,
_: &'static str,
_: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::Serialize,
{
Err(Error::KindNotSupported)
}

fn serialize_newtype_variant<T: ?Sized>(
self,
_: &'static str,
_: u32,
_: &'static str,
_: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::Serialize,
{
Err(Error::KindNotSupported)
}

fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_tuple_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(Error::KindNotSupported)
}

fn serialize_struct_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::KindNotSupported)
}
}
15 changes: 9 additions & 6 deletions sdk/src/message.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::convert::TryInto;

use fvm_ipld_encoding::DAG_CBOR;
use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_shared::econ::TokenAmount;
use fvm_shared::sys::{BlockId, Codec};
use fvm_shared::sys::BlockId;
use fvm_shared::{ActorID, MethodNum};

use crate::vm::INVOCATION_CONTEXT;
Expand Down Expand Up @@ -35,13 +35,16 @@ pub fn value_received() -> TokenAmount {
.expect("invalid bigint")
}

/// Returns the message codec and parameters.
pub fn params_raw(id: BlockId) -> SyscallResult<(Codec, Vec<u8>)> {
/// Returns the message parameters as an Option<IpldBlock>.
pub fn params_raw(id: BlockId) -> SyscallResult<Option<IpldBlock>> {
if id == NO_DATA_BLOCK_ID {
return Ok((DAG_CBOR, Vec::default())); // DAG_CBOR is a lie, but we have no nil codec.
return Ok(None);
}
unsafe {
let fvm_shared::sys::out::ipld::IpldStat { codec, size } = sys::ipld::block_stat(id)?;
Ok((codec, crate::ipld::get_block(id, Some(size))?))
Ok(Some(IpldBlock {
codec,
data: crate::ipld::get_block(id, Some(size))?,
}))
}
}
12 changes: 6 additions & 6 deletions sdk/src/send.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::convert::TryInto;

use fvm_ipld_encoding::{RawBytes, DAG_CBOR};
use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::{ErrorNumber, ExitCode};
Expand All @@ -15,7 +16,7 @@ use crate::{sys, SyscallResult, NO_DATA_BLOCK_ID};
pub fn send(
to: &Address,
method: MethodNum,
params: RawBytes,
params: Option<IpldBlock>,
value: TokenAmount,
) -> SyscallResult<Receipt> {
let recipient = to.to_bytes();
Expand All @@ -25,10 +26,9 @@ pub fn send(
unsafe {
// Insert parameters as a block. Nil parameters is represented as the
// NO_DATA_BLOCK_ID block ID in the FFI interface.
let params_id = if params.len() > 0 {
sys::ipld::block_create(DAG_CBOR, params.as_ptr(), params.len() as u32)?
} else {
NO_DATA_BLOCK_ID
let params_id = match params {
Some(p) => sys::ipld::block_create(p.codec, p.data.as_ptr(), p.data.len() as u32)?,
None => NO_DATA_BLOCK_ID,
};

// Perform the syscall to send the message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ pub fn invoke(params_pointer: u32) -> u32 {
let ret: Option<RawBytes> = match fvm_sdk::message::method_number() {
// Set initial value
1 => {
let params = params_raw(params_pointer).unwrap().1;
let x: i64 = RawBytes::new(params).deserialize().unwrap();
let params = params_raw(params_pointer).unwrap().unwrap();
let x: i64 = params.deserialize().unwrap();

let mut state = State::load();
state.value = x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,7 @@ pub fn call_extern() {

#[inline(never)]
pub fn do_send(m: u64) -> u32 {
let r = sdk::send::send(
&Address::new_id(10000),
m + 1,
Vec::new().into(),
TokenAmount::zero(),
);
let r = sdk::send::send(&Address::new_id(10000), m + 1, None, TokenAmount::zero());
match r {
Ok(rec) => match rec.exit_code {
ExitCode::OK => 0,
Expand Down

0 comments on commit 3fc2662

Please sign in to comment.