Skip to content
Permalink
Browse files
Receipt optimized encoding (#99)
  • Loading branch information
YaronWittenstein committed Mar 18, 2020
1 parent 4135d47 commit 0cda2e404b6e78677fb8e8376bf70b558a93db3d
Showing 22 changed files with 201 additions and 299 deletions.
@@ -17,6 +17,7 @@ pub enum Field {
FuncBufLength,
FuncBuf,
FuncArgsNoMoreMark,
ErrorLength,
}

impl fmt::Display for Field {
@@ -4,22 +4,30 @@ use crate::{
};

use super::super::{concat_nibbles, Field, Nibble, NibbleIter, NibbleWriter};
use super::{encode_func_args, WasmValueLayout, DO_SKIP, NO_MORE};
use super::{WasmValueLayout, DO_SKIP, NO_MORE};

pub fn decode_func_args(iter: &mut NibbleIter) -> Result<Vec<WasmValue>, ParseError> {
let mut func_args = Vec::new();
let layouts = decode_func_args_layout(iter)?;
decode_func_values(iter)
}

pub fn decode_func_rets(iter: &mut NibbleIter) -> Result<Vec<WasmValue>, ParseError> {
decode_func_values(iter)
}

fn decode_func_values(iter: &mut NibbleIter) -> Result<Vec<WasmValue>, ParseError> {
let mut func_values = Vec::new();
let layouts = decode_values_layouts(iter)?;

for (i, layout) in layouts.iter().enumerate() {
let arg = decode_func_arg(layout, i, iter)?;
let val = decode_func_value(layout, i, iter)?;

func_args.push(arg);
func_values.push(val);
}

Ok(func_args)
Ok(func_values)
}

fn decode_func_arg(
fn decode_func_value(
layout: &WasmValueLayout,
arg_idx: usize,
iter: &mut NibbleIter,
@@ -92,7 +100,7 @@ fn decode_func_arg(
Ok(val)
}

fn decode_func_args_layout(iter: &mut NibbleIter) -> Result<Vec<WasmValueLayout>, ParseError> {
fn decode_values_layouts(iter: &mut NibbleIter) -> Result<Vec<WasmValueLayout>, ParseError> {
let mut args_layout = Vec::new();
let mut has_more = true;

@@ -5,33 +5,41 @@ use crate::types::{WasmType, WasmValue};
use super::super::{Field, Nibble, NibbleWriter};
use super::{WasmValueLayout, NO_MORE};

pub fn encode_func_args(args: &[WasmValue], writer: &mut NibbleWriter) {
let mut layouts = Vec::with_capacity(args.len());
pub fn encode_func_args(args: &[WasmValue], w: &mut NibbleWriter) {
encode_func_values(args, w)
}

pub fn encode_func_rets(rets: &[WasmValue], w: &mut NibbleWriter) {
encode_func_values(rets, w)
}

for arg in args.iter() {
let layout = func_arg_layout(arg);
fn encode_func_values(values: &[WasmValue], w: &mut NibbleWriter) {
let mut layouts = Vec::with_capacity(values.len());

for val in values.iter() {
let layout = wasm_value_layout(val);
let nib = (&layout).into();

layouts.push(layout);
writer.write(&[nib]);
w.write(&[nib]);
}

// output `no more func args layouts` marker.
// output `no more func values layouts` marker.
let no_more_nib = nib!(NO_MORE);
writer.write(&[no_more_nib]);
w.write(&[no_more_nib]);

// write the args values
for (i, arg) in args.iter().enumerate() {
// write the func values
for (i, val) in values.iter().enumerate() {
let layout = &layouts[i];

encode_func_arg(arg, layout, writer);
encode_func_wasm_val(val, layout, w);
}
}

fn func_arg_layout(arg: &WasmValue) -> WasmValueLayout {
match arg {
fn wasm_value_layout(value: &WasmValue) -> WasmValueLayout {
match value {
WasmValue::I32(v) => {
let len = func_arg_byte_length(*v as u64);
let len = wasm_value_byte_length(*v as u64);
debug_assert!(len <= 4);

WasmValueLayout {
@@ -40,7 +48,7 @@ fn func_arg_layout(arg: &WasmValue) -> WasmValueLayout {
}
}
WasmValue::I64(v) => {
let len = func_arg_byte_length(*v);
let len = wasm_value_byte_length(*v);
debug_assert!(len <= 8);

WasmValueLayout {
@@ -51,7 +59,7 @@ fn func_arg_layout(arg: &WasmValue) -> WasmValueLayout {
}
}

fn func_arg_byte_length(value: u64) -> usize {
fn wasm_value_byte_length(value: u64) -> usize {
match value {
0 => 0,
0x01..=0xFF => 1,
@@ -66,7 +74,7 @@ fn func_arg_byte_length(value: u64) -> usize {
}
}

fn encode_func_arg(arg: &WasmValue, layout: &WasmValueLayout, writer: &mut NibbleWriter) {
fn encode_func_wasm_val(arg: &WasmValue, layout: &WasmValueLayout, w: &mut NibbleWriter) {
let mut nibbles = Vec::with_capacity(layout.len);

let mut val = match arg {
@@ -87,8 +95,8 @@ fn encode_func_arg(arg: &WasmValue, layout: &WasmValueLayout, writer: &mut Nibbl
}

// since we've scanned `val` from `lsb` to `msb` order,
// we need to reverse `nibbles` prior calling `writer` with them.
// we need to reverse `nibbles` prior calling `w` with them.
let nibbles: Vec<_> = nibbles.drain(..).rev().collect();

writer.write(&nibbles[..])
w.write(&nibbles[..])
}
@@ -2,8 +2,8 @@ mod decoder;
mod encoder;
mod layout;

pub use decoder::decode_func_args;
pub use encoder::encode_func_args;
pub use decoder::{decode_func_args, decode_func_rets};
pub use encoder::{encode_func_args, encode_func_rets};
pub use layout::{
WasmValueLayout, DO_SKIP, I32_0B, I32_1B, I32_2B, I32_3B, I32_4B, I64_0B, I64_1B, I64_2B,
I64_3B, I64_4B, I64_5B, I64_6B, I64_7B, I64_8B, NO_MORE,
@@ -17,11 +17,11 @@ pub use app::{DefaultAppDeserializer, DefaultAppSerializer};
pub use template::{DefaultAppTemplateDeserializer, DefaultAppTemplateSerializer};

mod field;
pub(crate) use field::Field;
pub use field::Field;

pub(crate) mod helpers;

pub use func_args::{decode_func_args, encode_func_args};
pub use func_args::{decode_func_args, decode_func_rets, encode_func_args, encode_func_rets};
pub use func_buf::{decode_func_buf, encode_func_buf};
pub use nibble::{concat_nibbles, Nibble, NibbleIter, NibbleWriter};
pub use varuint14::{decode_varuint14, encode_varuint14};
@@ -18,5 +18,5 @@ pub use app_tx::AppTransaction;
pub use host_ctx::HostCtx;

pub use hash::AppTemplateHash;
pub use wasm_type::{WasmConvertTypeError, WasmType};
pub use wasm_type::{WasmType, WasmTypeError};
pub use wasm_value::WasmValue;
@@ -11,30 +11,30 @@ pub enum WasmType {
}

/// Converts `WasmType` to its numeric representation
impl Into<u8> for WasmType {
fn into(self) -> u8 {
match self {
impl From<WasmType> for u8 {
fn from(ty: WasmType) -> u8 {
match ty {
WasmType::I32 => 1,
WasmType::I64 => 2,
}
}
}

/// Wasm function arguments error
pub enum WasmConvertTypeError {
pub enum WasmTypeError {
/// Unsupported type
UnsupportedType(u8),
}

/// Converts `WasmType` to its numeric representation
impl TryFrom<u8> for WasmType {
type Error = WasmConvertTypeError;
type Error = WasmTypeError;

fn try_from(value: u8) -> Result<WasmType, WasmConvertTypeError> {
fn try_from(value: u8) -> Result<WasmType, WasmTypeError> {
match value {
1 => Ok(WasmType::I32),
2 => Ok(WasmType::I64),
_ => Err(WasmConvertTypeError::UnsupportedType(value)),
_ => Err(WasmTypeError::UnsupportedType(value)),
}
}
}
@@ -13,6 +13,7 @@

use byteorder::{BigEndian, WriteBytesExt};

use svm_app::raw::NibbleWriter;
use svm_common::Address;
use svm_runtime::{
error::DeployTemplateError,
@@ -22,27 +23,27 @@ use svm_runtime::{
use super::{encode_error, helpers};

pub(crate) fn encode_template_receipt(receipt: &TemplateReceipt) -> Vec<u8> {
let mut buf = Vec::new();
let mut w = NibbleWriter::new();

let wrapped_receipt = Receipt::DeployTemplate(receipt);

helpers::encode_is_success(&mut buf, &wrapped_receipt);
helpers::encode_version(0, &mut w);
helpers::encode_is_success(&wrapped_receipt, &mut w);

if receipt.success {
encode_template_addr(&mut buf, receipt);
encode_template_addr(receipt, &mut w);
} else {
encode_error(&mut buf, &wrapped_receipt);
encode_error(&wrapped_receipt, &mut w);
};

buf
w.into_bytes()
}

fn encode_template_addr(buf: &mut Vec<u8>, receipt: &TemplateReceipt) {
fn encode_template_addr(receipt: &TemplateReceipt, w: &mut NibbleWriter) {
debug_assert!(receipt.success);

let addr = receipt.get_template_addr();

buf.extend_from_slice(addr.inner().as_slice());
helpers::encode_addr(addr.inner(), w);
}

#[cfg(test)]
@@ -3,26 +3,26 @@
//!
//! On failure (`is_success = 0`)
//! -----------------------------------------------------
//! | format | | |
//! | | | |
//! | version | is_success | error size |
//! | (4 bytes) | (1 byte) | (2 bytes) |
//! | | (1 nibble) | (varuint14) |
//! |____________|________________|_____________________|
//! | |
//! | error data (UTF-8 string) |
//! |___________________________________________________|
//!

use svm_app::raw::{encode_varuint14, NibbleWriter};
use svm_runtime::receipt::Receipt;

use byteorder::{BigEndian, WriteBytesExt};

pub(crate) fn encode_error(buf: &mut Vec<u8>, receipt: &Receipt) {
pub(crate) fn encode_error(receipt: &Receipt, w: &mut NibbleWriter) {
debug_assert!(receipt.is_success() == false);

let error_str = receipt.error_string();
let error_bytes = error_str.as_bytes();
let error_size = error_bytes.len() as u16;

buf.write_u16::<BigEndian>(error_size).unwrap();
buf.extend_from_slice(error_bytes);
encode_varuint14(error_size, w);

w.write_bytes(error_bytes)
}
@@ -2,9 +2,9 @@
//!
//! On success (`is_success = 1`)
//! ----------------------------------------------------
//! | format | | |
//! | | | |
//! | version | is_success | app new state |
//! | (4 bytes) | (1 byte) | (32 bytes) |
//! | | (1 nibble) | (32 bytes) |
//! |____________|______________|_______________________|
//! | | | | |
//! | #returns | ret #1 type | ret #1 | . . . . |
@@ -16,39 +16,40 @@

use byteorder::{BigEndian, WriteBytesExt};

use svm_app::raw::NibbleWriter;
use svm_common::State;
use svm_runtime::{
error::ExecAppError,
receipt::{ExecReceipt, Receipt},
value::Value,
};

use super::{encode_error, helpers};
use crate::svm_value_type;

pub(crate) fn encode_exec_receipt(receipt: &ExecReceipt) -> Vec<u8> {
let mut buf = Vec::new();
let mut w = NibbleWriter::new();

let wrapped_receipt = Receipt::ExecApp(receipt);

helpers::encode_is_success(&mut buf, &wrapped_receipt);
helpers::encode_version(0, &mut w);
helpers::encode_is_success(&wrapped_receipt, &mut w);

if receipt.success {
encode_new_state(&mut buf, receipt);
helpers::encode_returns(&mut buf, &wrapped_receipt);
encode_new_state(receipt, &mut w);
helpers::encode_returns(&wrapped_receipt, &mut w);
} else {
encode_error(&mut buf, &wrapped_receipt);
encode_error(&wrapped_receipt, &mut w);
};

buf
w.into_bytes()
}

fn encode_new_state(buf: &mut Vec<u8>, receipt: &ExecReceipt) {
fn encode_new_state(receipt: &ExecReceipt, w: &mut NibbleWriter) {
debug_assert!(receipt.success);

let new_state = receipt.get_new_state();

buf.extend_from_slice(new_state.as_slice());
helpers::encode_state(&new_state, w);
}

#[cfg(test)]
@@ -57,8 +58,9 @@ mod tests {

use crate::testing::{self, ClientExecReceipt};

use svm_app::types::WasmValue;
use svm_common::{Address, State};
use svm_runtime::{error::ExecAppError, value::Value};
use svm_runtime::error::ExecAppError;

#[test]
fn encode_decode_exec_receipt_error() {
@@ -110,7 +112,7 @@ mod tests {
#[test]
fn encode_decode_exec_receipt_success_with_returns() {
let new_state = State::from(0x10_20_30_40);
let returns = vec![Value::I32(10), Value::I64(20), Value::I32(30)];
let returns = vec![WasmValue::I32(10), WasmValue::I64(20), WasmValue::I32(30)];

let expected = ClientExecReceipt::Success {
new_state: new_state.clone(),

0 comments on commit 0cda2e4

Please sign in to comment.