Skip to content

Commit

Permalink
Add abi value
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 31, 2023
1 parent 2fdd400 commit dab2e0a
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 3 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ base64 = { version = "0.21.0", optional = true }
bitflags = "2.3"
bytes = { version = "1.4", optional = true }
crc32c = "0.6"
everscale-crypto = { version = "0.2", features = ["tl-proto"], optional = true }
hex = "0.4"
num-bigint = { version = "0.4", optional = true }
once_cell = "1.16"
serde = { version = "1", features = ["derive"], optional = true }
sha2 = "0.10"
smallvec = { version = "1.9", features = ["union"] }
thiserror = "1.0"

tl-proto = { version = "0.4", optional = true }
everscale-crypto = { version = "0.2", features = ["tl-proto"], optional = true }

everscale-types-proc = { version = "=0.1.3", path = "proc" }

Expand All @@ -54,7 +54,7 @@ sync = []
stats = []
serde = ["dep:serde", "base64"]
models = ["dep:everscale-crypto", "dep:tl-proto"]
abi = ["dep:serde", "dep:bytes", "models"]
abi = ["dep:serde", "dep:bytes", "dep:num-bigint", "models"]
venom = []

[profile.release]
Expand Down
2 changes: 2 additions & 0 deletions src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pub use self::traits::{WithAbiType, WithPlainAbiType};
pub use self::ty::{AbiHeaderType, AbiType, NamedAbiType, PlainAbiType};
pub use self::value::{AbiHeader, AbiValue, NamedAbiValue, PlainAbiValue};

pub mod error;

mod traits;
mod ty;
mod value;
33 changes: 33 additions & 0 deletions src/abi/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::collections::{BTreeMap, HashMap};
use std::num::NonZeroU8;
use std::rc::Rc;
use std::sync::Arc;

use bytes::Bytes;

Expand Down Expand Up @@ -55,12 +57,43 @@ impl_with_abi_type! {

Bytes => Bytes,
String => String,
str => String,

IntAddr => Address,
StdAddr => Address,
VarAddr => Address,
}

impl<T: WithAbiType> WithAbiType for &T {
fn abi_type() -> AbiType {
T::abi_type()
}
}

impl<T: WithAbiType> WithAbiType for &mut T {
fn abi_type() -> AbiType {
T::abi_type()
}
}

impl<T: WithAbiType> WithAbiType for Box<T> {
fn abi_type() -> AbiType {
T::abi_type()
}
}

impl<T: WithAbiType> WithAbiType for Arc<T> {
fn abi_type() -> AbiType {
T::abi_type()
}
}

impl<T: WithAbiType> WithAbiType for Rc<T> {
fn abi_type() -> AbiType {
T::abi_type()
}
}

impl<T: WithAbiType> WithAbiType for Option<T> {
fn abi_type() -> AbiType {
AbiType::Optional(Box::new(T::abi_type()))
Expand Down
198 changes: 198 additions & 0 deletions src/abi/value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
use std::collections::BTreeMap;
use std::num::NonZeroU8;

use bytes::Bytes;
use everscale_crypto::ed25519;
use num_bigint::{BigInt, BigUint};

use super::ty::*;
use crate::cell::Cell;
use crate::models::IntAddr;
use crate::num::Tokens;

/// ABI value with name.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct NamedAbiValue {
/// Item name.
pub name: String,
/// ABI value.
pub value: AbiValue,
}

impl NamedAbiValue {
/// Returns whether all values satisfy the provided types.
pub fn have_types(items: &[Self], types: &[NamedAbiType]) -> bool {
items.len() == types.len()
&& items
.iter()
.zip(types.iter())
.all(|(item, t)| item.value.has_type(&t.ty))
}
}

/// ABI encoded value.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum AbiValue {
/// Unsigned integer of n bits.
Uint(u16, BigUint),
/// Signed integer of n bits.
Int(u16, BigInt),
/// Variable-length unsigned integer of maximum n bytes.
VarUint(NonZeroU8, BigUint),
/// Variable-length signed integer of maximum n bytes.
VarInt(NonZeroU8, BigInt),
/// Boolean.
Bool(bool),
/// Tree of cells ([`Cell`]).
///
/// [`Cell`]: crate::cell::Cell
Cell(Cell),
/// Internal address ([`IntAddr`]).
///
/// [`IntAddr`]: crate::models::message::IntAddr
Address(Box<IntAddr>),
/// Byte array.
Bytes(Bytes),
/// Byte array of fixed length.
FixedBytes(Bytes),
/// Utf8-encoded string.
String(String),
/// Variable length 120-bit integer ([`Tokens`]).
///
/// [`Tokens`]: crate::num::Tokens
Token(Tokens),
/// Product type.
Tuple(Vec<NamedAbiValue>),
/// Array of ABI values.
Array(Box<AbiType>, Vec<Self>),
/// Fixed-length array of ABI values.
FixedArray(Box<AbiType>, Vec<Self>),
/// Sorted dictionary of ABI values.
Map(
PlainAbiType,
Box<AbiType>,
BTreeMap<PlainAbiValue, AbiValue>,
),
/// Optional value.
Optional(Box<AbiType>, Option<Box<Self>>),
/// Value stored in a new cell.
Ref(Box<Self>),
}

impl AbiValue {
/// Returns whether this value has the same type as the provided one.
pub fn has_type(&self, ty: &AbiType) -> bool {
match (self, ty) {
(Self::Uint(n, _), AbiType::Uint(t)) => n == t,
(Self::Int(n, _), AbiType::Int(t)) => n == t,
(Self::VarUint(n, _), AbiType::VarUint(t)) => n == t,
(Self::VarInt(n, _), AbiType::VarInt(t)) => n == t,
(Self::FixedBytes(bytes), AbiType::FixedBytes(len)) => bytes.len() == *len,
(Self::Tuple(items), AbiType::Tuple(types)) => NamedAbiValue::have_types(items, types),
(Self::Array(ty, _), AbiType::Array(t)) => ty == t,
(Self::FixedArray(ty, items), AbiType::FixedArray(t, len)) => {
items.len() == *len && ty == t
}
(Self::Map(key_ty, value_ty, _), AbiType::Map(k, v)) => key_ty == k && value_ty == v,
(Self::Optional(ty, _), AbiType::Optional(t)) => ty == t,
(Self::Ref(value), AbiType::Ref(t)) => value.has_type(t),
(Self::Bool(_), AbiType::Bool)
| (Self::Cell(_), AbiType::Cell)
| (Self::Address(_), AbiType::Address)
| (Self::Bytes(_), AbiType::Bytes)
| (Self::String(_), AbiType::String)
| (Self::Token(_), AbiType::Token) => true,
_ => false,
}
}

/// Returns an ABI type of the value.
pub fn get_type(&self) -> AbiType {
match self {
AbiValue::Uint(n, _) => AbiType::Uint(*n),
AbiValue::Int(n, _) => AbiType::Int(*n),
AbiValue::VarUint(n, _) => AbiType::VarUint(*n),
AbiValue::VarInt(n, _) => AbiType::VarInt(*n),
AbiValue::Bool(_) => AbiType::Bool,
AbiValue::Cell(_) => AbiType::Cell,
AbiValue::Address(_) => AbiType::Address,
AbiValue::Bytes(_) => AbiType::Bytes,
AbiValue::FixedBytes(bytes) => AbiType::FixedBytes(bytes.len()),
AbiValue::String(_) => AbiType::String,
AbiValue::Token(_) => AbiType::Token,
AbiValue::Tuple(items) => AbiType::Tuple(
items
.iter()
.map(|item| NamedAbiType::new(item.name.clone(), item.value.get_type()))
.collect(),
),
AbiValue::Array(ty, _) => AbiType::Array(ty.clone()),
AbiValue::FixedArray(ty, items) => AbiType::FixedArray(ty.clone(), items.len()),
AbiValue::Map(key_ty, value_ty, _) => AbiType::Map(*key_ty, value_ty.clone()),
AbiValue::Optional(ty, _) => AbiType::Optional(ty.clone()),
AbiValue::Ref(value) => AbiType::Ref(Box::new(value.get_type())),
}
}
}

/// ABI value which has a fixed bits representation
/// and therefore can be used as a map key.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum PlainAbiValue {
/// Unsigned integer of n bits.
Uint(u16, BigUint),
/// Signed integer of n bits.
Int(u16, BigInt),
/// Boolean.
Bool(bool),
/// Internal address ([`IntAddr`]).
///
/// [`IntAddr`]: crate::models::message::IntAddr
Address(Box<IntAddr>),
}

impl PlainAbiValue {
/// Returns whether this value has the same type as the provided one.
pub fn has_type(&self, ty: &PlainAbiType) -> bool {
match (self, ty) {
(Self::Uint(n, _), PlainAbiType::Uint(t)) => n == t,
(Self::Int(n, _), PlainAbiType::Int(t)) => n == t,
(Self::Bool(_), PlainAbiType::Bool) | (Self::Address(_), PlainAbiType::Address) => true,
_ => false,
}
}
}

impl From<PlainAbiValue> for AbiValue {
fn from(value: PlainAbiValue) -> Self {
match value {
PlainAbiValue::Uint(n, value) => Self::Uint(n, value),
PlainAbiValue::Int(n, value) => Self::Int(n, value),
PlainAbiValue::Bool(value) => Self::Bool(value),
PlainAbiValue::Address(value) => Self::Address(value),
}
}
}

/// ABI header value.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum AbiHeader {
/// `time` header.
Time(u64),
/// `expire` header.
Expire(u32),
/// `pubkey` header.
PublicKey(Option<Box<ed25519::PublicKey>>),
}

impl AbiHeader {
/// Returns whether this value has the same type as the provided one.
pub fn has_type(&self, ty: &AbiHeaderType) -> bool {
matches!(
(self, ty),
(Self::Time(_), AbiHeaderType::Time)
| (Self::Expire(_), AbiHeaderType::Expire)
| (Self::PublicKey(_), AbiHeaderType::PublicKey)
)
}
}

0 comments on commit dab2e0a

Please sign in to comment.