diff --git a/Cargo.lock b/Cargo.lock index 7c0cdfa91f..95d8100d62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2607,6 +2607,7 @@ version = "0.1.0" dependencies = [ "dfx_info 0.0.0", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ic-http-agent 0.1.0", "lalrpop 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-util 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "leb128 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/dfx_info/src/lib.rs b/src/dfx_info/src/lib.rs index 990c499754..e594b751f6 100644 --- a/src/dfx_info/src/lib.rs +++ b/src/dfx_info/src/lib.rs @@ -48,6 +48,7 @@ pub trait Serializer: Sized { fn serialize_struct(self) -> Result; fn serialize_vec(self, len: usize) -> Result; fn serialize_variant(self, index: u64) -> Result; + fn serialize_blob(self, v: &[u8]) -> Result<(), Self::Error>; } pub trait Compound { diff --git a/src/dfx_info/src/types.rs b/src/dfx_info/src/types.rs index 673429c7f3..6346c085fe 100644 --- a/src/dfx_info/src/types.rs +++ b/src/dfx_info/src/types.rs @@ -29,6 +29,7 @@ pub enum Type { Vec(Box), Record(Vec), Variant(Vec), + Service, } #[derive(Debug, PartialEq, Hash, Eq, Clone)] @@ -38,10 +39,12 @@ pub struct Field { pub ty: Type, } +// This function decides only non-primitive types go into the type table pub fn is_primitive(t: &Type) -> bool { use Type::*; match t { Null | Bool | Nat | Int | Text => true, + Service => false, Unknown => panic!("Unknown type"), Knot(_) => true, Opt(_) | Vec(_) | Record(_) | Variant(_) => false, diff --git a/src/serde_idl/Cargo.toml b/src/serde_idl/Cargo.toml index e8ae2a7363..15b244ecc7 100644 --- a/src/serde_idl/Cargo.toml +++ b/src/serde_idl/Cargo.toml @@ -15,6 +15,7 @@ serde_bytes = "0.11.2" paste = "0.1" num_enum = "0.4.1" dfx_info = { path = "../dfx_info" } +ic-http-agent = { path = "../ic_http_agent" } lalrpop-util = "0.17.2" pretty = "0.5" diff --git a/src/serde_idl/src/de.rs b/src/serde_idl/src/de.rs index bdc8c6d55f..16ceb54bc5 100644 --- a/src/serde_idl/src/de.rs +++ b/src/serde_idl/src/de.rs @@ -25,6 +25,7 @@ enum Opcode { Vec = -19, Record = -20, Variant = -21, + Service = -23, } /// Use this struct to deserialize a sequence of Rust values (heterogeneous) from IDL binary message. @@ -152,17 +153,21 @@ impl<'de> Deserializer<'de> { fn sleb128_read(&mut self) -> Result { sleb128_decode(&mut self.input).map_err(Error::msg) } - fn parse_string(&mut self, len: usize) -> Result { - let mut buf = Vec::new(); - buf.resize(len, 0); - self.input.read_exact(&mut buf)?; - String::from_utf8(buf).map_err(Error::msg) - } fn parse_byte(&mut self) -> Result { let mut buf = [0u8; 1]; self.input.read_exact(&mut buf)?; Ok(buf[0]) } + fn parse_bytes(&mut self, len: usize) -> Result> { + let mut buf = Vec::new(); + buf.resize(len, 0); + self.input.read_exact(&mut buf)?; + Ok(buf) + } + fn parse_string(&mut self, len: usize) -> Result { + let buf = self.parse_bytes(len)?; + String::from_utf8(buf).map_err(Error::msg) + } fn parse_magic(&mut self) -> Result<()> { let mut buf = [0u8; 4]; match self.input.read(&mut buf) { @@ -190,6 +195,18 @@ impl<'de> Deserializer<'de> { buf.push(RawValue::I(self.sleb128_read()?)); } } + Ok(Opcode::Service) => { + let meth_len = self.leb128_read()?; + buf.push(RawValue::U(meth_len)); + for _ in 0..meth_len { + let name_len = self.leb128_read()?; + buf.push(RawValue::U(name_len)); + for _ in 0..name_len { + buf.push(RawValue::U(self.parse_byte()? as u64)); + } + buf.push(RawValue::I(self.sleb128_read()?)); + } + } _ => { return Err(Error::msg(format!( "Unsupported op_code {} in type table", @@ -298,6 +315,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { Opcode::Opt => self.deserialize_option(visitor), Opcode::Record => self.deserialize_struct("_", &[], visitor), Opcode::Variant => self.deserialize_enum("_", &[], visitor), + Opcode::Service => self.deserialize_any(visitor), } } @@ -342,6 +360,24 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } visitor.visit_enum(Compound::new(&mut self, Style::Enum { len, fs })) } + Opcode::Service => { + self.check_type(Opcode::Service)?; + let len = self.pop_current_type()?.get_u64()? as u32; + for _ in 0..len { + let name_len = self.pop_current_type()?.get_u64()? as u32; + for _ in 0..name_len { + self.pop_current_type()?.get_u64()?; + } + self.pop_current_type()?.get_i64()?; + } + let bit = self.parse_byte()?; + if bit != 1u8 { + return Err(Error::msg("Opaque reference not supported")); + } + let len = self.leb128_read()? as usize; + let vec = self.parse_bytes(len)?; + visitor.visit_byte_buf(vec) + } } } diff --git a/src/serde_idl/src/grammar.lalrpop b/src/serde_idl/src/grammar.lalrpop index ff073f59aa..4f18673ee5 100644 --- a/src/serde_idl/src/grammar.lalrpop +++ b/src/serde_idl/src/grammar.lalrpop @@ -69,6 +69,7 @@ Arg: IDLValue = { IDLValue::Record(fs) }, "variant" "{" "}" => IDLValue::Variant(Box::new(<>)), + "service" <"text"> => IDLValue::Service(<>), } Field: IDLField = { diff --git a/src/serde_idl/src/lib.rs b/src/serde_idl/src/lib.rs index 0e2916a52e..e13407b555 100644 --- a/src/serde_idl/src/lib.rs +++ b/src/serde_idl/src/lib.rs @@ -47,6 +47,7 @@ //! ``` extern crate dfx_info; +extern crate ic_http_agent; extern crate leb128; extern crate num_enum; extern crate serde; diff --git a/src/serde_idl/src/ser.rs b/src/serde_idl/src/ser.rs index 46bf432767..a31f5816c8 100644 --- a/src/serde_idl/src/ser.rs +++ b/src/serde_idl/src/ser.rs @@ -6,6 +6,7 @@ use super::value::IDLValue; use dfx_info::types::{Field, Type}; use std::collections::HashMap; use std::io; +use std::io::Write; use std::vec::Vec; use leb128::write::{signed as sleb128_encode, unsigned as leb128_encode}; @@ -67,6 +68,9 @@ impl ValueSerializer { fn write_leb128(&mut self, value: u64) { leb128_encode(&mut self.value, value).unwrap(); } + fn write(&mut self, bytes: &[u8]) { + self.value.write_all(bytes).unwrap(); + } } impl<'a> dfx_info::Serializer for &'a mut ValueSerializer { @@ -74,7 +78,7 @@ impl<'a> dfx_info::Serializer for &'a mut ValueSerializer { type Compound = Compound<'a>; fn serialize_bool(self, v: bool) -> Result<()> { let v = if v { 1 } else { 0 }; - self.write_leb128(v); + self.write(&[v]); Ok(()) } fn serialize_int(self, v: i64) -> Result<()> { @@ -91,6 +95,12 @@ impl<'a> dfx_info::Serializer for &'a mut ValueSerializer { self.value.append(&mut buf); Ok(()) } + fn serialize_blob(self, blob: &[u8]) -> Result<()> { + self.write(&[1]); + self.write_leb128(blob.len() as u64); + self.write(blob); + Ok(()) + } fn serialize_null(self, _v: ()) -> Result<()> { Ok(()) } @@ -209,6 +219,11 @@ impl TypeSerialize { self.encode(&mut buf, ty)?; } } + Type::Service => { + sleb128_encode(&mut buf, -23)?; + // TODO add method types + leb128_encode(&mut buf, 0)?; + } _ => panic!("unreachable"), }; self.type_table[idx] = buf; diff --git a/src/serde_idl/src/value.rs b/src/serde_idl/src/value.rs index 8f8756bfc4..b74f9e2a93 100644 --- a/src/serde_idl/src/value.rs +++ b/src/serde_idl/src/value.rs @@ -1,4 +1,5 @@ use dfx_info::types::{Field, Type}; +use ic_http_agent::CanisterId; use serde::de; use serde::de::{Deserialize, Visitor}; use std::fmt; @@ -16,6 +17,7 @@ pub enum IDLValue { Vec(Vec), Record(Vec), Variant(Box), + Service(String), } #[derive(Debug, PartialEq, Clone)] @@ -110,6 +112,7 @@ impl fmt::Display for IDLValue { write!(f, "}}") } IDLValue::Variant(ref v) => write!(f, "variant {{ {} }}", v), + IDLValue::Service(ref s) => write!(f, "service \"{}\"", s), } } } @@ -169,6 +172,7 @@ impl dfx_info::IDLType for IDLValue { }; Type::Variant(vec![f]) } + IDLValue::Service(_) => Type::Service, } } fn idl_serialize(&self, serializer: S) -> Result<(), S::Error> @@ -203,6 +207,10 @@ impl dfx_info::IDLType for IDLValue { ser.serialize_element(&v.val)?; Ok(()) } + IDLValue::Service(ref s) => { + let blob = CanisterId::from_text(s).unwrap().into_blob(); + serializer.serialize_blob(blob.as_slice()) + } } } } @@ -237,6 +245,10 @@ impl<'de> Deserialize<'de> for IDLValue { { self.visit_string(String::from(value)) } + fn visit_byte_buf(self, blob: Vec) -> Result { + let canister_id = CanisterId::from_bytes(blob); + Ok(IDLValue::Service(canister_id.to_text())) + } fn visit_none(self) -> Result { Ok(IDLValue::None) } diff --git a/src/serde_idl/tests/value.rs b/src/serde_idl/tests/value.rs index a6bd8e0c0e..79bde3c422 100644 --- a/src/serde_idl/tests/value.rs +++ b/src/serde_idl/tests/value.rs @@ -15,6 +15,7 @@ fn test_parser() { parse_check( "(variant { cons=record{ 42; variant { cons=record{43; variant { nil=record{} }} } } })", ); + parse_check("(service \"ic:00\")"); } #[test] @@ -67,6 +68,16 @@ fn test_variant() { test_decode(&encoded, &value); } +#[test] +fn test_reference() { + use IDLValue::*; + check(Service("ic:00".to_string()), "4449444c01690001000100"); + check( + Service("ic:ABCD01A7".to_string()), + "4449444c01690001000103abcd01", + ); +} + fn parse_check(str: &str) { let args = str.parse::().unwrap(); let encoded = args.to_bytes().unwrap();