Skip to content

Commit

Permalink
wip: Add token value serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 31, 2023
1 parent dab2e0a commit b98fe2c
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 3 deletions.
11 changes: 11 additions & 0 deletions src/abi/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
//! ABI related error types.
/// Error type for ABI version parsing related errors.
#[derive(Debug, Clone, thiserror::Error)]
pub enum ParseAbiVersionError {
/// Expected a dot separated major and minor components.
#[error("invalid format")]
InvalidFormat,
/// Failed to parse version component as number.
#[error("invalid component")]
InvalidComponent(#[source] std::num::ParseIntError),
}

/// Error type for ABI type parsing related errors.
#[derive(Debug, Clone, thiserror::Error)]
pub enum ParseAbiTypeError {
Expand Down
54 changes: 54 additions & 0 deletions src/abi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Common ABI implementation.
use std::str::FromStr;

pub use self::traits::{WithAbiType, WithPlainAbiType};
pub use self::ty::{AbiHeaderType, AbiType, NamedAbiType, PlainAbiType};
pub use self::value::{AbiHeader, AbiValue, NamedAbiValue, PlainAbiValue};
Expand All @@ -9,3 +11,55 @@ pub mod error;
mod traits;
mod ty;
mod value;

/// ABI version.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct AbiVersion {
/// Major version component.
pub major: u8,
/// Minor version component.
pub minor: u8,
}

impl AbiVersion {
/// A legacy ABI version.
pub const V1_0: Self = Self::new(1, 0);
/// A base version of an ABI 2.
pub const V2_0: Self = Self::new(2, 0);
/// A base version with strings and refs.
pub const V2_1: Self = Self::new(2, 1);
/// Same as 2.1 but with a more compact address serialization.
pub const V2_2: Self = Self::new(2, 2);
/// Same as 2.2 but uses an address during signing.
pub const V2_3: Self = Self::new(2, 3);

/// Creates an ABI version from components.
pub const fn new(major: u8, minor: u8) -> Self {
Self { major, minor }
}
}

impl FromStr for AbiVersion {
type Err = error::ParseAbiVersionError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let (major, minor) = ok!(s
.split_once('.')
.ok_or(error::ParseAbiVersionError::InvalidFormat));

Ok(Self {
major: ok!(major
.parse()
.map_err(error::ParseAbiVersionError::InvalidComponent)),
minor: ok!(minor
.parse()
.map_err(error::ParseAbiVersionError::InvalidComponent)),
})
}
}

impl std::fmt::Display for AbiVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.major, self.minor)
}
}
110 changes: 109 additions & 1 deletion src/abi/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use everscale_crypto::ed25519;
use num_bigint::{BigInt, BigUint};

use super::ty::*;
use crate::cell::Cell;
use super::AbiVersion;
use crate::cell::{Cell, CellBuilder, DefaultFinalizer, Store};
use crate::error::Error;
use crate::models::IntAddr;
use crate::num::Tokens;

Expand Down Expand Up @@ -133,6 +135,19 @@ impl AbiValue {
AbiValue::Ref(value) => AbiType::Ref(Box::new(value.get_type())),
}
}

fn store_into_part(&self, version: AbiVersion) -> Result<SerializedValue, Error> {

Check failure on line 139 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Lints

unused variable: `version`

Check failure on line 139 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Lints

method `store_into_part` is never used

Check warning on line 139 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Check

unused variable: `version`

Check warning on line 139 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Check

method `store_into_part` is never used

Check warning on line 139 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused variable: `version`

Check warning on line 139 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Test Suite

method `store_into_part` is never used
// TODO: use stack from builders and reuse the top item when possible
match self {
Self::Uint(bits, value) => write_int(*bits, num_bigint::Sign::Plus, value),
Self::Int(bits, value) => write_int(*bits, value.sign(), value.magnitude()),
Self::Bool(value) => write_simple(value, 1, 0),
Self::Cell(value) => write_simple(value, 0, 1),
Self::Address(value) => write_simple(value, IntAddr::BITS_MAX as usize, 0),
Self::Token(value) => write_simple(value, Tokens::MAX_BITS as usize, 0),
_ => todo!(),
}
}
}

/// ABI value which has a fixed bits representation
Expand Down Expand Up @@ -196,3 +211,96 @@ impl AbiHeader {
)
}
}

struct SerializedValue {

Check failure on line 215 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Lints

struct `SerializedValue` is never constructed

Check warning on line 215 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Check

struct `SerializedValue` is never constructed

Check warning on line 215 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Test Suite

struct `SerializedValue` is never constructed
pub data: CellBuilder,
pub max_bits: usize,
pub max_refs: usize,
}

fn write_int(bits: u16, sign: num_bigint::Sign, value: &BigUint) -> Result<SerializedValue, Error> {

Check failure on line 221 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Lints

function `write_int` is never used

Check warning on line 221 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Check

function `write_int` is never used

Check warning on line 221 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Test Suite

function `write_int` is never used
#[inline]
fn is_zero(value: &u8) -> bool {
*value == 0
}

#[inline]
pub fn twos_complement_le(digits: &mut [u8]) {
let mut carry = true;
for digit in digits {
*digit = !*digit;
if carry {
let (d, c) = digit.overflowing_add(1);
*digit = d;
carry = c;
}
}
}

fn to_signed_bytes_be(value: &BigUint) -> Vec<u8> {
let mut bytes = value.to_bytes_le();
let last_byte = bytes.last().cloned().unwrap_or(0);
if last_byte > 0x7f && !(last_byte == 0x80 && bytes.iter().rev().skip(1).all(is_zero)) {
// msb used by magnitude, extend by 1 byte
bytes.push(0);
}
twos_complement_le(&mut bytes);
bytes.reverse();
bytes
}

let mut result = CellBuilder::new();

let is_negative = sign == num_bigint::Sign::Minus;

let bytes = if is_negative {
to_signed_bytes_be(value)
} else {
value.to_bytes_be()
};
let value_bits = (bytes.len() * 8) as u16;

if bytes.is_empty() {
ok!(result.store_zeros(bits));
} else if bits > value_bits {
let diff = bits - value_bits;
ok!(if is_negative {
result.store_ones(diff)
} else {
result.store_zeros(diff)
});
ok!(result.store_raw(&bytes, value_bits));
} else {
let bits_offset = value_bits - bits;
let bytes_offset = (bits_offset / 8) as usize;
let rem = bits_offset % 8;

let (left, right) = bytes.split_at(bytes_offset + 1);
if let Some(left) = left.last() {
ok!(result.store_small_uint(*left << rem, 8 - rem));
}
if !right.is_empty() {
ok!(result.store_raw(right, right.len() as u16));
}
}

Ok(SerializedValue {
data: result,
max_bits: bits as usize,
max_refs: 0,
})
}

fn write_simple(

Check failure on line 294 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Lints

function `write_simple` is never used

Check warning on line 294 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Check

function `write_simple` is never used

Check warning on line 294 in src/abi/value.rs

View workflow job for this annotation

GitHub Actions / Test Suite

function `write_simple` is never used
value: &dyn Store,
max_bits: usize,
max_refs: usize,
) -> Result<SerializedValue, Error> {
let mut data = CellBuilder::new();
ok!(value.store_into(&mut data, &mut Cell::default_finalizer()));
Ok(SerializedValue {
data,
max_bits,
max_refs,
})
}
6 changes: 6 additions & 0 deletions src/cell/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,12 @@ impl CellBuilder {
}
}

/// Tries to store the specified number of set bits in the cell,
/// returning `false` if there is not enough remaining capacity.
pub fn store_ones(&mut self, bits: u16) -> Result<(), Error> {
self.store_raw(crate::cell::cell_impl::ALL_ONES_CELL.data(), bits)
}

/// Tries to store one zero bit in the cell,
/// returning `false` if there is not enough remaining capacity.
pub fn store_bit_zero(&mut self) -> Result<(), Error> {
Expand Down
4 changes: 2 additions & 2 deletions src/cell/cell_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl CellImpl for StaticCell {
}
}

static ALL_ZEROS_CELL: StaticCell = StaticCell {
pub(crate) static ALL_ZEROS_CELL: StaticCell = StaticCell {
descriptor: CellDescriptor::new([0, 0xff]),
data: &ALL_ZEROS_CELL_DATA,
bit_len: 1023,
Expand All @@ -212,7 +212,7 @@ const ALL_ZEROS_CELL_HASH: [u8; 32] = [
0x88, 0x9e, 0xbd, 0xf9, 0xd3, 0xb2, 0xf0, 0x1d, 0xbf, 0x94, 0x2c, 0x29, 0xbc, 0x48, 0x98, 0x71,
];

static ALL_ONES_CELL: StaticCell = StaticCell {
pub(crate) static ALL_ONES_CELL: StaticCell = StaticCell {
descriptor: CellDescriptor::new([0, 0xff]),
data: &ALL_ONES_CELL_DATA,
bit_len: 1023,
Expand Down

0 comments on commit b98fe2c

Please sign in to comment.