From b98fe2ceb4db1fbaf04e40295cb2b3bf5e15565f Mon Sep 17 00:00:00 2001 From: Ivan Kalinin Date: Fri, 1 Sep 2023 01:54:25 +0200 Subject: [PATCH] wip: Add token value serializer --- src/abi/error.rs | 11 ++++ src/abi/mod.rs | 54 +++++++++++++++++++ src/abi/value.rs | 110 +++++++++++++++++++++++++++++++++++++- src/cell/builder.rs | 6 +++ src/cell/cell_impl/mod.rs | 4 +- 5 files changed, 182 insertions(+), 3 deletions(-) diff --git a/src/abi/error.rs b/src/abi/error.rs index 437ae523..f21f540c 100644 --- a/src/abi/error.rs +++ b/src/abi/error.rs @@ -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 { diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 3153e2c8..5d0f46d0 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -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}; @@ -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 { + 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) + } +} diff --git a/src/abi/value.rs b/src/abi/value.rs index 2b3eb49a..c8d3e824 100644 --- a/src/abi/value.rs +++ b/src/abi/value.rs @@ -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; @@ -133,6 +135,19 @@ impl AbiValue { AbiValue::Ref(value) => AbiType::Ref(Box::new(value.get_type())), } } + + fn store_into_part(&self, version: AbiVersion) -> Result { + // 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 @@ -196,3 +211,96 @@ impl AbiHeader { ) } } + +struct SerializedValue { + pub data: CellBuilder, + pub max_bits: usize, + pub max_refs: usize, +} + +fn write_int(bits: u16, sign: num_bigint::Sign, value: &BigUint) -> Result { + #[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 { + 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( + value: &dyn Store, + max_bits: usize, + max_refs: usize, +) -> Result { + let mut data = CellBuilder::new(); + ok!(value.store_into(&mut data, &mut Cell::default_finalizer())); + Ok(SerializedValue { + data, + max_bits, + max_refs, + }) +} diff --git a/src/cell/builder.rs b/src/cell/builder.rs index 5ae77bc4..fd020bde 100644 --- a/src/cell/builder.rs +++ b/src/cell/builder.rs @@ -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> { diff --git a/src/cell/cell_impl/mod.rs b/src/cell/cell_impl/mod.rs index 362423c6..575e8e9c 100644 --- a/src/cell/cell_impl/mod.rs +++ b/src/cell/cell_impl/mod.rs @@ -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, @@ -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,