From 6d20d2e58f43f451e9942aba45a99c6472ed152b Mon Sep 17 00:00:00 2001 From: Alexander Gapak Date: Thu, 27 Jul 2023 12:07:32 -0400 Subject: [PATCH 01/15] Update ABI.md spec (2.4) --- docs/ABI.md | 92 ++++++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/docs/ABI.md b/docs/ABI.md index 0f2483d7..9fec223a 100644 --- a/docs/ABI.md +++ b/docs/ABI.md @@ -1,4 +1,8 @@ -# Smart Contracts ABI v2.3 Specification +--- +title: ABI Specification +--- + +# Smart Contracts ABI v2.4 Specification ABI specifies message bodies layout for client to contract and contract to contract interaction. @@ -20,7 +24,7 @@ Message body with encoded function call has the following format: First comes an optional signature. It is prefixed by one bit flag that indicates the signature presence. If it is `1`, then in the next `512 bit` a signature is placed, otherwise the signature is omitted. -Then comes the encoded header parameters set (same for all functions). +Then comes the encoded header parameters set (same for all functions). It is followed by ***32 bits*** of function ID identifying which contract functions are called. The `function ID` comes within the first `32 bits` of the `SHA256` hash of the function signature. @@ -153,12 +157,30 @@ If a function has no input parameters or does not return any values, the corresp - Note: this type is useful to store payloads as a tree of cells analog to contract code and data in the form of `StateInit` structure of `message` structure. - [`address`](#address) is an account address in Everscale blockchain. Encoded as `MsgAddress` struct (see TL-B schema in blockchain [spec](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L107)). - [`bytes`](#bytes): an array of `uint8` type elements. The array is put into a separate cell. -- [`fixedbytes`](#fixedbytesn): a fixed-size array of `N` `uint8` type elements. Encoding is equivalent to `bytes` - [`string`](#string) - a type containing UTF-8 string data, encoded like `bytes`. - [`optional`](#optionalinnertype) - value of optional type `optional(innerType)` can store a value of `innerType` or be empty. - [`itemType[]`](#itemtype) is a dynamic array of `itemType` type elements. It is encoded as a TVM dictionary. `uint32` defines the array elements count placed into the cell body. `HashmapE` (see TL-B schema in TVM spec) struct is then added (one bit as a dictionary root and one reference with data if the dictionary is not empty). The dictionary key is a serialized `uint32` index of the array element, and the value is a serialized array element as `itemType` type. - `T[k]` is a static size array of `T` type elements. Encoding is equivalent to `T[]` without elements count. +## Default values for parameter types + +Starting from API 2.4 the specification defines default values for parameter types. + +- [`int`](#intn) – `N` zero bits. +- [`uint`](#uintn) – `N` zero bits. +- [`varint`](#varintn)/[`varuint`](#varuintn) – `x` zero bits, where `x = [log2(N)]`. +- [`bool`](#bool) – equivalent to [`int`](#uintn), where `N = 1`. +- [`tuple(T1, T2, ..., Tn)`](#tuple) – default values for each type, i.e. `D(tuple(T1, T2, ..., Tn)) = tuple(D(T1), D(T2), ..., D(Tn))`, where `D` is defined as a function that takes ABI type and returns the corresponding default value. +- [`map(K,V)`](#mapkeytypevaluetype) – 1 zero bit, i.e. `b{0}`. +- [`cell`](#cell) – reference to an empty cell, i.e. `^EmptyCell`. +- [`address`](#address) – `addr_none$00` constructor, i.e. 2 zero bits. +- [`bytes`](#bytes) – reference to an empty cell, i.e. `^EmptyCell`. +- [`string`](#string) – reference to an empty cell, i.e. `^EmptyCell`. +- [`optional(T)`](#optionalinnertype) – 1 zero bit, i.e. `b{0}`. +- [`T[]`](#itemtype) – `x{00000000} b{0}`, i.e. 33 zero bits. +- `T[k]` – encoded as an array with `k` default values of type `T` +- `ref(T)` – reference to a cell, cell is encoded as the default value of type `T`. + ## Encoding of function ID and its arguments Function ID and the function arguments are located in the chain of cells. The last reference of each cell (except for the last cell in the chain) refers to the next cell. After adding the current parameter in the current cell we must presume an invariant (rule that stays true for the object) for our cell: number of unassigned references in the cell must be not less than 1 because the last reference is used for storing the reference on the next cell. The last cell in the chain can use all 4 references to store argument's values. @@ -274,6 +296,7 @@ type Data = Param & { type Param = { name: string, type: string, + init: boolean, components?: Param[], } ``` @@ -356,54 +379,41 @@ This section specifies the events used in the contract. An event is an external `inputs` have the same format as for functions. -### Data - -This section covers the contract global public variables. Data is typically used when deploying multiple identical contracts with the same deployer keys. It affects the contract address, and thus varying data results in unique addresses for identical contracts. - -```json5 -{ - "data": [ - { - "name": "var_name", - "type": "abi_type", - "key": "" // index of variable in contract data dictionary - }, - ] -} -``` - ### Fields -This section describes internal structure of the smart contracts data. +This section describes persistent smart contract data. Data structure is described as a list of variables names with corresponding data types and init flag. They are listed in the order in which they are stored in the smart contract data. -Data structure is described as a list of variables' names with corresponding data types. -It includes contract state variables and some internal contract specific hidden variables. -They are listed in the order in which they are stored in the data field of the contract. -Example for a Solidity contract [BankClient](https://github.com/tonlabs/samples/blob/master/solidity/5_BankClient.sol): +Fields that are `init = true` are recommended to be specified as initial data during deploy for correct contract behaviour. Tools and SDK, that responsible for encoding of this section should raise errors when a developer attempts to set a non-init (`init = false`) variable, requiring the specification of all init variables and filling non-init variables with [default values](#default-values-for-parameter-types). -Contract state variables: +:::note +In case of [Solidity Compiler implementation for TVM](https://github.com/tonlabs/TON-Solidity-Compiler/tree/master) fields with `init = true` contain Solidity static variables and some specific internal Solidity variables that are required for smart contract deploy by the compiler, for example, `_pubkey`. +::: + +Solidity contract state variables example: ```solidity -contract BankClient { - uint public creditLimit = 0; // allowed credit limit; - uint public totalDebt = 0; // contract total debt; - uint public balance = 0; // contract balance; - uint public value = 0; // inbound message value. +contract Bank { + uint256 creditLimit; + uint256 totalDebt; + uint256 balance; + uint256 value; + uint256 static seqno; } ``` -Fields section of the abi file: +Fields section of the abi file. In this case the developer will need to explicitly pass `_pubkey` and `seqno` fields, and the rest of the variables will be filled with default values for its types. ```json { "fields": [ - {"name":"_pubkey","type":"uint256"}, - {"name":"_timestamp","type":"uint64"}, - {"name":"_constructorFlag","type":"bool"}, - {"name":"creditLimit","type":"uint256"}, - {"name":"totalDebt","type":"uint256"}, - {"name":"balance","type":"uint256"}, - {"name":"value","type":"uint256"} + {"name":"_pubkey","type":"uint256","init": true}, + {"name":"_timestamp","type":"uint64","init": false}, + {"name":"_constructorFlag","type":"bool","init": false}, + {"name":"creditLimit","type":"uint256","init": false}, + {"name":"totalDebt","type":"uint256","init": false}, + {"name":"balance","type":"uint256","init": false}, + {"name":"value","type":"uint256","init": false}, + {"name":"seqno","type":"uint256","init": true} ] } ``` @@ -638,12 +648,6 @@ Analog of `bytes` in Solidity. In C lang can be used as `void*`. | Cell | cell with data stored in a ref | | 0 bit | 1 ref | | JSON object | binary daya represented as hex string | `"313233"` | | | -#### `fixedbytes` - -Where N is a decimal byte length from 1 to 32. It is denoted in abi as `uint`, -where `M` is a bit length and `M = 8 * N`. -Processed like `int`. - #### `string` UTF-8 String data. Encoded like `bytes`. In JSON is represented as a sting. From 54db84acd74baaa48a04a085f69cf6ba5ac9fe7e Mon Sep 17 00:00:00 2001 From: Alexander Gapak Date: Thu, 27 Jul 2023 12:13:56 -0400 Subject: [PATCH 02/15] Update CHANGELOG.md (2.4) --- CHANGELOG.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e5489e..dd548974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ All notable changes to this project will be documented in this file. +## Version 2.4 + +- Param in fields section extended with `init: boolean` + +- Default values for parameter types: +- - `int` – `N` zero bits. +- - `uint` – `N` zero bits. +- - `varint`/`varuint` – `x` zero bits, where `x = [log2(N)]`. +- - `bool` – equivalent to [`int`](#uintn), where `N = 1`. +- - `tuple(T1, T2, ..., Tn)` – default values for each type, i.e. `D(tuple(T1, T2, ..., Tn)) = tuple(D(T1), D(T2), ..., D(Tn))`, where `D` is defined as a function that takes ABI type and returns the corresponding default value. +- - `map(K,V)` – 1 zero bit, i.e. `b{0}`. +- - `cell` – reference to an empty cell, i.e. `^EmptyCell`. +- - `address` – `addr_none$00` constructor, i.e. 2 zero bits. +- - `bytes` – reference to an empty cell, i.e. `^EmptyCell`. +- - `string` – reference to an empty cell, i.e. `^EmptyCell`. +- - `optional(T)` – 1 zero bit, i.e. `b{0}`. +- - `T[]` – `x{00000000} b{0}`, i.e. 33 zero bits. +- - `T[k]` – encoded as an array with `k` default values of type `T` +- - `ref(T)` – reference to a cell, cell is encoded as the default value of type `T`. + ## Version 2.3.130 - Revert tests @@ -106,4 +126,4 @@ Initial design of Application Binary Interface for TVM blockchain: - [Function signature concept](docs/ABI.md#function-signature-function-id) - Basic [types](docs/ABI.md#types-reference) and the rules of their encoding - Cell overflow handling -- [JSON interface](docs/ABI.md#abi-json) sturcture \ No newline at end of file +- [JSON interface](docs/ABI.md#abi-json) sturcture From 4507b23c2e98d5ad08670b0829e3c222f83a9dd4 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Fri, 28 Jul 2023 12:20:39 +0300 Subject: [PATCH 03/15] ABI v2.4 implemented --- src/contract.rs | 313 ++++++++++---- src/error.rs | 62 +-- src/event.rs | 20 +- src/function.rs | 168 +++++--- src/int.rs | 12 +- src/json_abi.rs | 68 +++- src/lib.rs | 33 +- src/param.rs | 57 ++- src/param_type/deserialize.rs | 122 +++--- src/param_type/mod.rs | 4 +- src/param_type/param_type.rs | 140 ++----- src/param_type/tests.rs | 128 +++--- src/tests/test_contract.rs | 4 +- src/tests/test_param.rs | 203 ++++++---- src/tests/v1/full_stack_tests.rs | 169 ++++---- src/tests/v1/test_contract.rs | 117 ++++-- src/tests/v2/full_stack_tests.rs | 384 +++++++++++------- src/tests/v2/test_contract.rs | 153 ++++--- src/token/deserialize.rs | 240 +++++++---- src/token/detokenizer.rs | 77 ++-- src/token/mod.rs | 238 +++++++++-- src/token/serialize.rs | 207 +++++++--- src/token/test_encoding.rs | 675 ++++++++++++++++++++++--------- src/token/tests.rs | 407 ++++++++++++------- src/token/tokenizer.rs | 19 +- 25 files changed, 2606 insertions(+), 1414 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index f356a459..03e26d7f 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -11,24 +11,28 @@ * limitations under the License. */ -use crate::{TokenValue, error::AbiError, event::Event, function::Function, param::Param, param_type::ParamType, token::Token}; -use std::fmt::Display; -use std::io; -use std::collections::HashMap; -use serde::de::{Error as SerdeError}; +use crate::param::SerdeParam; +use crate::{ + error::AbiError, event::Event, function::Function, param::Param, param_type::ParamType, + token::Token, TokenValue, +}; +use serde::de::Error as SerdeError; use serde_json; -use ton_block::{Serializable, MsgAddressInt}; -use ton_types::{BuilderData, error, fail, HashmapE, Result, SliceData}; - +use std::collections::HashMap; +use std::io; +use std::{collections::HashSet, fmt::Display}; +use ton_block::{MsgAddressInt, Serializable}; +use ton_types::{error, fail, BuilderData, HashmapE, Result, SliceData}; pub const MIN_SUPPORTED_VERSION: AbiVersion = ABI_VERSION_1_0; -pub const MAX_SUPPORTED_VERSION: AbiVersion = ABI_VERSION_2_3; +pub const MAX_SUPPORTED_VERSION: AbiVersion = ABI_VERSION_2_4; pub const ABI_VERSION_1_0: AbiVersion = AbiVersion::from_parts(1, 0); pub const ABI_VERSION_2_0: AbiVersion = AbiVersion::from_parts(2, 0); pub const ABI_VERSION_2_1: AbiVersion = AbiVersion::from_parts(2, 1); pub const ABI_VERSION_2_2: AbiVersion = AbiVersion::from_parts(2, 2); pub const ABI_VERSION_2_3: AbiVersion = AbiVersion::from_parts(2, 3); +pub const ABI_VERSION_2_4: AbiVersion = AbiVersion::from_parts(2, 4); #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] pub struct AbiVersion { @@ -40,13 +44,24 @@ impl AbiVersion { pub fn parse(str_version: &str) -> Result { let parts: Vec<&str> = str_version.split(".").collect(); if parts.len() < 2 { - fail!(AbiError::InvalidVersion(format!("version must consist of two parts divided by `.` ({})", str_version))); + fail!(AbiError::InvalidVersion(format!( + "version must consist of two parts divided by `.` ({})", + str_version + ))); } - let major = u8::from_str_radix(parts[0], 10) - .map_err(|err| error!(AbiError::InvalidVersion(format!("can not parse version string: {} ({})", err, str_version))))?; - let minor = u8::from_str_radix(parts[1], 10) - .map_err(|err| error!(AbiError::InvalidVersion(format!("can not parse version string: {} ({})", err, str_version))))?; + let major = u8::from_str_radix(parts[0], 10).map_err(|err| { + error!(AbiError::InvalidVersion(format!( + "can not parse version string: {} ({})", + err, str_version + ))) + })?; + let minor = u8::from_str_radix(parts[1], 10).map_err(|err| { + error!(AbiError::InvalidVersion(format!( + "can not parse version string: {} ({})", + err, str_version + ))) + })?; Ok(Self { major, minor }) } @@ -68,7 +83,10 @@ impl Display for AbiVersion { impl From for AbiVersion { fn from(value: u8) -> Self { - Self { major: value, minor: 0 } + Self { + major: value, + minor: 0, + } } } @@ -88,25 +106,35 @@ impl<'de> serde::de::Visitor<'de> for StringVisitor { formatter.write_str("String") } - fn visit_string(self, v: String) -> std::result::Result where E: serde::de::Error { + fn visit_string(self, v: String) -> std::result::Result + where + E: serde::de::Error, + { Ok(v) } - fn visit_str(self, v: &str) -> std::result::Result where E: serde::de::Error { + fn visit_str(self, v: &str) -> std::result::Result + where + E: serde::de::Error, + { Ok(v.to_string()) } } pub fn deserialize_opt_u32_from_string<'de, D>(d: D) -> std::result::Result, D::Error> - where D: serde::Deserializer<'de> +where + D: serde::Deserializer<'de>, { match d.deserialize_string(StringVisitor) { Err(_) => Ok(None), Ok(string) => { if !string.starts_with("0x") { - return Err(D::Error::custom(format!("Number parsing error: number must be prefixed with 0x ({})", string))); + return Err(D::Error::custom(format!( + "Number parsing error: number must be prefixed with 0x ({})", + string + ))); } - + u32::from_str_radix(&string[2..], 16) .map_err(|err| D::Error::custom(format!("Error parsing number: {}", err))) .map(|value| Some(value)) @@ -128,7 +156,7 @@ pub(crate) struct SerdeFunction { /// Calculated function ID #[serde(default)] #[serde(deserialize_with = "deserialize_opt_u32_from_string")] - pub id: Option + pub id: Option, } /// Contract event specification. @@ -141,7 +169,7 @@ pub(crate) struct SerdeEvent { pub inputs: Vec, #[serde(default)] #[serde(deserialize_with = "deserialize_opt_u32_from_string")] - pub id: Option + pub id: Option, } fn bool_true() -> bool { @@ -151,13 +179,13 @@ fn bool_true() -> bool { #[derive(Debug, Clone, PartialEq, Deserialize)] struct SerdeContract { /// ABI version up to 2. - #[serde(rename="ABI version")] + #[serde(rename = "ABI version")] pub abi_version: Option, /// ABI version. pub version: Option, /// Set timestamp in message. - #[serde(rename="setTime")] - #[serde(default="bool_true")] + #[serde(rename = "setTime")] + #[serde(default = "bool_true")] pub set_time: bool, /// Header parameters. #[serde(default)] @@ -172,7 +200,7 @@ struct SerdeContract { pub data: Vec, /// Contract storage fields. #[serde(default)] - pub fields: Vec, + pub fields: Vec, } pub struct DecodedMessage { @@ -195,12 +223,14 @@ pub struct Contract { data: HashMap, /// Contract storage fields. fields: Vec, + /// List of `fields` parameters with `init == true` + init_fields: HashSet, } impl Contract { /// Loads contract from json. pub fn load(reader: T) -> Result { - // A little trick similar to `Param` deserialization: first deserialize JSON into temporary + // A little trick similar to `Param` deserialization: first deserialize JSON into temporary // struct `SerdeContract` containing necessary fields and then repack fields into HashMap let mut serde_contract: SerdeContract = serde_json::from_reader(reader)?; @@ -209,26 +239,37 @@ impl Contract { } else if let Some(version) = serde_contract.abi_version { AbiVersion::from_parts(version, 0) } else { - fail!(AbiError::InvalidVersion("No version in ABI JSON".to_owned())); + fail!(AbiError::InvalidVersion( + "No version in ABI JSON".to_owned() + )); }; if !version.is_supported() { - fail!(AbiError::InvalidVersion(format!("Provided ABI version is not supported ({})", version))); + fail!(AbiError::InvalidVersion(format!( + "Provided ABI version is not supported ({})", + version + ))); } if version.major == 1 { if serde_contract.header.len() != 0 { return Err(AbiError::InvalidData { - msg: "Header parameters are not supported in ABI v1".into() - }.into()); + msg: "Header parameters are not supported in ABI v1".into(), + } + .into()); } if serde_contract.set_time { - serde_contract.header.push(Param { name: "time".into(), kind: ParamType::Time}); + serde_contract.header.push(Param { + name: "time".into(), + kind: ParamType::Time, + }); } } if !serde_contract.fields.is_empty() && version < ABI_VERSION_2_1 { - fail!(AbiError::InvalidData {msg: "Storage fields are supported since ABI v2.1".into()}); + fail!(AbiError::InvalidData { + msg: "Storage fields are supported since ABI v2.1".into() + }); } let mut result = Self { @@ -237,7 +278,8 @@ impl Contract { functions: HashMap::new(), events: HashMap::new(), data: HashMap::new(), - fields: serde_contract.fields, + fields: Vec::new(), + init_fields: HashSet::new(), }; for function in serde_contract.functions { @@ -245,12 +287,16 @@ impl Contract { Self::check_params_support(&version, function.outputs.iter())?; result.functions.insert( function.name.clone(), - Function::from_serde(version.clone(), function, result.header.clone())); + Function::from_serde(version.clone(), function, result.header.clone()), + ); } for event in serde_contract.events { Self::check_params_support(&version, event.inputs.iter())?; - result.events.insert(event.name.clone(), Event::from_serde(version.clone(), event)); + result.events.insert( + event.name.clone(), + Event::from_serde(version.clone(), event), + ); } Self::check_params_support(&version, serde_contract.data.iter().map(|val| &val.value))?; @@ -258,18 +304,29 @@ impl Contract { result.data.insert(data.value.name.clone(), data); } + for field in serde_contract.fields { + if field.init { + result.init_fields.insert(field.name.clone()); + } + result + .fields + .push(Param::from_serde(field).map_err(|err| AbiError::InvalidData { msg: err })?); + } + Ok(result) } fn check_params_support<'a, T>(abi_version: &AbiVersion, params: T) -> Result<()> - where - T: std::iter::Iterator + where + T: std::iter::Iterator, { for param in params { if !param.kind.is_supported(abi_version) { - return Err(AbiError::InvalidData { - msg: format!("Parameters of type {} are not supported in ABI v{}", param.kind, abi_version) - }.into()); + return Err(AbiError::NotSupported { + subject: format!("Parameter type {}", param.kind), + version: *abi_version, + } + .into()); } } Ok(()) @@ -277,24 +334,38 @@ impl Contract { /// Returns `Function` struct with provided function name. pub fn function(&self, name: &str) -> Result<&Function> { - self.functions.get(name).ok_or_else(|| AbiError::InvalidName { name: name.to_owned() }.into()) + self.functions.get(name).ok_or_else(|| { + AbiError::InvalidName { + name: name.to_owned(), + } + .into() + }) } /// Returns `Function` struct with provided function id. pub fn function_by_id(&self, id: u32, input: bool) -> Result<&Function> { for (_, func) in &self.functions { - let func_id = if input { func.get_input_id() } else { func.get_output_id() }; + let func_id = if input { + func.get_input_id() + } else { + func.get_output_id() + }; if func_id == id { return Ok(func); } } - Err(AbiError::InvalidFunctionId { id }.into()) + Err(AbiError::InvalidFunctionId { id }.into()) } /// Returns `Event` struct with provided function name. pub fn event(&self, name: &str) -> Result<&Event> { - self.events.get(name).ok_or_else(|| AbiError::InvalidName { name: name.to_owned() }.into()) + self.events.get(name).ok_or_else(|| { + AbiError::InvalidName { + name: name.to_owned(), + } + .into() + }) } /// Returns `Event` struct with provided function id. @@ -339,15 +410,20 @@ impl Contract { } /// Decodes contract answer and returns name of the function called - pub fn decode_output(&self, data: SliceData, internal: bool, allow_partial: bool) -> Result { + pub fn decode_output( + &self, + data: SliceData, + internal: bool, + allow_partial: bool, + ) -> Result { let original_data = data.clone(); - + let func_id = Function::decode_output_id(data)?; - if let Ok(func) = self.function_by_id(func_id, false){ + if let Ok(func) = self.function_by_id(func_id, false) { let tokens = func.decode_output(original_data, internal, allow_partial)?; - Ok( DecodedMessage { + Ok(DecodedMessage { function_name: func.name.clone(), tokens: tokens, }) @@ -355,7 +431,7 @@ impl Contract { let event = self.event_by_id(func_id)?; let tokens = event.decode_input(original_data, allow_partial)?; - Ok( DecodedMessage { + Ok(DecodedMessage { function_name: event.name.clone(), tokens: tokens, }) @@ -363,16 +439,21 @@ impl Contract { } /// Decodes contract answer and returns name of the function called - pub fn decode_input(&self, data: SliceData, internal: bool, allow_partial: bool) -> Result { + pub fn decode_input( + &self, + data: SliceData, + internal: bool, + allow_partial: bool, + ) -> Result { let original_data = data.clone(); - + let func_id = Function::decode_input_id(&self.abi_version, data, &self.header, internal)?; let func = self.function_by_id(func_id, true)?; let tokens = func.decode_input(original_data, internal, allow_partial)?; - Ok( DecodedMessage { + Ok(DecodedMessage { function_name: func.name.clone(), tokens, }) @@ -380,43 +461,60 @@ impl Contract { pub const DATA_MAP_KEYLEN: usize = 64; + pub fn data_map_supported_in_version(abi_version: &AbiVersion) -> bool { + abi_version < &ABI_VERSION_2_4 + } + + pub fn data_map_supported(&self) -> bool { + Self::data_map_supported_in_version(&self.abi_version) + } + + fn check_data_map_support(&self) -> Result<()> { + if !self.data_map_supported() { + return Err(AbiError::NotSupported { + subject: "Initial data dictionary".to_owned(), + version: self.abi_version, + } + .into()); + } + Ok(()) + } + /// Changes initial values for public contract variables pub fn update_data(&self, data: SliceData, tokens: &[Token]) -> Result { - let mut map = HashmapE::with_hashmap( - Self::DATA_MAP_KEYLEN, - data.reference_opt(0), - ); + self.check_data_map_support()?; + let mut map = HashmapE::with_hashmap(Self::DATA_MAP_KEYLEN, data.reference_opt(0)); for token in tokens { let builder = token.value.pack_into_chain(&self.abi_version)?; - let key = self.data + let key = self + .data .get(&token.name) - .ok_or_else(|| - AbiError::InvalidData { msg: format!("data item {} not found in contract ABI", token.name) } - )?.key; - - map.set_builder( - SliceData::load_builder(key.write_to_new_cell()?)?, - &builder, - )?; + .ok_or_else(|| AbiError::InvalidData { + msg: format!("data item {} not found in contract ABI", token.name), + })? + .key; + + map.set_builder(SliceData::load_builder(key.write_to_new_cell()?)?, &builder)?; } SliceData::load_cell(map.serialize()?) } /// Decode initial values of public contract variables pub fn decode_data(&self, data: SliceData, allow_partial: bool) -> Result> { - let map = HashmapE::with_hashmap( - Self::DATA_MAP_KEYLEN, - data.reference_opt(0), - ); + self.check_data_map_support()?; + let map = HashmapE::with_hashmap(Self::DATA_MAP_KEYLEN, data.reference_opt(0)); let mut tokens = vec![]; for (_, item) in &self.data { let key = SliceData::load_builder(item.key.write_to_new_cell()?)?; if let Some(value) = map.get(key)? { - tokens.append( - &mut TokenValue::decode_params(&vec![item.value.clone()], value, &self.abi_version, allow_partial)? - ); + tokens.append(&mut TokenValue::decode_params( + &vec![item.value.clone()], + value, + &self.abi_version, + allow_partial, + )?); } } @@ -425,10 +523,7 @@ impl Contract { // Gets public key from contract data pub fn get_pubkey(data: &SliceData) -> Result>> { - let map = HashmapE::with_hashmap( - Self::DATA_MAP_KEYLEN, - data.reference_opt(0), - ); + let map = HashmapE::with_hashmap(Self::DATA_MAP_KEYLEN, data.reference_opt(0)); map.get(SliceData::load_builder(0u64.write_to_new_cell()?)?) .map(|opt| opt.map(|slice| slice.get_bytestring(0))) } @@ -439,14 +534,8 @@ impl Contract { let pubkey_len = pubkey_vec.len() * 8; let value = BuilderData::with_raw(pubkey_vec, pubkey_len)?; - let mut map = HashmapE::with_hashmap( - Self::DATA_MAP_KEYLEN, - data.reference_opt(0) - ); - map.set_builder( - SliceData::load_builder(0u64.write_to_new_cell()?)?, - &value, - )?; + let mut map = HashmapE::with_hashmap(Self::DATA_MAP_KEYLEN, data.reference_opt(0)); + map.set_builder(SliceData::load_builder(0u64.write_to_new_cell()?)?, &value)?; SliceData::load_cell(map.serialize()?) } @@ -455,13 +544,54 @@ impl Contract { &self, signature: &[u8], public_key: Option<&[u8]>, - function_call: SliceData + function_call: SliceData, ) -> Result { Function::add_sign_to_encoded_input(&self.abi_version, signature, public_key, function_call) } + /// Encode account storage fields + pub fn encode_storage_fields( + &self, + mut init_fields: HashMap, + ) -> Result { + let mut tokens = vec![]; + for param in &self.fields { + let token = init_fields + .remove_entry(¶m.name) + .map(|(name, value)| Token { name, value }); + + if self.init_fields.contains(¶m.name) { + let token = token.ok_or_else(|| AbiError::InvalidInputData { + msg: format!( + "Storage field '{}' is marked as `init` and should be supplied", + param.name + ), + })?; + tokens.push(token); + } else { + if token.is_some() { + return Err(error!(AbiError::InvalidInputData { + msg: format!( + "Storage field '{}' is not marked as `init` and should not be supplied", + param.name + ) + })); + } + tokens.push(Token { + name: param.name.clone(), + value: TokenValue::default_value(¶m.kind), + }); + } + } + TokenValue::pack_values_into_chain(&tokens, vec![], &self.abi_version) + } + /// Decode account storage fields - pub fn decode_storage_fields(&self, data: SliceData, allow_partial: bool) -> Result> { + pub fn decode_storage_fields( + &self, + data: SliceData, + allow_partial: bool, + ) -> Result> { TokenValue::decode_params(&self.fields, data, &self.abi_version, allow_partial) } @@ -475,3 +605,12 @@ impl Contract { } } +#[cfg(test)] +#[path = "tests/test_contract.rs"] +mod tests_common; +#[cfg(test)] +#[path = "tests/v1/test_contract.rs"] +mod tests_v1; +#[cfg(test)] +#[path = "tests/v2/test_contract.rs"] +mod tests_v2; diff --git a/src/error.rs b/src/error.rs index 333ce4a5..58690092 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,50 +11,57 @@ * limitations under the License. */ +use crate::contract::AbiVersion; + #[derive(Debug, failure::Fail)] pub enum AbiError { - #[fail(display = "Invalid data: {}", msg)] - InvalidData { - msg: String + InvalidData { msg: String }, + + #[fail(display = "{} is not supported in ABI v{}", subject, version)] + NotSupported { + subject: String, + version: AbiVersion, }, #[fail(display = "Invalid name: {}", name)] - InvalidName { - name: String - }, + InvalidName { name: String }, #[fail(display = "Invalid function id: {:X}", id)] - InvalidFunctionId { - id: u32 - }, + InvalidFunctionId { id: u32 }, #[fail(display = "Deserialization error {}: {}", msg, cursor)] DeserializationError { msg: &'static str, - cursor: ton_types::SliceData + cursor: ton_types::SliceData, }, #[fail(display = "Not implemented")] NotImplemented, - #[fail(display = "Wrong parameters count. Expected: {}, provided: {}", expected, provided)] - WrongParametersCount { - expected: usize, - provided: usize - }, + #[fail( + display = "Wrong parameters count. Expected: {}, provided: {}", + expected, provided + )] + WrongParametersCount { expected: usize, provided: usize }, #[fail(display = "Token types do not match expected function parameter types")] WrongParameterType, - #[fail(display = "Wrong data format in `{}` parameter:\n{}\n{} expected", name, val, expected)] + #[fail( + display = "Wrong data format in `{}` parameter:\n{}\n{} expected", + name, val, expected + )] WrongDataFormat { val: serde_json::Value, name: String, expected: String, }, - #[fail(display = "Invalid parameter `{}` length, expected {}:\n{}", name, expected, val)] + #[fail( + display = "Invalid parameter `{}` length, expected {}:\n{}", + name, expected, val + )] InvalidParameterLength { name: String, val: serde_json::Value, @@ -72,30 +79,27 @@ pub enum AbiError { IncompleteDeserializationError, #[fail(display = "Invalid input data: {}", msg)] - InvalidInputData { - msg: String - }, + InvalidInputData { msg: String }, #[fail(display = "Invalid version: {}", 0)] InvalidVersion(String), #[fail(display = "Wrong function ID: {:x}", id)] - WrongId { - id: u32 - }, + WrongId { id: u32 }, #[fail(display = "Serde json error: {}", err)] - SerdeError { - err: serde_json::Error - }, + SerdeError { err: serde_json::Error }, #[fail(display = "Tuple description should contain non empty `components` field")] EmptyComponents, - #[fail(display = "Type description contains non empty `components` field but it is not a tuple")] + #[fail( + display = "Type description contains non empty `components` field but it is not a tuple" + )] UnusedComponents, - #[fail(display = "Message destination address is required to encode signed external inbound message body since ABI version 2.3")] + #[fail( + display = "Message destination address is required to encode signed external inbound message body since ABI version 2.3" + )] AddressRequired, } - diff --git a/src/event.rs b/src/event.rs index 72e5c592..bb2212a1 100644 --- a/src/event.rs +++ b/src/event.rs @@ -11,10 +11,10 @@ * limitations under the License. */ -use {Function, Param, Token, TokenValue}; +use crate::error::AbiError; use contract::{AbiVersion, SerdeEvent}; use ton_types::{Result, SliceData}; -use crate::error::AbiError; +use {Function, Param, Token, TokenValue}; /// Contract event specification. #[derive(Debug, Clone, PartialEq)] @@ -26,7 +26,7 @@ pub struct Event { /// Event input. pub inputs: Vec, /// Event ID - pub id: u32 + pub id: u32, } impl Event { @@ -36,7 +36,7 @@ impl Event { abi_version, name: serde_event.name, inputs: serde_event.inputs, - id: 0 + id: 0, }; event.id = if let Some(id) = serde_event.id { id @@ -48,9 +48,7 @@ impl Event { /// Returns all input params of given function. pub fn input_params(&self) -> Vec { - self.inputs.iter() - .map(|p| p.clone()) - .collect() + self.inputs.iter().map(|p| p.clone()).collect() } /// Returns true if function has input parameters, false in not @@ -60,7 +58,9 @@ impl Event { /// Retruns ABI function signature pub fn get_function_signature(&self) -> String { - let input_types = self.inputs.iter() + let input_types = self + .inputs + .iter() .map(|param| param.kind.type_signature()) .collect::>() .join(","); @@ -84,7 +84,9 @@ impl Event { pub fn decode_input(&self, mut data: SliceData, allow_partial: bool) -> Result> { let id = data.get_next_u32()?; - if id != self.get_id() { Err(AbiError::WrongId { id } )? } + if id != self.get_id() { + Err(AbiError::WrongId { id })? + } TokenValue::decode_params(&self.input_params(), data, &self.abi_version, allow_partial) } diff --git a/src/function.rs b/src/function.rs index e6772cf4..a6c9631a 100644 --- a/src/function.rs +++ b/src/function.rs @@ -13,15 +13,21 @@ //! Contract function call builder. -use crate::{contract::{ABI_VERSION_1_0, ABI_VERSION_2_3}, error::AbiError, param::Param, token::{SerializedValue, Token, TokenValue}, ParamType}; - -use std::collections::HashMap; -use sha2::{Digest, Sha256}; +use crate::{ + contract::{ABI_VERSION_1_0, ABI_VERSION_2_3}, + error::AbiError, + param::Param, + token::{SerializedValue, Token, TokenValue}, + ParamType, +}; + use contract::{AbiVersion, SerdeFunction}; use ed25519::signature::Signer; use ed25519_dalek::{Keypair, SIGNATURE_LENGTH}; -use ton_block::{Serializable, MsgAddressInt}; -use ton_types::{BuilderData, error, fail, IBitstring, Result, SliceData, MAX_DATA_BYTES, Cell}; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; +use ton_block::{MsgAddressInt, Serializable}; +use ton_types::{error, fail, BuilderData, Cell, IBitstring, Result, SliceData, MAX_DATA_BYTES}; /// Contract function specification. #[derive(Debug, Clone, PartialEq)] @@ -44,7 +50,11 @@ pub struct Function { impl Function { /// Creates `Function` struct from parsed JSON struct `SerdeFunction` - pub(crate) fn from_serde(abi_version: AbiVersion, serde_function: SerdeFunction, header: Vec) -> Self { + pub(crate) fn from_serde( + abi_version: AbiVersion, + serde_function: SerdeFunction, + header: Vec, + ) -> Self { let mut function = Function { abi_version, name: serde_function.name, @@ -52,7 +62,7 @@ impl Function { inputs: serde_function.inputs, outputs: serde_function.outputs, input_id: 0, - output_id: 0 + output_id: 0, }; if let Some(id) = serde_function.id { function.input_id = id; @@ -94,23 +104,36 @@ impl Function { pub fn get_function_signature(&self) -> String { let mut input_types = vec![]; if self.abi_version.major == 1 { - input_types.append(&mut self.header.iter() - .map(|param| param.kind.type_signature()) - .collect::>()) + input_types.append( + &mut self + .header + .iter() + .map(|param| param.kind.type_signature()) + .collect::>(), + ) } - input_types.append(&mut self.inputs.iter() - .map(|param| param.kind.type_signature()) - .collect::>()); - + input_types.append( + &mut self + .inputs + .iter() + .map(|param| param.kind.type_signature()) + .collect::>(), + ); + let input_types = input_types.join(","); - let output_types = self.outputs.iter() + let output_types = self + .outputs + .iter() .map(|param| param.kind.type_signature()) .collect::>() .join(","); - format!("{}({})({})v{}", self.name, input_types, output_types, self.abi_version.major) + format!( + "{}({})({})v{}", + self.name, input_types, output_types, self.abi_version.major + ) } pub fn calc_function_id(signature: &str) -> u32 { @@ -131,7 +154,7 @@ impl Function { Self::calc_function_id(&signature) } - /// Returns ID for call message + /// Returns ID for call message pub fn get_input_id(&self) -> u32 { self.input_id } @@ -142,19 +165,38 @@ impl Function { } /// Parses the ABI function output to list of tokens. - pub fn decode_output(&self, mut data: SliceData, internal: bool, allow_partial: bool) -> Result> { + pub fn decode_output( + &self, + mut data: SliceData, + internal: bool, + allow_partial: bool, + ) -> Result> { let id = data.get_next_u32()?; - if !internal && id != self.get_output_id() { Err(AbiError::WrongId { id } )? } + if !internal && id != self.get_output_id() { + Err(AbiError::WrongId { id })? + } TokenValue::decode_params(self.output_params(), data, &self.abi_version, allow_partial) } /// Parses the ABI function call to list of tokens. - pub fn decode_input(&self, data: SliceData, internal: bool, allow_partial: bool) -> Result> { + pub fn decode_input( + &self, + data: SliceData, + internal: bool, + allow_partial: bool, + ) -> Result> { let (_, id, cursor) = Self::decode_header(&self.abi_version, data, &self.header, internal)?; - if id != self.get_input_id() { Err(AbiError::WrongId { id } )? } + if id != self.get_input_id() { + Err(AbiError::WrongId { id })? + } - TokenValue::decode_params(self.input_params(), cursor, &self.abi_version, allow_partial) + TokenValue::decode_params( + self.input_params(), + cursor, + &self.abi_version, + allow_partial, + ) } /// Decodes function id from contract answer @@ -162,7 +204,7 @@ impl Function { abi_version: &AbiVersion, cursor: SliceData, header: &Vec, - internal: bool + internal: bool, ) -> Result { let (_, id, _) = Self::decode_header(abi_version, cursor, header, internal)?; Ok(id) @@ -182,7 +224,8 @@ impl Function { pair: Option<&Keypair>, address: Option, ) -> Result { - let (mut builder, hash) = self.create_unsigned_call(header, input, internal, pair.is_some(), address)?; + let (mut builder, hash) = + self.create_unsigned_call(header, input, internal, pair.is_some(), address)?; if !internal { builder = match pair { @@ -192,9 +235,10 @@ impl Function { &self.abi_version, Some(&signature), Some(&pair.public.to_bytes()), - builder)? - }, - None => Self::fill_sign(&self.abi_version, None, None, builder)? + builder, + )? + } + None => Self::fill_sign(&self.abi_version, None, None, builder)?, } } @@ -202,11 +246,7 @@ impl Function { } /// Encodes provided function return values into `BuilderData` - pub fn encode_internal_output( - &self, - answer_id: u32, - input: &[Token] - ) -> Result { + pub fn encode_internal_output(&self, answer_id: u32, input: &[Token]) -> Result { let mut vec = vec![]; vec.push(answer_id.write_to_new_cell()?.into()); let builder = TokenValue::pack_values_into_chain(input, vec, &self.abi_version)?; @@ -217,7 +257,7 @@ impl Function { fn encode_header( &self, header_tokens: &HashMap, - internal: bool + internal: bool, ) -> Result> { let mut vec = vec![]; if !internal { @@ -228,7 +268,10 @@ impl Function { } vec.append(&mut token.write_to_cells(&self.abi_version)?); } else { - vec.append(&mut TokenValue::get_default_value_for_header(¶m.kind)?.write_to_cells(&self.abi_version)?); + vec.append( + &mut TokenValue::get_default_value_for_header(¶m.kind)? + .write_to_cells(&self.abi_version)?, + ); } } } @@ -245,7 +288,7 @@ impl Function { abi_version: &AbiVersion, mut cursor: SliceData, header: &Vec, - internal: bool + internal: bool, ) -> Result<(Vec, u32, SliceData)> { let mut tokens = vec![]; let mut id = 0; @@ -263,10 +306,14 @@ impl Function { } for param in header { - let (token_value, new_cursor) = TokenValue::read_from(¶m.kind, cursor, false, abi_version, false)?; - + let (token_value, new_cursor) = + TokenValue::read_from(¶m.kind, cursor, false, abi_version, false)?; + cursor = new_cursor; - tokens.push(Token { name: param.name.clone(), value: token_value }); + tokens.push(Token { + name: param.name.clone(), + value: token_value, + }); } } if abi_version != &ABI_VERSION_1_0 { @@ -281,12 +328,16 @@ impl Function { address: Option, ) -> Result<(Vec, Vec)> { let signature = if abi_version == &ABI_VERSION_1_0 { - SliceData::load_cell(cursor.checked_drain_reference()?)?.get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH)? + SliceData::load_cell(cursor.checked_drain_reference()?)? + .get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH)? } else { if cursor.get_next_bit()? { cursor.get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH)? } else { - return Err(AbiError::InvalidData { msg: "No signature".to_owned() }.into()); + return Err(AbiError::InvalidData { + msg: "No signature".to_owned(), + } + .into()); } }; @@ -333,8 +384,11 @@ impl Function { // reserve in-cell data if reserve_sign { if self.abi_version >= ABI_VERSION_2_3 { - sign_builder.append_raw(&[0u8; MAX_DATA_BYTES], ParamType::Address.max_bit_size())?; - remove_bits = ParamType::Address.max_bit_size(); + sign_builder.append_raw( + &[0u8; MAX_DATA_BYTES], + TokenValue::max_bit_size(&ParamType::Address), + )?; + remove_bits = TokenValue::max_bit_size(&ParamType::Address); } else { sign_builder.append_bit_one()?; sign_builder.append_raw(&[0u8; SIGNATURE_LENGTH], SIGNATURE_LENGTH * 8)?; @@ -345,11 +399,18 @@ impl Function { remove_bits = 1; } } - cells.insert(0, SerializedValue { - data: sign_builder, - max_bits: if self.abi_version >= ABI_VERSION_2_3 { ParamType::Address.max_bit_size() } else { 1 + SIGNATURE_LENGTH * 8 }, - max_refs: if remove_ref { 1 } else { 0 } - }); + cells.insert( + 0, + SerializedValue { + data: sign_builder, + max_bits: if self.abi_version >= ABI_VERSION_2_3 { + TokenValue::max_bit_size(&ParamType::Address) + } else { + 1 + SIGNATURE_LENGTH * 8 + }, + max_refs: if remove_ref { 1 } else { 0 }, + }, + ); } // encoding itself @@ -384,20 +445,21 @@ impl Function { abi_version: &AbiVersion, signature: Option<&[u8]>, public_key: Option<&[u8]>, - mut builder: BuilderData + mut builder: BuilderData, ) -> Result { - if abi_version == &ABI_VERSION_1_0 { // sign in reference if builder.references_free() == 0 { - fail!(AbiError::InvalidInputData { msg: "No free reference for signature".to_owned() } ); + fail!(AbiError::InvalidInputData { + msg: "No free reference for signature".to_owned() + }); } let cell = if let Some(signature) = signature { let mut signature = signature.to_vec(); if let Some(public_key) = public_key { signature.extend_from_slice(public_key); } - + let len = signature.len() * 8; BuilderData::with_raw(signature, len)?.into_cell()? } else { @@ -425,7 +487,7 @@ impl Function { abi_version: &AbiVersion, signature: &[u8], public_key: Option<&[u8]>, - function_call: SliceData + function_call: SliceData, ) -> Result { let builder = function_call.as_builder(); diff --git a/src/int.rs b/src/int.rs index 960a84dc..c8a53b5b 100644 --- a/src/int.rs +++ b/src/int.rs @@ -25,16 +25,20 @@ pub struct Uint { pub size: usize, } - impl Int { pub fn new(number: i128, size: usize) -> Self { - Self { number: BigInt::from(number), size } + Self { + number: BigInt::from(number), + size, + } } } - impl Uint { pub fn new(number: u128, size: usize) -> Self { - Self { number: BigUint::from(number), size } + Self { + number: BigUint::from(number), + size, + } } } diff --git a/src/json_abi.rs b/src/json_abi.rs index 799dd099..5aaa769c 100644 --- a/src/json_abi.rs +++ b/src/json_abi.rs @@ -12,14 +12,16 @@ */ use crate::{ - error::AbiError, contract::Contract, token::{Detokenizer, Tokenizer, TokenValue} + contract::Contract, + error::AbiError, + token::{Detokenizer, TokenValue, Tokenizer}, }; use ed25519_dalek::Keypair; use serde_json::Value; -use ton_block::MsgAddressInt; use std::{collections::HashMap, str::FromStr}; -use ton_types::{Result, BuilderData, SliceData}; +use ton_block::MsgAddressInt; +use ton_types::{BuilderData, Result, SliceData}; /// Encodes `parameters` for given `function` of contract described by `abi` into `BuilderData` /// which can be used as message body for calling contract @@ -37,20 +39,25 @@ pub fn encode_function_call( let function = contract.function(&function)?; let mut header_tokens = if let Some(header) = header { - let v: Value = serde_json::from_str(&header).map_err(|err| AbiError::SerdeError { err } )?; - Tokenizer::tokenize_optional_params(function.header_params(), &v, &HashMap::new())? + let v: Value = serde_json::from_str(&header).map_err(|err| AbiError::SerdeError { err })?; + Tokenizer::tokenize_optional_params(function.header_params(), &v)? } else { HashMap::new() }; // add public key into header if pair.is_some() && header_tokens.get("pubkey").is_none() { - header_tokens.insert("pubkey".to_owned(), TokenValue::PublicKey(pair.map(|pair| pair.public))); + header_tokens.insert( + "pubkey".to_owned(), + TokenValue::PublicKey(pair.map(|pair| pair.public)), + ); } - let v: Value = serde_json::from_str(¶meters).map_err(|err| AbiError::SerdeError { err } )?; + let v: Value = serde_json::from_str(¶meters).map_err(|err| AbiError::SerdeError { err })?; let input_tokens = Tokenizer::tokenize_all_params(function.input_params(), &v)?; - let address = address.map(|string| MsgAddressInt::from_str(&string)).transpose()?; + let address = address + .map(|string| MsgAddressInt::from_str(&string)) + .transpose()?; function.encode_input(&header_tokens, &input_tokens, internal, pair, address) } @@ -70,16 +77,18 @@ pub fn prepare_function_call_for_sign( let function = contract.function(&function)?; let header_tokens = if let Some(header) = header { - let v: Value = serde_json::from_str(&header).map_err(|err| AbiError::SerdeError { err } )?; - Tokenizer::tokenize_optional_params(function.header_params(), &v, &HashMap::new())? + let v: Value = serde_json::from_str(&header).map_err(|err| AbiError::SerdeError { err })?; + Tokenizer::tokenize_optional_params(function.header_params(), &v)? } else { HashMap::new() }; - let v: Value = serde_json::from_str(¶meters).map_err(|err| AbiError::SerdeError { err } )?; + let v: Value = serde_json::from_str(¶meters).map_err(|err| AbiError::SerdeError { err })?; let input_tokens = Tokenizer::tokenize_all_params(function.input_params(), &v)?; - let address = address.map(|string| MsgAddressInt::from_str(&string)).transpose()?; + let address = address + .map(|string| MsgAddressInt::from_str(&string)) + .transpose()?; function.create_unsigned_call(&header_tokens, &input_tokens, false, true, address) } @@ -89,7 +98,7 @@ pub fn add_sign_to_function_call( abi: String, signature: &[u8], public_key: Option<&[u8]>, - function_call: SliceData + function_call: SliceData, ) -> Result { let contract = Contract::load(abi.as_bytes())?; contract.add_sign_to_encoded_input(signature, public_key, function_call) @@ -114,7 +123,7 @@ pub fn decode_function_response( pub struct DecodedMessage { pub function_name: String, - pub params: String + pub params: String, } /// Decodes output parameters returned by some function call. Returns parametes and function name @@ -132,7 +141,7 @@ pub fn decode_unknown_function_response( Ok(DecodedMessage { function_name: result.function_name, - params: output + params: output, }) } @@ -151,7 +160,7 @@ pub fn decode_unknown_function_call( Ok(DecodedMessage { function_name: result.function_name, - params: input + params: input, }) } @@ -195,7 +204,32 @@ pub fn get_signature_data( address: Option, ) -> Result<(Vec, Vec)> { let contract = Contract::load(abi.as_bytes())?; - let address = address.map(|string| MsgAddressInt::from_str(&string)).transpose()?; + let address = address + .map(|string| MsgAddressInt::from_str(&string)) + .transpose()?; contract.get_signature_data(cursor, address) } +/// Encodes `parameters` for given `function` of contract described by `abi` into `BuilderData` +/// which can be used as message body for calling contract +pub fn encode_storage_fields(abi: &str, init_fields: Option<&str>) -> Result { + let contract = Contract::load(abi.as_bytes())?; + + let init_fields = if let Some(init_fields) = init_fields { + let v: Value = + serde_json::from_str(&init_fields).map_err(|err| AbiError::SerdeError { err })?; + Tokenizer::tokenize_optional_params(&contract.fields(), &v)? + } else { + HashMap::new() + }; + + contract.encode_storage_fields(init_fields) +} + +#[cfg(test)] +#[path = "tests/v1/full_stack_tests.rs"] +mod tests_v1; + +#[cfg(test)] +#[path = "tests/v2/full_stack_tests.rs"] +mod tests_v2; diff --git a/src/lib.rs b/src/lib.rs index ab9addbe..dc0427e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,42 +11,47 @@ * limitations under the License. */ -extern crate sha2; -extern crate num_bigint; extern crate hex; -extern crate ton_block; -extern crate ton_types; +extern crate num_bigint; +extern crate sha2; +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; extern crate serde; extern crate serde_json; +extern crate ton_block; +extern crate ton_types; #[macro_use] extern crate serde_derive; -extern crate ed25519; -extern crate ed25519_dalek; extern crate base64; extern crate chrono; +extern crate ed25519; +extern crate ed25519_dalek; extern crate failure; extern crate num_traits; pub mod contract; -pub mod function; +pub mod error; pub mod event; +pub mod function; pub mod int; +pub mod json_abi; pub mod param; pub mod param_type; pub mod token; -pub mod json_abi; -pub mod error; -pub use param_type::ParamType; pub use contract::{Contract, DataItem}; -pub use token::{Token, TokenValue}; -pub use function::Function; +pub use error::*; pub use event::Event; +pub use function::Function; +pub use int::{Int, Uint}; pub use json_abi::*; pub use param::Param; -pub use int::{Int, Uint}; -pub use error::*; +pub use param_type::ParamType; +pub use token::{Token, TokenValue}; extern crate byteorder; +#[cfg(test)] +extern crate rand; include!("../common/src/info.rs"); diff --git a/src/param.rs b/src/param.rs index 04078463..b38946da 100644 --- a/src/param.rs +++ b/src/param.rs @@ -28,36 +28,56 @@ impl Param { pub fn new(name: &str, kind: ParamType) -> Self { Self { name: name.to_string(), - kind + kind, } } + + pub(crate) fn from_serde(serde_param: SerdeParam) -> Result { + let mut result = Self { + name: serde_param.name, + kind: serde_param.kind, + }; + + result + .kind + .set_components(serde_param.components) + .map_err(|err| err.to_string())?; + + Ok(result) + } } #[derive(Debug, Clone, PartialEq, Deserialize)] -struct SerdeParam { +pub(crate) struct SerdeParam { /// Param name. pub name: String, /// Param type. - #[serde(rename="type")] + #[serde(rename = "type")] pub kind: ParamType, /// Tuple components #[serde(default)] - pub components: Vec + pub components: Vec, + /// `init` flag for fields section + #[serde(default)] + pub init: bool, } impl<'a> Deserialize<'a> for Param { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { // A little trick: tuple parameters is described in JSON as addition field `components` - // but struct `Param` doesn't have such a field and tuple components is stored inside of + // but struct `Param` doesn't have such a field and tuple components is stored inside of // `ParamType::Tuple` enum. To use automated deserialization instead of manual parameters // recognizing we first deserialize parameter into temp struct `SerdeParam` and then - // if parameter is a tuple repack tuple components from `SerdeParam::components` + // if parameter is a tuple repack tuple components from `SerdeParam::components` // into `ParamType::Tuple` let value = serde_json::Value::deserialize(deserializer)?; if value.is_string() { let type_str = value.as_str().unwrap(); - let param_type: ParamType = serde_json::from_value(value.clone()) - .map_err(|err| D::Error::custom(err))?; + let param_type: ParamType = + serde_json::from_value(value.clone()).map_err(|err| D::Error::custom(err))?; match param_type { ParamType::Tuple(_) | ParamType::Array(_) | @@ -70,22 +90,17 @@ impl<'a> Deserialize<'a> for Param { } Ok(Self { name: type_str.to_owned(), - kind: param_type + kind: param_type, }) } else { - let serde_param: SerdeParam = serde_json::from_value(value).map_err(|err| D::Error::custom(err))?; - - let mut result = Self { - name: serde_param.name, - kind: serde_param.kind, - }; - - result.kind - .set_components(serde_param.components) - .map_err(D::Error::custom)?; + let serde_param: SerdeParam = + serde_json::from_value(value).map_err(|err| D::Error::custom(err))?; - Ok(result) + Self::from_serde(serde_param).map_err(D::Error::custom) } } } +#[cfg(test)] +#[path = "tests/test_param.rs"] +mod tests; diff --git a/src/param_type/deserialize.rs b/src/param_type/deserialize.rs index a5e97744..c67530f3 100644 --- a/src/param_type/deserialize.rs +++ b/src/param_type/deserialize.rs @@ -12,13 +12,16 @@ */ use crate::{error::AbiError, param_type::ParamType}; -use std::fmt; -use serde::{Deserialize, Deserializer}; use serde::de::{Error as SerdeError, Visitor}; +use serde::{Deserialize, Deserializer}; +use std::fmt; use ton_types::{error, fail, Result}; impl<'a> Deserialize<'a> for ParamType { - fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'a> { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'a>, + { deserializer.deserialize_identifier(ParamTypeVisitor) } } @@ -32,11 +35,17 @@ impl<'a> Visitor<'a> for ParamTypeVisitor { write!(formatter, "a correct name of abi-encodable parameter type") } - fn visit_str(self, value: &str) -> std::result::Result where E: SerdeError { + fn visit_str(self, value: &str) -> std::result::Result + where + E: SerdeError, + { read_type(value).map_err(|e| SerdeError::custom(e.to_string())) } - fn visit_string(self, value: String) -> std::result::Result where E: SerdeError { + fn visit_string(self, value: String) -> std::result::Result + where + E: SerdeError, + { self.visit_str(value.as_str()) } } @@ -46,7 +55,8 @@ pub fn read_type(name: &str) -> Result { // check if it is a fixed or dynamic array. if let Some(']') = name.chars().last() { // take number part - let num: String = name.chars() + let num: String = name + .chars() .rev() .skip(1) .take_while(|c| *c != '[') @@ -62,9 +72,10 @@ pub fn read_type(name: &str) -> Result { return Ok(ParamType::Array(Box::new(subtype))); } else { // it's a fixed array. - let len = usize::from_str_radix(&num, 10) - .map_err(|_| AbiError::InvalidName { name: name.to_owned() } )?; - + let len = usize::from_str_radix(&num, 10).map_err(|_| AbiError::InvalidName { + name: name.to_owned(), + })?; + let subtype = read_type(&name[..count - num.len() - 2])?; return Ok(ParamType::FixedArray(Box::new(subtype), len)); } @@ -72,86 +83,79 @@ pub fn read_type(name: &str) -> Result { let result = match name { "bool" => ParamType::Bool, - // a little trick - here we only recognize parameter as a tuple and fill it + // a little trick - here we only recognize parameter as a tuple and fill it // with parameters in `Param` type deserialization "tuple" => ParamType::Tuple(Vec::new()), s if s.starts_with("int") => { - let len = usize::from_str_radix(&s[3..], 10) - .map_err(|_| AbiError::InvalidName { name: name.to_owned() } )?; + let len = usize::from_str_radix(&s[3..], 10).map_err(|_| AbiError::InvalidName { + name: name.to_owned(), + })?; ParamType::Int(len) - }, + } s if s.starts_with("uint") => { - let len = usize::from_str_radix(&s[4..], 10) - .map_err(|_| AbiError::InvalidName { name: name.to_owned() } )?; + let len = usize::from_str_radix(&s[4..], 10).map_err(|_| AbiError::InvalidName { + name: name.to_owned(), + })?; ParamType::Uint(len) - }, + } s if s.starts_with("varint") => { - let len = usize::from_str_radix(&s[6..], 10) - .map_err(|_| AbiError::InvalidName { name: name.to_owned() } )?; + let len = usize::from_str_radix(&s[6..], 10).map_err(|_| AbiError::InvalidName { + name: name.to_owned(), + })?; ParamType::VarInt(len) - }, + } s if s.starts_with("varuint") => { - let len = usize::from_str_radix(&s[7..], 10) - .map_err(|_| AbiError::InvalidName { name: name.to_owned() } )?; + let len = usize::from_str_radix(&s[7..], 10).map_err(|_| AbiError::InvalidName { + name: name.to_owned(), + })?; ParamType::VarUint(len) - }, + } s if s.starts_with("map(") && s.ends_with(")") => { let types: Vec<&str> = name[4..name.len() - 1].splitn(2, ",").collect(); if types.len() != 2 { - fail!(AbiError::InvalidName { name: name.to_owned() } ); + fail!(AbiError::InvalidName { + name: name.to_owned() + }); } let key_type = read_type(types[0])?; let value_type = read_type(types[1])?; - match key_type - { - ParamType::Int(_) | ParamType::Uint(_) | ParamType::Address => - ParamType::Map(Box::new(key_type), Box::new(value_type)), - _ => fail!(AbiError::InvalidName { - name: "Only integer and std address values can be map keys".to_owned() - }), + match key_type { + ParamType::Int(_) | ParamType::Uint(_) | ParamType::Address => { + ParamType::Map(Box::new(key_type), Box::new(value_type)) + } + _ => fail!(AbiError::InvalidName { + name: "Only integer and std address values can be map keys".to_owned() + }), } - }, - "cell" => { - ParamType::Cell - } - "address" => { - ParamType::Address - } - "token" => { - ParamType::Token - } - "bytes" => { - ParamType::Bytes } + "cell" => ParamType::Cell, + "address" => ParamType::Address, + "token" => ParamType::Token, + "bytes" => ParamType::Bytes, s if s.starts_with("fixedbytes") => { - let len = usize::from_str_radix(&s[10..], 10) - .map_err(|_| AbiError::InvalidName { name: name.to_owned() } )?; + let len = usize::from_str_radix(&s[10..], 10).map_err(|_| AbiError::InvalidName { + name: name.to_owned(), + })?; ParamType::FixedBytes(len) } - "time" => { - ParamType::Time - } - "expire" => { - ParamType::Expire - } - "pubkey" => { - ParamType::PublicKey - } - "string" => { - ParamType::String - } + "time" => ParamType::Time, + "expire" => ParamType::Expire, + "pubkey" => ParamType::PublicKey, + "string" => ParamType::String, s if s.starts_with("optional(") && s.ends_with(")") => { let inner_type = read_type(&name[9..name.len() - 1])?; ParamType::Optional(Box::new(inner_type)) - }, + } s if s.starts_with("ref(") && s.ends_with(")") => { let inner_type = read_type(&name[4..name.len() - 1])?; ParamType::Ref(Box::new(inner_type)) - }, + } _ => { - fail!(AbiError::InvalidName { name: name.to_owned() } ); + fail!(AbiError::InvalidName { + name: name.to_owned() + }); } }; diff --git a/src/param_type/mod.rs b/src/param_type/mod.rs index 328b4e73..283e2348 100644 --- a/src/param_type/mod.rs +++ b/src/param_type/mod.rs @@ -16,6 +16,8 @@ mod deserialize; mod param_type; -pub use self::param_type::ParamType; pub use self::deserialize::read_type; +pub use self::param_type::ParamType; +#[cfg(test)] +mod tests; diff --git a/src/param_type/param_type.rs b/src/param_type/param_type.rs index 55f7d0f5..86949e0b 100644 --- a/src/param_type/param_type.rs +++ b/src/param_type/param_type.rs @@ -13,13 +13,12 @@ //! Function and event param types. +use crate::contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_1, ABI_VERSION_2_4}; +use crate::{contract::ABI_VERSION_2_0, AbiError}; use std::fmt; use Param; -use crate::{AbiError, contract::ABI_VERSION_2_0}; -use crate::contract::{ABI_VERSION_1_0, ABI_VERSION_2_1, AbiVersion}; - -use ton_types::{BuilderData, Result, error}; +use ton_types::{error, Result}; /// Function and event param types. #[derive(Debug, Clone, PartialEq, Eq)] @@ -89,13 +88,17 @@ impl ParamType { } signature.replace_range(..1, "("); signature + ")" - }, + } ParamType::Array(ref param_type) => format!("{}[]", param_type.type_signature()), - ParamType::FixedArray(ref param_type, size) => - format!("{}[{}]", param_type.type_signature(), size), + ParamType::FixedArray(ref param_type, size) => { + format!("{}[{}]", param_type.type_signature(), size) + } ParamType::Cell => "cell".to_owned(), - ParamType::Map(key_type, value_type) => - format!("map({},{})", key_type.type_signature(), value_type.type_signature()), + ParamType::Map(key_type, value_type) => format!( + "map({},{})", + key_type.type_signature(), + value_type.type_signature() + ), ParamType::Address => format!("address"), ParamType::Bytes => format!("bytes"), ParamType::FixedBytes(size) => format!("fixedbytes{}", size), @@ -104,7 +107,9 @@ impl ParamType { ParamType::Time => format!("time"), ParamType::Expire => format!("expire"), ParamType::PublicKey => format!("pubkey"), - ParamType::Optional(ref param_type) => format!("optional({})", param_type.type_signature()), + ParamType::Optional(ref param_type) => { + format!("optional({})", param_type.type_signature()) + } ParamType::Ref(ref param_type) => format!("ref({})", param_type.type_signature()), } } @@ -117,119 +122,34 @@ impl ParamType { } else { Ok(*params = components) } - } - ParamType::Array(array_type) => { - array_type.set_components(components) - } - ParamType::FixedArray(array_type, _) => { - array_type.set_components(components) } - ParamType::Map(_, value_type) => { - value_type.set_components(components) - } - ParamType::Optional(inner_type) => { - inner_type.set_components(components) - } - ParamType::Ref(inner_type) => { - inner_type.set_components(components) - } - _ => { + ParamType::Array(array_type) => array_type.set_components(components), + ParamType::FixedArray(array_type, _) => array_type.set_components(components), + ParamType::Map(_, value_type) => value_type.set_components(components), + ParamType::Optional(inner_type) => inner_type.set_components(components), + ParamType::Ref(inner_type) => inner_type.set_components(components), + _ => { if components.len() != 0 { Err(error!(AbiError::UnusedComponents)) } else { Ok(()) } - }, + } } } /// Check if parameter type is supoorted in particular ABI version pub fn is_supported(&self, abi_version: &AbiVersion) -> bool { match self { - ParamType::Time | ParamType::Expire | ParamType::PublicKey => abi_version >= &ABI_VERSION_2_0, - ParamType::String | ParamType::Optional(_)| ParamType::VarInt(_) | ParamType::VarUint(_) => abi_version >= &ABI_VERSION_2_1, - ParamType::Ref(_) => false, - _ => abi_version >= &ABI_VERSION_1_0, - } - } - - pub fn get_map_key_size(&self) -> Result { - match self { - ParamType::Int(size) | ParamType::Uint(size) => Ok(*size), - ParamType::Address => Ok(crate::token::STD_ADDRESS_BIT_LENGTH), - _ => Err(error!(AbiError::InvalidData { - msg: "Only integer and std address values can be map keys".to_owned() - })) - } - } - - pub(crate) fn varint_size_len(size: usize) -> usize { - 8 - ((size - 1) as u8).leading_zeros() as usize - } - - pub(crate) fn is_large_optional(&self) -> bool { - self.max_bit_size() >= BuilderData::bits_capacity() || - self.max_refs_count() >= BuilderData::references_capacity() - } - - pub(crate) fn max_refs_count(&self) -> usize { - match self { - // in-cell serialized types - ParamType::Uint(_) | ParamType::Int(_) | ParamType::VarUint(_) |ParamType::VarInt(_) - | ParamType::Bool | ParamType::Address | ParamType::Token | ParamType::Time - | ParamType::Expire |ParamType::PublicKey => 0, - // reference serialized types - ParamType::Array(_) | ParamType::FixedArray(_, _) | ParamType::Cell | ParamType::String - | ParamType::Map(_, _) | ParamType::Bytes | ParamType::FixedBytes(_) - | ParamType::Ref(_) => 1, - // tuple refs is sum of inner types refs - ParamType::Tuple(params) => { - params - .iter() - .fold(0, |acc, param| acc + param.kind.max_refs_count()) - }, - // large optional is serialized into reference - ParamType::Optional(param_type) => { - if param_type.is_large_optional() { - 1 - } else { - param_type.max_refs_count() - } - }, - } - } - - pub(crate) fn max_bit_size(&self) -> usize { - match self { - ParamType::Uint(size) => *size, - ParamType::Int(size) => *size, - ParamType::VarUint(size) => Self::varint_size_len(*size) + (size - 1) * 8, - ParamType::VarInt(size) => Self::varint_size_len(*size) + (size - 1) * 8, - ParamType::Bool => 1, - ParamType::Array(_) => 33, - ParamType::FixedArray(_, _) => 1, - ParamType::Cell => 0, - ParamType::Map(_, _) => 1, - ParamType::Address => 591, - ParamType::Bytes | ParamType::FixedBytes(_) => 0, - ParamType::String => 0, - ParamType::Token => 124, - ParamType::Time => 64, - ParamType::Expire => 32, - ParamType::PublicKey => 257, - ParamType::Ref(_) => 0, - ParamType::Tuple(params) => { - params - .iter() - .fold(0, |acc, param| acc + param.kind.max_bit_size()) - }, - ParamType::Optional(param_type) => { - if param_type.is_large_optional() { - 1 - } else { - 1 + param_type.max_bit_size() - } + ParamType::Time | ParamType::Expire | ParamType::PublicKey => { + abi_version >= &ABI_VERSION_2_0 } + ParamType::String + | ParamType::Optional(_) + | ParamType::VarInt(_) + | ParamType::VarUint(_) => abi_version >= &ABI_VERSION_2_1, + ParamType::Ref(_) => abi_version >= &ABI_VERSION_2_4, + _ => abi_version >= &ABI_VERSION_1_0, } } } diff --git a/src/param_type/tests.rs b/src/param_type/tests.rs index 6de0bbf8..afced949 100644 --- a/src/param_type/tests.rs +++ b/src/param_type/tests.rs @@ -12,8 +12,8 @@ */ mod param_type_tests { - use ParamType; use Param; + use ParamType; #[test] fn test_param_type_signature() { @@ -23,53 +23,81 @@ mod param_type_tests { assert_eq!( ParamType::Array(Box::new(ParamType::Cell)).type_signature(), - "cell[]".to_owned()); + "cell[]".to_owned() + ); assert_eq!( ParamType::FixedArray(Box::new(ParamType::Int(33)), 2).type_signature(), - "int33[2]".to_owned()); + "int33[2]".to_owned() + ); assert_eq!( ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bytes))), 2) .type_signature(), - "bytes[][2]".to_owned()); + "bytes[][2]".to_owned() + ); let mut tuple_params = vec![]; - tuple_params.push(Param {name: "a".to_owned(), kind: ParamType::Uint(123)}); - tuple_params.push(Param {name: "b".to_owned(), kind: ParamType::Int(8)}); + tuple_params.push(Param { + name: "a".to_owned(), + kind: ParamType::Uint(123), + }); + tuple_params.push(Param { + name: "b".to_owned(), + kind: ParamType::Int(8), + }); let tuple_with_tuple = vec![ - Param {name: "a".to_owned(), kind: ParamType::Tuple(tuple_params.clone())}, - Param {name: "b".to_owned(), kind: ParamType::Token} + Param { + name: "a".to_owned(), + kind: ParamType::Tuple(tuple_params.clone()), + }, + Param { + name: "b".to_owned(), + kind: ParamType::Token, + }, ]; assert_eq!( ParamType::Tuple(tuple_params.clone()).type_signature(), - "(uint123,int8)".to_owned()); + "(uint123,int8)".to_owned() + ); assert_eq!( ParamType::Array(Box::new(ParamType::Tuple(tuple_with_tuple))).type_signature(), - "((uint123,int8),gram)[]".to_owned()); + "((uint123,int8),gram)[]".to_owned() + ); assert_eq!( ParamType::FixedArray(Box::new(ParamType::Tuple(tuple_params)), 4).type_signature(), - "(uint123,int8)[4]".to_owned()); + "(uint123,int8)[4]".to_owned() + ); assert_eq!( - ParamType::Map(Box::new(ParamType::Int(456)), Box::new(ParamType::Address)).type_signature(), - "map(int456,address)".to_owned()); + ParamType::Map(Box::new(ParamType::Int(456)), Box::new(ParamType::Address)) + .type_signature(), + "map(int456,address)".to_owned() + ); assert_eq!(ParamType::String.type_signature(), "string".to_owned()); - assert_eq!(ParamType::VarUint(16).type_signature(), "varuint16".to_owned()); - assert_eq!(ParamType::VarInt(32).type_signature(), "varint32".to_owned()); + assert_eq!( + ParamType::VarUint(16).type_signature(), + "varuint16".to_owned() + ); + assert_eq!( + ParamType::VarInt(32).type_signature(), + "varint32".to_owned() + ); assert_eq!( ParamType::Optional(Box::new(ParamType::Int(123))).type_signature(), - "optional(int123)".to_owned()); + "optional(int123)".to_owned() + ); assert_eq!( ParamType::Ref(Box::new(ParamType::Uint(123))).type_signature(), - "ref(uint123)".to_owned()); + "ref(uint123)".to_owned() + ); } } @@ -84,36 +112,40 @@ mod deserialize_tests { "address", "bytes", "fixedbytes32", "token", "time", "expire", "pubkey", "string", "varuint16", "varint32", "optional(bytes)", "ref(bool)"]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![ - ParamType::Uint(256), - ParamType::Int(64), - ParamType::Bool, - ParamType::Array(Box::new(ParamType::Bool)), - ParamType::FixedArray(Box::new(ParamType::Int(33)), 2), - ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2), - ParamType::Tuple(vec![]), - ParamType::Array(Box::new(ParamType::Tuple(vec![]))), - ParamType::FixedArray(Box::new(ParamType::Tuple(vec![])), 4), - ParamType::Cell, - ParamType::Map(Box::new(ParamType::Int(3)), Box::new(ParamType::Bool)), - ParamType::Map( - Box::new(ParamType::Uint(1023)), - Box::new(ParamType::FixedArray( - Box::new(ParamType::Array( - Box::new(ParamType::Tuple(vec![])))), - 5))), - ParamType::Address, - ParamType::Bytes, - ParamType::FixedBytes(32), - ParamType::Token, - ParamType::Time, - ParamType::Expire, - ParamType::PublicKey, - ParamType::String, - ParamType::VarUint(16), - ParamType::VarInt(32), - ParamType::Optional(Box::new(ParamType::Bytes)), - ParamType::Ref(Box::new(ParamType::Bool)), - ]); + assert_eq!( + deserialized, + vec![ + ParamType::Uint(256), + ParamType::Int(64), + ParamType::Bool, + ParamType::Array(Box::new(ParamType::Bool)), + ParamType::FixedArray(Box::new(ParamType::Int(33)), 2), + ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2), + ParamType::Tuple(vec![]), + ParamType::Array(Box::new(ParamType::Tuple(vec![]))), + ParamType::FixedArray(Box::new(ParamType::Tuple(vec![])), 4), + ParamType::Cell, + ParamType::Map(Box::new(ParamType::Int(3)), Box::new(ParamType::Bool)), + ParamType::Map( + Box::new(ParamType::Uint(1023)), + Box::new(ParamType::FixedArray( + Box::new(ParamType::Array(Box::new(ParamType::Tuple(vec![])))), + 5 + )) + ), + ParamType::Address, + ParamType::Bytes, + ParamType::FixedBytes(32), + ParamType::Token, + ParamType::Time, + ParamType::Expire, + ParamType::PublicKey, + ParamType::String, + ParamType::VarUint(16), + ParamType::VarInt(32), + ParamType::Optional(Box::new(ParamType::Bytes)), + ParamType::Ref(Box::new(ParamType::Bool)), + ] + ); } } diff --git a/src/tests/test_contract.rs b/src/tests/test_contract.rs index 2d8ea8f2..edf16b6a 100644 --- a/src/tests/test_contract.rs +++ b/src/tests/test_contract.rs @@ -18,8 +18,8 @@ use Contract; const DEPOOL_TVC: &[u8] = include_bytes!("data/DePool.tvc"); const PUB_KEY: [u8; ed25519_dalek::PUBLIC_KEY_LENGTH] = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, ]; #[test] diff --git a/src/tests/test_param.rs b/src/tests/test_param.rs index 426b9abf..eedd34e2 100644 --- a/src/tests/test_param.rs +++ b/src/tests/test_param.rs @@ -14,28 +14,26 @@ use token::Detokenizer; use Int; +use ton_types::BuilderData; +use ton_types::IBitstring; +use Function; use Token; use {Param, ParamType}; use {TokenValue, Uint}; -use Function; -use ton_types::BuilderData; -use ton_types::IBitstring; use crate::contract::ABI_VERSION_2_0; #[test] fn int_json_representation() { - let value = Detokenizer::detokenize_to_json_value( - &[ - Token::new("u8", TokenValue::Uint(Uint::new(1, 8))), - Token::new("i32", TokenValue::Int(Int::new(-1, 32))), - Token::new("u256", TokenValue::Uint(Uint::new(1, 256))), - Token::new("u128", TokenValue::Uint(Uint::new(1, 128))), - Token::new("i256", TokenValue::Int(Int::new(-1, 256))), - Token::new("vi16", TokenValue::VarInt(16, (-1i32).into())), - Token::new("vu32", TokenValue::VarUint(32, 1u32.into())), - ], - ) + let value = Detokenizer::detokenize_to_json_value(&[ + Token::new("u8", TokenValue::Uint(Uint::new(1, 8))), + Token::new("i32", TokenValue::Int(Int::new(-1, 32))), + Token::new("u256", TokenValue::Uint(Uint::new(1, 256))), + Token::new("u128", TokenValue::Uint(Uint::new(1, 128))), + Token::new("i256", TokenValue::Int(Int::new(-1, 256))), + Token::new("vi16", TokenValue::VarInt(16, (-1i32).into())), + Token::new("vu32", TokenValue::VarUint(32, 1u32.into())), + ]) .unwrap(); assert_eq!( value, @@ -63,29 +61,39 @@ fn test_encode_internal_output() { output_id: 0, }; - let tokens = - [ - Token::new("u8", TokenValue::Uint(Uint::new(1, 8))), - Token::new("i32", TokenValue::Int(Int::new(-1, 32))), - Token::new("u256", TokenValue::Uint(Uint::new(1, 256))), - Token::new("u128", TokenValue::Uint(Uint::new(1, 128))), - Token::new("i256", TokenValue::Int(Int::new(-1, 256))), - ]; + let tokens = [ + Token::new("u8", TokenValue::Uint(Uint::new(1, 8))), + Token::new("i32", TokenValue::Int(Int::new(-1, 32))), + Token::new("u256", TokenValue::Uint(Uint::new(1, 256))), + Token::new("u128", TokenValue::Uint(Uint::new(1, 128))), + Token::new("i256", TokenValue::Int(Int::new(-1, 256))), + ]; let test_tree = func.encode_internal_output(1u32 << 31, &tokens).unwrap(); let mut expected_tree = BuilderData::new(); - expected_tree.append_u32(1u32 << 31).unwrap(); // answer_id + expected_tree.append_u32(1u32 << 31).unwrap(); // answer_id expected_tree.append_u8(1).unwrap(); expected_tree.append_i32(-1).unwrap(); - expected_tree.append_raw( - &hex::decode("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - 32 * 8).unwrap(); - expected_tree.append_raw( - &hex::decode("00000000000000000000000000000001").unwrap(), - 16 * 8).unwrap(); - expected_tree.append_raw( - &hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(), - 32 * 8).unwrap(); + expected_tree + .append_raw( + &hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(), + 32 * 8, + ) + .unwrap(); + expected_tree + .append_raw( + &hex::decode("00000000000000000000000000000001").unwrap(), + 16 * 8, + ) + .unwrap(); + expected_tree + .append_raw( + &hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap(), + 32 * 8, + ) + .unwrap(); assert_eq!(test_tree, expected_tree); } @@ -98,10 +106,13 @@ fn test_simple_param_deserialization() { let deserialized: Param = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Param { - name: "a".to_owned(), - kind: ParamType::Int(9), - }); + assert_eq!( + deserialized, + Param { + name: "a".to_owned(), + kind: ParamType::Int(9), + } + ); } #[test] @@ -123,13 +134,22 @@ fn test_tuple_param_deserialization() { let deserialized: Param = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Param { - name: "a".to_owned(), - kind: ParamType::Tuple(vec![ - Param { name: "a".to_owned(), kind: ParamType::Int(8) }, - Param { name: "b".to_owned(), kind: ParamType::Int(8) }, - ]), - }); + assert_eq!( + deserialized, + Param { + name: "a".to_owned(), + kind: ParamType::Tuple(vec![ + Param { + name: "a".to_owned(), + kind: ParamType::Int(8) + }, + Param { + name: "b".to_owned(), + kind: ParamType::Int(8) + }, + ]), + } + ); } #[test] @@ -161,25 +181,34 @@ fn test_tuples_array_deserialization() { let deserialized: Param = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Param { - name: "a".to_owned(), - kind: ParamType::Array(Box::new(ParamType::Tuple(vec![ - Param { - name: "a".to_owned(), - kind: ParamType::Bool - }, - Param { - name: "b".to_owned(), - kind: ParamType::FixedArray( - Box::new(ParamType::Tuple(vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(8) }, - Param { name: "b".to_owned(), kind: ParamType::Int(15) }, - ])), - 5 - ) - }, - ]))), - }); + assert_eq!( + deserialized, + Param { + name: "a".to_owned(), + kind: ParamType::Array(Box::new(ParamType::Tuple(vec![ + Param { + name: "a".to_owned(), + kind: ParamType::Bool + }, + Param { + name: "b".to_owned(), + kind: ParamType::FixedArray( + Box::new(ParamType::Tuple(vec![ + Param { + name: "a".to_owned(), + kind: ParamType::Uint(8) + }, + Param { + name: "b".to_owned(), + kind: ParamType::Int(15) + }, + ])), + 5 + ) + }, + ]))), + } + ); } #[test] @@ -201,24 +230,31 @@ fn test_tuples_array_map_map() { let deserialized: Param = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Param { - name: "d".to_owned(), - kind: ParamType::Map( + assert_eq!( + deserialized, + Param { + name: "d".to_owned(), + kind: ParamType::Map( Box::new(ParamType::Uint(32)), Box::new(ParamType::Map( Box::new(ParamType::Uint(32)), Box::new(ParamType::FixedArray( - Box::new(ParamType::Array( - Box::new(ParamType::Tuple(vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(256) }, - Param { name: "b".to_owned(), kind: ParamType::Uint(256) }, - ])) - )), + Box::new(ParamType::Array(Box::new(ParamType::Tuple(vec![ + Param { + name: "a".to_owned(), + kind: ParamType::Uint(256) + }, + Param { + name: "b".to_owned(), + kind: ParamType::Uint(256) + }, + ])))), 5 )), )) ), - }); + } + ); } #[test] @@ -249,11 +285,20 @@ fn test_optional_tuple_param_deserialization() { let deserialized: Param = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Param { - name: "a".to_owned(), - kind: ParamType::Optional(Box::new(ParamType::Tuple(vec![ - Param { name: "a".to_owned(), kind: ParamType::Int(8) }, - Param { name: "b".to_owned(), kind: ParamType::Int(8) }, - ]))), - }); + assert_eq!( + deserialized, + Param { + name: "a".to_owned(), + kind: ParamType::Optional(Box::new(ParamType::Tuple(vec![ + Param { + name: "a".to_owned(), + kind: ParamType::Int(8) + }, + Param { + name: "b".to_owned(), + kind: ParamType::Int(8) + }, + ]))), + } + ); } diff --git a/src/tests/v1/full_stack_tests.rs b/src/tests/v1/full_stack_tests.rs index 17c712c6..5818cc13 100644 --- a/src/tests/v1/full_stack_tests.rs +++ b/src/tests/v1/full_stack_tests.rs @@ -13,9 +13,9 @@ use ed25519::signature::{Signature, Signer}; -use ton_types::{BuilderData, SliceData}; -use ton_types::dictionary::HashmapE; use ton_block::{MsgAddressInt, Serializable}; +use ton_types::dictionary::HashmapE; +use ton_types::{BuilderData, SliceData}; use json_abi::*; @@ -145,40 +145,35 @@ fn test_constructor_call() { false, None, None, - ).unwrap(); + ) + .unwrap(); - let mut expected_tree = BuilderData::with_bitstring(vec![0x54, 0xc1, 0xf4, 0x0f, 0x80]).unwrap(); - expected_tree.checked_prepend_reference(Default::default()).unwrap(); + let mut expected_tree = + BuilderData::with_bitstring(vec![0x54, 0xc1, 0xf4, 0x0f, 0x80]).unwrap(); + expected_tree + .checked_prepend_reference(Default::default()) + .unwrap(); let test_tree = SliceData::load_builder(test_tree).unwrap(); let expected_tree = SliceData::load_builder(expected_tree).unwrap(); assert_eq!(test_tree, expected_tree); - let response = decode_unknown_function_call( - WALLET_ABI.to_owned(), - test_tree.clone(), - false, - false, - ).unwrap(); + let response = + decode_unknown_function_call(WALLET_ABI.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!(response.params, params); assert_eq!(response.function_name, "constructor"); - let test_tree = SliceData::from_raw(vec![0xd4, 0xc1, 0xf4, 0x0f, 0x80], 32); - let response = decode_unknown_function_response( - WALLET_ABI.to_owned(), - test_tree.clone(), - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_response(WALLET_ABI.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!(response.params, params); assert_eq!(response.function_name, "constructor"); - let response = decode_function_response( WALLET_ABI.to_owned(), "constructor".to_owned(), @@ -217,17 +212,14 @@ fn test_signed_call() { let mut test_tree = SliceData::load_builder(test_tree).unwrap(); - let response = decode_unknown_function_call( - WALLET_ABI.to_owned(), - test_tree.clone(), - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_call(WALLET_ABI.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!( serde_json::from_str::(&response.params).unwrap(), - serde_json::from_str::(&expected_params).unwrap()); + serde_json::from_str::(&expected_params).unwrap() + ); assert_eq!(response.function_name, "createArbitraryLimit"); let mut vec = vec![0x3C, 0x0B, 0xB9, 0xBC]; @@ -236,9 +228,7 @@ fn test_signed_call() { let expected_tree = BuilderData::with_bitstring(vec).unwrap(); - let (test_sign, test_hash) = get_signature_data( - WALLET_ABI, test_tree.clone(), None - ).unwrap(); + let (test_sign, test_hash) = get_signature_data(WALLET_ABI, test_tree.clone(), None).unwrap(); let mut sign = SliceData::load_cell(test_tree.checked_drain_reference().unwrap()).unwrap(); let sign = sign.get_next_bytes(64).unwrap(); @@ -254,10 +244,12 @@ fn test_signed_call() { let expected_response = r#"{"value0":"0"}"#; let response_tree = SliceData::load_builder( - BuilderData::with_bitstring( - vec![0xBC, 0x0B, 0xB9, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80] - ).unwrap() - ).unwrap(); + BuilderData::with_bitstring(vec![ + 0xBC, 0x0B, 0xB9, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + ]) + .unwrap(), + ) + .unwrap(); let response = decode_function_response( WALLET_ABI.to_owned(), @@ -270,14 +262,9 @@ fn test_signed_call() { assert_eq!(response, expected_response); - - let response = decode_unknown_function_response( - WALLET_ABI.to_owned(), - response_tree, - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_response(WALLET_ABI.to_owned(), response_tree, false, false) + .unwrap(); assert_eq!(response.params, expected_response); assert_eq!(response.function_name, "createArbitraryLimit"); @@ -302,9 +289,12 @@ fn test_not_signed_call() { .unwrap(); let mut expected_tree = BuilderData::with_bitstring(vec![ - 0x23, 0xF3, 0x3E, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80 - ]).unwrap(); - expected_tree.checked_prepend_reference(Default::default()).unwrap(); + 0x23, 0xF3, 0x3E, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + ]) + .unwrap(); + expected_tree + .checked_prepend_reference(Default::default()) + .unwrap(); assert_eq!(test_tree, expected_tree); } @@ -331,12 +321,12 @@ fn test_add_signature_full() { WALLET_ABI.to_owned(), &signature, Some(&pair.public.to_bytes()), - msg).unwrap(); + msg, + ) + .unwrap(); - let msg = SliceData::load_builder(msg).unwrap(); - let decoded = decode_unknown_function_call( - WALLET_ABI.to_owned(), msg, false, false, - ).unwrap(); + let msg = SliceData::load_builder(msg).unwrap(); + let decoded = decode_unknown_function_call(WALLET_ABI.to_owned(), msg, false, false).unwrap(); assert_eq!(decoded.params, params); } @@ -344,14 +334,12 @@ fn test_add_signature_full() { #[test] fn test_find_event() { let event_tree = SliceData::load_builder( - BuilderData::with_bitstring( - vec![0x13, 0x47, 0xD7, 0x9D, 0xFF, 0x80] - ).unwrap() - ).unwrap(); + BuilderData::with_bitstring(vec![0x13, 0x47, 0xD7, 0x9D, 0xFF, 0x80]).unwrap(), + ) + .unwrap(); - let decoded = decode_unknown_function_response( - WALLET_ABI.to_owned(), event_tree, false, false, - ).unwrap(); + let decoded = + decode_unknown_function_response(WALLET_ABI.to_owned(), event_tree, false, false).unwrap(); assert_eq!(decoded.function_name, "event"); assert_eq!(decoded.params, r#"{"param":"255"}"#); @@ -361,21 +349,22 @@ fn test_find_event() { fn test_store_pubkey() { let mut test_map = HashmapE::with_bit_len(Contract::DATA_MAP_KEYLEN); let test_pubkey = vec![11u8; 32]; - test_map.set_builder( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), - ).unwrap(); + test_map + .set_builder( + SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), + &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), + ) + .unwrap(); let data = SliceData::load_builder(test_map.write_to_new_cell().unwrap()).unwrap(); let new_data = Contract::insert_pubkey(data, &test_pubkey).unwrap(); let new_map = HashmapE::with_hashmap(Contract::DATA_MAP_KEYLEN, new_data.reference_opt(0)); - let key_slice = new_map.get( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let key_slice = new_map + .get(SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!(key_slice.get_bytestring(0), test_pubkey); } @@ -383,10 +372,12 @@ fn test_store_pubkey() { #[test] fn test_update_decode_contract_data() { let mut test_map = HashmapE::with_bit_len(Contract::DATA_MAP_KEYLEN); - test_map.set_builder( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), - ).unwrap(); + test_map + .set_builder( + SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), + &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), + ) + .unwrap(); let params = r#"{ "subscription": "0:1111111111111111111111111111111111111111111111111111111111111111", @@ -398,32 +389,30 @@ fn test_update_decode_contract_data() { let new_data = update_contract_data(WALLET_ABI, params, data).unwrap(); let new_map = HashmapE::with_hashmap(Contract::DATA_MAP_KEYLEN, new_data.reference_opt(0)); - - let key_slice = new_map.get( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let key_slice = new_map + .get(SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!(key_slice.get_bytestring(0), vec![0u8; 32]); - - let subscription_slice = new_map.get( - SliceData::load_builder(101u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let subscription_slice = new_map + .get(SliceData::load_builder(101u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!( subscription_slice.into_cell(), - MsgAddressInt::with_standart(None, 0, [0x11; 32].into()).unwrap().serialize().unwrap()); - + MsgAddressInt::with_standart(None, 0, [0x11; 32].into()) + .unwrap() + .serialize() + .unwrap() + ); - let owner_slice = new_map.get( - SliceData::load_builder(100u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let owner_slice = new_map + .get(SliceData::load_builder(100u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!(owner_slice.get_bytestring(0), vec![0x22; 32]); diff --git a/src/tests/v1/test_contract.rs b/src/tests/v1/test_contract.rs index 64c5c869..f6c262b9 100644 --- a/src/tests/v1/test_contract.rs +++ b/src/tests/v1/test_contract.rs @@ -11,8 +11,8 @@ * limitations under the License. */ -use {Contract, Function, Event, Param, ParamType, DataItem}; use std::collections::HashMap; +use {Contract, DataItem, Event, Function, Param, ParamType}; const TEST_ABI: &str = r#" { @@ -68,7 +68,10 @@ fn test_abi_parse() { let parsed_contract = Contract::load(TEST_ABI.as_bytes()).unwrap(); let mut functions = HashMap::new(); - let header = vec![Param { name: "time".into(), kind: ParamType::Time}]; + let header = vec![Param { + name: "time".into(), + kind: ParamType::Time, + }]; functions.insert( "input_and_output".to_owned(), @@ -77,18 +80,37 @@ fn test_abi_parse() { name: "input_and_output".to_owned(), header: header.clone(), inputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(64) }, - Param { name: "b".to_owned(), kind: ParamType::Array( - Box::new(ParamType::Uint(8))) }, - Param { name: "c".to_owned(), kind: ParamType::Bytes }, + Param { + name: "a".to_owned(), + kind: ParamType::Uint(64), + }, + Param { + name: "b".to_owned(), + kind: ParamType::Array(Box::new(ParamType::Uint(8))), + }, + Param { + name: "c".to_owned(), + kind: ParamType::Bytes, + }, ], outputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Int(16) }, - Param { name: "b".to_owned(), kind: ParamType::Uint(8) }, + Param { + name: "a".to_owned(), + kind: ParamType::Int(16), + }, + Param { + name: "b".to_owned(), + kind: ParamType::Uint(8), + }, ], - input_id: Function::calc_function_id("input_and_output(time,uint64,uint8[],bytes)(int16,uint8)v1") & 0x7FFFFFFF, - output_id: Function::calc_function_id("input_and_output(time,uint64,uint8[],bytes)(int16,uint8)v1") | 0x80000000 - }); + input_id: Function::calc_function_id( + "input_and_output(time,uint64,uint8[],bytes)(int16,uint8)v1", + ) & 0x7FFFFFFF, + output_id: Function::calc_function_id( + "input_and_output(time,uint64,uint8[],bytes)(int16,uint8)v1", + ) | 0x80000000, + }, + ); functions.insert( "no_output".to_owned(), @@ -96,13 +118,15 @@ fn test_abi_parse() { abi_version: 1.into(), name: "no_output".to_owned(), header: header.clone(), - inputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(15) }, - ], + inputs: vec![Param { + name: "a".to_owned(), + kind: ParamType::Uint(15), + }], outputs: vec![], input_id: Function::calc_function_id("no_output(time,uint15)()v1") & 0x7FFFFFFF, - output_id: Function::calc_function_id("no_output(time,uint15)()v1") | 0x80000000 - }); + output_id: Function::calc_function_id("no_output(time,uint15)()v1") | 0x80000000, + }, + ); functions.insert( "no_input".to_owned(), @@ -111,12 +135,14 @@ fn test_abi_parse() { name: "no_input".to_owned(), header: header.clone(), inputs: vec![], - outputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(8) }, - ], + outputs: vec![Param { + name: "a".to_owned(), + kind: ParamType::Uint(8), + }], input_id: Function::calc_function_id("no_input(time)(uint8)v1") & 0x7FFFFFFF, - output_id: Function::calc_function_id("no_input(time)(uint8)v1") | 0x80000000 - }); + output_id: Function::calc_function_id("no_input(time)(uint8)v1") | 0x80000000, + }, + ); functions.insert( "constructor".to_owned(), @@ -127,8 +153,9 @@ fn test_abi_parse() { inputs: vec![], outputs: vec![], input_id: Function::calc_function_id("constructor(time)()v1") & 0x7FFFFFFF, - output_id: Function::calc_function_id("constructor(time)()v1") | 0x80000000 - }); + output_id: Function::calc_function_id("constructor(time)()v1") | 0x80000000, + }, + ); functions.insert( "has_id".to_owned(), @@ -139,8 +166,9 @@ fn test_abi_parse() { inputs: vec![], outputs: vec![], input_id: 0x01234567, - output_id: 0x01234567 - }); + output_id: 0x01234567, + }, + ); let mut events = HashMap::new(); @@ -149,11 +177,13 @@ fn test_abi_parse() { Event { abi_version: 1.into(), name: "input".to_owned(), - inputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(64) }, - ], - id: Function::calc_function_id("input(uint64)v1") & 0x7FFFFFFF - }); + inputs: vec![Param { + name: "a".to_owned(), + kind: ParamType::Uint(64), + }], + id: Function::calc_function_id("input(uint64)v1") & 0x7FFFFFFF, + }, + ); events.insert( "no_input".to_owned(), @@ -161,8 +191,9 @@ fn test_abi_parse() { abi_version: 1.into(), name: "no_input".to_owned(), inputs: vec![], - id: Function::calc_function_id("no_input()v1") & 0x7FFFFFFF - }); + id: Function::calc_function_id("no_input()v1") & 0x7FFFFFFF, + }, + ); events.insert( "has_id".to_owned(), @@ -170,8 +201,9 @@ fn test_abi_parse() { abi_version: 1.into(), name: "has_id".to_owned(), inputs: vec![], - id: 0x89abcdef - }); + id: 0x89abcdef, + }, + ); let mut data = HashMap::new(); @@ -180,12 +212,21 @@ fn test_abi_parse() { DataItem { value: Param { name: "a".to_owned(), - kind: ParamType::Uint(256) + kind: ParamType::Uint(256), }, - key: 100 - }); + key: 100, + }, + ); - let expected_contract = Contract { abi_version: 1.into(), header, functions, events, data, fields: vec![] }; + let expected_contract = Contract { + abi_version: 1.into(), + header, + functions, + events, + data, + fields: vec![], + init_fields: Default::default(), + }; assert_eq!(parsed_contract, expected_contract); } diff --git a/src/tests/v2/full_stack_tests.rs b/src/tests/v2/full_stack_tests.rs index 20bf67dd..cea2035d 100644 --- a/src/tests/v2/full_stack_tests.rs +++ b/src/tests/v2/full_stack_tests.rs @@ -13,9 +13,9 @@ use ed25519::signature::{Signature, Signer}; -use ton_types::{BuilderData, SliceData, IBitstring}; -use ton_types::dictionary::HashmapE; use ton_block::{MsgAddressInt, Serializable}; +use ton_types::dictionary::HashmapE; +use ton_types::{BuilderData, IBitstring, SliceData}; use json_abi::*; @@ -180,43 +180,35 @@ fn test_constructor_call() { false, None, None, - ).unwrap(); + ) + .unwrap(); let mut expected_tree = BuilderData::new(); - expected_tree.append_bit_zero().unwrap(); // None for signature - expected_tree.append_u32(0xffffffff).unwrap(); // max u32 for expire - expected_tree.append_bit_zero().unwrap(); // None for public key - expected_tree.append_u32(0x68B55F3F).unwrap(); // function id + expected_tree.append_bit_zero().unwrap(); // None for signature + expected_tree.append_u32(0xffffffff).unwrap(); // max u32 for expire + expected_tree.append_bit_zero().unwrap(); // None for public key + expected_tree.append_u32(0x68B55F3F).unwrap(); // function id let test_tree = SliceData::load_builder(test_tree).unwrap(); let expected_tree = SliceData::load_builder(expected_tree).unwrap(); assert_eq!(test_tree, expected_tree); - let response = decode_unknown_function_call( - WALLET_ABI.to_owned(), - test_tree.clone(), - false, - false, - ).unwrap(); + let response = + decode_unknown_function_call(WALLET_ABI.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!(response.params, params); assert_eq!(response.function_name, "constructor"); - let test_tree = SliceData::from_raw(vec![0xE8, 0xB5, 0x5F, 0x3F], 32); - let response = decode_unknown_function_response( - WALLET_ABI.to_owned(), - test_tree.clone(), - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_response(WALLET_ABI.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!(response.params, params); assert_eq!(response.function_name, "constructor"); - let response = decode_function_response( WALLET_ABI.to_owned(), "constructor".to_owned(), @@ -254,34 +246,36 @@ fn test_signed_call() { let mut test_tree = SliceData::load_builder(test_tree).unwrap(); - let response = decode_unknown_function_call( - WALLET_ABI.to_owned(), - test_tree.clone(), - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_call(WALLET_ABI.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!( serde_json::from_str::(&response.params).unwrap(), - serde_json::from_str::(&expected_params).unwrap()); + serde_json::from_str::(&expected_params).unwrap() + ); assert_eq!(response.function_name, "createArbitraryLimit"); let mut expected_tree = BuilderData::new(); - expected_tree.append_u32(0xffffffff).unwrap(); // expire - expected_tree.append_bit_one().unwrap(); // Some for public key - expected_tree.append_raw(&pair.public.to_bytes(), ed25519_dalek::PUBLIC_KEY_LENGTH * 8).unwrap(); - expected_tree.append_u32(0x2238B58A).unwrap(); // function id - expected_tree.append_raw(&[0; 15], 15 * 8).unwrap(); // value - expected_tree.append_u8(12).unwrap(); // value - expected_tree.append_u32(30).unwrap(); // period - - let (test_sign, test_hash) = get_signature_data( - WALLET_ABI, test_tree.clone(), None - ).unwrap(); + expected_tree.append_u32(0xffffffff).unwrap(); // expire + expected_tree.append_bit_one().unwrap(); // Some for public key + expected_tree + .append_raw( + &pair.public.to_bytes(), + ed25519_dalek::PUBLIC_KEY_LENGTH * 8, + ) + .unwrap(); + expected_tree.append_u32(0x2238B58A).unwrap(); // function id + expected_tree.append_raw(&[0; 15], 15 * 8).unwrap(); // value + expected_tree.append_u8(12).unwrap(); // value + expected_tree.append_u32(30).unwrap(); // period + + let (test_sign, test_hash) = get_signature_data(WALLET_ABI, test_tree.clone(), None).unwrap(); assert!(test_tree.get_next_bit().unwrap()); - let sign = &test_tree.get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH).unwrap(); + let sign = &test_tree + .get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH) + .unwrap(); assert_eq!(sign, &test_sign); let sign = Signature::from_bytes(sign).unwrap(); @@ -294,10 +288,12 @@ fn test_signed_call() { let expected_response = r#"{"value0":"0"}"#; let response_tree = SliceData::load_builder( - BuilderData::with_bitstring( - vec![0xA2, 0x38, 0xB5, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80] - ).unwrap() - ).unwrap(); + BuilderData::with_bitstring(vec![ + 0xA2, 0x38, 0xB5, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + ]) + .unwrap(), + ) + .unwrap(); let response = decode_function_response( WALLET_ABI.to_owned(), @@ -310,14 +306,9 @@ fn test_signed_call() { assert_eq!(response, expected_response); - - let response = decode_unknown_function_response( - WALLET_ABI.to_owned(), - response_tree, - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_response(WALLET_ABI.to_owned(), response_tree, false, false) + .unwrap(); assert_eq!(response.params, expected_response); assert_eq!(response.function_name, "createArbitraryLimit"); @@ -345,14 +336,18 @@ fn test_not_signed_call() { .unwrap(); let mut expected_tree = BuilderData::new(); - expected_tree.append_bit_zero().unwrap(); // None for signature - expected_tree.append_u32(123).unwrap(); // expire - expected_tree.append_bit_one().unwrap(); // Some for public key - expected_tree.append_raw( - &hex::decode("11c0a428b6768562df09db05326595337dbb5f8dde0e128224d4df48df760f17").unwrap(), - 32 * 8).unwrap(); // pubkey - expected_tree.append_u32(0x4B774C98).unwrap(); // function id - expected_tree.append_u64(2).unwrap(); // limitId + expected_tree.append_bit_zero().unwrap(); // None for signature + expected_tree.append_u32(123).unwrap(); // expire + expected_tree.append_bit_one().unwrap(); // Some for public key + expected_tree + .append_raw( + &hex::decode("11c0a428b6768562df09db05326595337dbb5f8dde0e128224d4df48df760f17") + .unwrap(), + 32 * 8, + ) + .unwrap(); // pubkey + expected_tree.append_u32(0x4B774C98).unwrap(); // function id + expected_tree.append_u64(2).unwrap(); // limitId assert_eq!(test_tree, expected_tree); @@ -391,12 +386,12 @@ fn test_add_signature_full() { WALLET_ABI.to_owned(), &signature, Some(&pair.public.to_bytes()), - msg).unwrap(); + msg, + ) + .unwrap(); let msg = SliceData::load_builder(msg).unwrap(); - let decoded = decode_unknown_function_call( - WALLET_ABI.to_owned(), msg, false, false, - ).unwrap(); + let decoded = decode_unknown_function_call(WALLET_ABI.to_owned(), msg, false, false).unwrap(); assert_eq!(decoded.params, params); } @@ -404,14 +399,12 @@ fn test_add_signature_full() { #[test] fn test_find_event() { let event_tree = SliceData::load_builder( - BuilderData::with_bitstring( - vec![0x0C, 0xAF, 0x24, 0xBE, 0xFF, 0x80] - ).unwrap() - ).unwrap(); + BuilderData::with_bitstring(vec![0x0C, 0xAF, 0x24, 0xBE, 0xFF, 0x80]).unwrap(), + ) + .unwrap(); - let decoded = decode_unknown_function_response( - WALLET_ABI.to_owned(), event_tree, false, false, - ).unwrap(); + let decoded = + decode_unknown_function_response(WALLET_ABI.to_owned(), event_tree, false, false).unwrap(); assert_eq!(decoded.function_name, "event"); assert_eq!(decoded.params, r#"{"param":"255"}"#); @@ -421,21 +414,22 @@ fn test_find_event() { fn test_store_pubkey() { let mut test_map = HashmapE::with_bit_len(Contract::DATA_MAP_KEYLEN); let test_pubkey = vec![11u8; 32]; - test_map.set_builder( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), - ).unwrap(); + test_map + .set_builder( + SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), + &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), + ) + .unwrap(); let data = SliceData::load_cell(test_map.serialize().unwrap()).unwrap(); let new_data = Contract::insert_pubkey(data.into(), &test_pubkey).unwrap(); let new_map = HashmapE::with_hashmap(Contract::DATA_MAP_KEYLEN, new_data.reference_opt(0)); - let key_slice = new_map.get( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let key_slice = new_map + .get(SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!(key_slice.get_bytestring(0), test_pubkey); } @@ -443,10 +437,12 @@ fn test_store_pubkey() { #[test] fn test_update_decode_contract_data() { let mut test_map = HashmapE::with_bit_len(Contract::DATA_MAP_KEYLEN); - test_map.set_builder( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), - ).unwrap(); + test_map + .set_builder( + SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), + &BuilderData::with_raw(vec![0u8; 32], 256).unwrap(), + ) + .unwrap(); let params = r#"{ "subscription": "0:1111111111111111111111111111111111111111111111111111111111111111", @@ -458,32 +454,33 @@ fn test_update_decode_contract_data() { let new_data = update_contract_data(WALLET_ABI, params, data).unwrap(); let new_map = HashmapE::with_hashmap(Contract::DATA_MAP_KEYLEN, new_data.reference_opt(0)); - - let key_slice = new_map.get( - SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let key_slice = new_map + .get(SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!(key_slice.get_bytestring(0), vec![0u8; 32]); - - let subscription_slice = new_map.get( - SliceData::load_builder(101u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let subscription_slice = new_map + .get(SliceData::load_builder(101u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!( subscription_slice, - SliceData::load_cell(MsgAddressInt::with_standart(None, 0, [0x11; 32].into()).unwrap().serialize().unwrap()).unwrap()); + SliceData::load_cell( + MsgAddressInt::with_standart(None, 0, [0x11; 32].into()) + .unwrap() + .serialize() + .unwrap() + ) + .unwrap() + ); - - let owner_slice = new_map.get( - SliceData::load_builder(100u64.write_to_new_cell().unwrap()).unwrap(), - ) - .unwrap() - .unwrap(); + let owner_slice = new_map + .get(SliceData::load_builder(100u64.write_to_new_cell().unwrap()).unwrap()) + .unwrap() + .unwrap(); assert_eq!(owner_slice.get_bytestring(0), vec![0x22; 32]); @@ -508,7 +505,9 @@ const ABI_WITH_FIELDS: &str = r#"{ #[test] fn test_decode_storage_fields() { let mut storage = BuilderData::new(); - storage.append_bitstring(&[vec![0x55; 32], vec![0x80]].join(&[][..])).unwrap(); + storage + .append_bitstring(&[vec![0x55; 32], vec![0x80]].join(&[][..])) + .unwrap(); storage.append_u64(123).unwrap(); storage.append_bit_one().unwrap(); storage.append_u32(456).unwrap(); @@ -516,12 +515,16 @@ fn test_decode_storage_fields() { let decoded = decode_storage_fields(ABI_WITH_FIELDS, storage, false).unwrap(); - assert_eq!(decoded, serde_json::json!({ - "__pubkey": format!("0x{}", hex::encode([0x55; 32])), - "__timestamp":"123", - "ok": true, - "value": "456" - }).to_string()); + assert_eq!( + decoded, + serde_json::json!({ + "__pubkey": format!("0x{}", hex::encode([0x55; 32])), + "__timestamp":"123", + "ok": true, + "value": "456" + }) + .to_string() + ); } #[test] @@ -546,17 +549,17 @@ fn test_add_signature_full_v23() { WALLET_ABI_V23.to_owned(), &signature, Some(&pair.public.to_bytes()), - msg).unwrap(); + msg, + ) + .unwrap(); let msg = SliceData::load_builder(msg).unwrap(); - let decoded = decode_unknown_function_call( - WALLET_ABI_V23.to_owned(), msg, false, false, - ).unwrap(); + let decoded = + decode_unknown_function_call(WALLET_ABI_V23.to_owned(), msg, false, false).unwrap(); assert_eq!(decoded.params, params); } - #[test] fn test_signed_call_v23() { let params = r#" @@ -583,44 +586,52 @@ fn test_signed_call_v23() { let mut test_tree = SliceData::load_builder(test_tree).unwrap(); - let response = decode_unknown_function_call( - WALLET_ABI_V23.to_owned(), - test_tree.clone(), - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_call(WALLET_ABI_V23.to_owned(), test_tree.clone(), false, false) + .unwrap(); assert_eq!( serde_json::from_str::(&response.params).unwrap(), - serde_json::from_str::(&expected_params).unwrap()); + serde_json::from_str::(&expected_params).unwrap() + ); assert_eq!(response.function_name, "createArbitraryLimit"); let mut expected_tree = BuilderData::new(); - expected_tree.append_u32(0xffffffff).unwrap(); // expire - expected_tree.append_bit_one().unwrap(); // Some for public key - expected_tree.append_raw(&pair.public.to_bytes(), ed25519_dalek::PUBLIC_KEY_LENGTH * 8).unwrap(); - expected_tree.append_u32(0x2238B58A).unwrap(); // function id + expected_tree.append_u32(0xffffffff).unwrap(); // expire + expected_tree.append_bit_one().unwrap(); // Some for public key + expected_tree + .append_raw( + &pair.public.to_bytes(), + ed25519_dalek::PUBLIC_KEY_LENGTH * 8, + ) + .unwrap(); + expected_tree.append_u32(0x2238B58A).unwrap(); // function id let mut expected_tree_child = BuilderData::new(); - expected_tree_child.append_raw(&[0; 15], 15 * 8).unwrap(); // value - expected_tree_child.append_u8(12).unwrap(); // value - expected_tree_child.append_u32(30).unwrap(); // period + expected_tree_child.append_raw(&[0; 15], 15 * 8).unwrap(); // value + expected_tree_child.append_u8(12).unwrap(); // value + expected_tree_child.append_u32(30).unwrap(); // period - expected_tree.checked_append_reference(expected_tree_child.into_cell().unwrap()).unwrap(); + expected_tree + .checked_append_reference(expected_tree_child.into_cell().unwrap()) + .unwrap(); - let (test_sign, test_hash) = get_signature_data( - WALLET_ABI_V23, test_tree.clone(), Some(address.to_owned()) - ).unwrap(); + let (test_sign, test_hash) = + get_signature_data(WALLET_ABI_V23, test_tree.clone(), Some(address.to_owned())).unwrap(); assert!(test_tree.get_next_bit().unwrap()); - let sign = &test_tree.get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH).unwrap(); + let sign = &test_tree + .get_next_bytes(ed25519_dalek::SIGNATURE_LENGTH) + .unwrap(); assert_eq!(sign, &test_sign); let sign = Signature::from_bytes(sign).unwrap(); assert_eq!(test_tree, SliceData::load_builder(expected_tree).unwrap()); - let mut signed_tree = MsgAddressInt::from_str(address).unwrap().write_to_new_cell().unwrap(); + let mut signed_tree = MsgAddressInt::from_str(address) + .unwrap() + .write_to_new_cell() + .unwrap(); signed_tree.append_builder(&test_tree.as_builder()).unwrap(); let hash = signed_tree.into_cell().unwrap().repr_hash(); @@ -630,10 +641,12 @@ fn test_signed_call_v23() { let expected_response = r#"{"value0":"0"}"#; let response_tree = SliceData::load_builder( - BuilderData::with_bitstring( - vec![0xA2, 0x38, 0xB5, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80] - ).unwrap() - ).unwrap(); + BuilderData::with_bitstring(vec![ + 0xA2, 0x38, 0xB5, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + ]) + .unwrap(), + ) + .unwrap(); let response = decode_function_response( WALLET_ABI_V23.to_owned(), @@ -646,14 +659,9 @@ fn test_signed_call_v23() { assert_eq!(response, expected_response); - - let response = decode_unknown_function_response( - WALLET_ABI_V23.to_owned(), - response_tree, - false, - false, - ) - .unwrap(); + let response = + decode_unknown_function_response(WALLET_ABI_V23.to_owned(), response_tree, false, false) + .unwrap(); assert_eq!(response.params, expected_response); assert_eq!(response.function_name, "createArbitraryLimit"); @@ -668,7 +676,8 @@ fn value_helper(abi_type: &str, value: &str) -> Result { ], "events": [], "data": [] - }).to_string(); + }) + .to_string(); let params = json!({"value": value}).to_string(); encode_function_call( abi, @@ -689,7 +698,8 @@ fn test_max_varuint32() { assert_eq!( encoded.data(), - &hex::decode("1869a0307ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc").unwrap() + &hex::decode("1869a0307ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc") + .unwrap() ); assert_eq!(encoded.length_in_bits(), 286); assert_eq!(encoded.references().len(), 0); @@ -703,7 +713,8 @@ fn test_max_varint32() { assert_eq!( encoded.data(), - &hex::decode("30d82fc87dfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc").unwrap() + &hex::decode("30d82fc87dfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc") + .unwrap() ); assert_eq!(encoded.length_in_bits(), 286); assert_eq!(encoded.references().len(), 0); @@ -716,7 +727,8 @@ fn test_max_uint() { assert_eq!( encoded.data(), - &hex::decode("3a8707b37fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80").unwrap() + &hex::decode("3a8707b37fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80") + .unwrap() ); assert_eq!(encoded.length_in_bits(), 289); assert_eq!(encoded.references().len(), 0); @@ -729,8 +741,70 @@ fn test_max_int() { assert_eq!( encoded.data(), - &hex::decode("088fb044bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0").unwrap() + &hex::decode("088fb044bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0") + .unwrap() ); assert_eq!(encoded.length_in_bits(), 290); assert_eq!(encoded.references().len(), 0); } + +const ABI_WITH_FIELDS_V24: &str = r#"{ + "version": "2.4", + "functions": [], + "fields": [ + {"name":"__pubkey","type":"uint256","init":true}, + {"name":"__timestamp","type":"uint64"}, + {"name":"ok","type":"bool", "init": true}, + {"name":"value","type":"address"} + ] +}"#; + +#[test] +fn test_encode_storage_fields() { + let test_tree = encode_storage_fields( + ABI_WITH_FIELDS_V24, + Some( + r#"{ + "__pubkey": "0x11c0a428b6768562df09db05326595337dbb5f8dde0e128224d4df48df760f17", + "ok": true + }"#, + ), + ) + .unwrap(); + + let mut expected_tree = BuilderData::new(); + expected_tree + .append_raw( + &hex::decode("11c0a428b6768562df09db05326595337dbb5f8dde0e128224d4df48df760f17") + .unwrap(), + 32 * 8, + ) + .unwrap(); + expected_tree.append_u64(0).unwrap(); + expected_tree.append_bit_one().unwrap(); + expected_tree.append_bits(0, 2).unwrap(); + + assert_eq!(test_tree, expected_tree); + + assert!(dbg!(encode_storage_fields( + ABI_WITH_FIELDS_V24, + Some( + r#"{ + "ok": true + }"# + ), + )) + .is_err()); + + assert!(dbg!(encode_storage_fields( + ABI_WITH_FIELDS_V24, + Some( + r#"{ + "__pubkey": "0x11c0a428b6768562df09db05326595337dbb5f8dde0e128224d4df48df760f17", + "__timestamp": 123, + "ok": true + }"# + ), + )) + .is_err()); +} diff --git a/src/tests/v2/test_contract.rs b/src/tests/v2/test_contract.rs index 50f0b1ab..0e53e30d 100644 --- a/src/tests/v2/test_contract.rs +++ b/src/tests/v2/test_contract.rs @@ -11,14 +11,14 @@ * limitations under the License. */ -use {Contract, Function, Event, Param, ParamType, DataItem}; use std::collections::HashMap; +use {Contract, DataItem, Event, Function, Param, ParamType}; -use crate::contract::ABI_VERSION_2_2; +use crate::contract::ABI_VERSION_2_4; const TEST_ABI: &str = r#" { - "version": "2.2", + "version": "2.4", "header": [ "time", "expire", @@ -71,7 +71,7 @@ const TEST_ABI: &str = r#" ], "fields": [ { "name": "a", "type": "uint32" }, - { "name": "b", "type": "int128" } + { "name": "b", "type": "int128", "init": true } ] }"#; @@ -81,12 +81,24 @@ fn test_abi_parse() { let mut functions = HashMap::new(); let header = vec![ - Param { name: "time".into(), kind: ParamType::Time}, - Param { name: "expire".into(), kind: ParamType::Expire}, - Param { name: "pubkey".into(), kind: ParamType::PublicKey}, - Param { name: "a".into(), kind: ParamType::Uint(64)}, + Param { + name: "time".into(), + kind: ParamType::Time, + }, + Param { + name: "expire".into(), + kind: ParamType::Expire, + }, + Param { + name: "pubkey".into(), + kind: ParamType::PublicKey, + }, + Param { + name: "a".into(), + kind: ParamType::Uint(64), + }, ]; - let abi_version = ABI_VERSION_2_2; + let abi_version = ABI_VERSION_2_4; functions.insert( "input_and_output".to_owned(), @@ -95,18 +107,37 @@ fn test_abi_parse() { name: "input_and_output".to_owned(), header: header.clone(), inputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(64) }, - Param { name: "b".to_owned(), kind: ParamType::Array( - Box::new(ParamType::Uint(8))) }, - Param { name: "c".to_owned(), kind: ParamType::Bytes }, + Param { + name: "a".to_owned(), + kind: ParamType::Uint(64), + }, + Param { + name: "b".to_owned(), + kind: ParamType::Array(Box::new(ParamType::Uint(8))), + }, + Param { + name: "c".to_owned(), + kind: ParamType::Bytes, + }, ], outputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Int(16) }, - Param { name: "b".to_owned(), kind: ParamType::Uint(8) }, + Param { + name: "a".to_owned(), + kind: ParamType::Int(16), + }, + Param { + name: "b".to_owned(), + kind: ParamType::Uint(8), + }, ], - input_id: Function::calc_function_id("input_and_output(uint64,uint8[],bytes)(int16,uint8)v2") & 0x7FFFFFFF, - output_id: Function::calc_function_id("input_and_output(uint64,uint8[],bytes)(int16,uint8)v2") | 0x80000000 - }); + input_id: Function::calc_function_id( + "input_and_output(uint64,uint8[],bytes)(int16,uint8)v2", + ) & 0x7FFFFFFF, + output_id: Function::calc_function_id( + "input_and_output(uint64,uint8[],bytes)(int16,uint8)v2", + ) | 0x80000000, + }, + ); functions.insert( "no_output".to_owned(), @@ -114,13 +145,15 @@ fn test_abi_parse() { abi_version: abi_version.clone(), name: "no_output".to_owned(), header: header.clone(), - inputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(15) }, - ], + inputs: vec![Param { + name: "a".to_owned(), + kind: ParamType::Uint(15), + }], outputs: vec![], input_id: Function::calc_function_id("no_output(uint15)()v2") & 0x7FFFFFFF, - output_id: Function::calc_function_id("no_output(uint15)()v2") | 0x80000000 - }); + output_id: Function::calc_function_id("no_output(uint15)()v2") | 0x80000000, + }, + ); functions.insert( "no_input".to_owned(), @@ -129,12 +162,14 @@ fn test_abi_parse() { name: "no_input".to_owned(), header: header.clone(), inputs: vec![], - outputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(8) }, - ], + outputs: vec![Param { + name: "a".to_owned(), + kind: ParamType::Uint(8), + }], input_id: Function::calc_function_id("no_input()(uint8)v2") & 0x7FFFFFFF, - output_id: Function::calc_function_id("no_input()(uint8)v2") | 0x80000000 - }); + output_id: Function::calc_function_id("no_input()(uint8)v2") | 0x80000000, + }, + ); functions.insert( "constructor".to_owned(), @@ -145,8 +180,9 @@ fn test_abi_parse() { inputs: vec![], outputs: vec![], input_id: Function::calc_function_id("constructor()()v2") & 0x7FFFFFFF, - output_id: Function::calc_function_id("constructor()()v2") | 0x80000000 - }); + output_id: Function::calc_function_id("constructor()()v2") | 0x80000000, + }, + ); functions.insert( "has_id".to_owned(), @@ -157,8 +193,9 @@ fn test_abi_parse() { inputs: vec![], outputs: vec![], input_id: 0x01234567, - output_id: 0x01234567 - }); + output_id: 0x01234567, + }, + ); let mut events = HashMap::new(); @@ -167,11 +204,13 @@ fn test_abi_parse() { Event { abi_version: abi_version.clone(), name: "input".to_owned(), - inputs: vec![ - Param { name: "a".to_owned(), kind: ParamType::Uint(64) }, - ], - id: Function::calc_function_id("input(uint64)v2") & 0x7FFFFFFF - }); + inputs: vec![Param { + name: "a".to_owned(), + kind: ParamType::Uint(64), + }], + id: Function::calc_function_id("input(uint64)v2") & 0x7FFFFFFF, + }, + ); events.insert( "no_input".to_owned(), @@ -179,8 +218,9 @@ fn test_abi_parse() { abi_version: abi_version.clone(), name: "no_input".to_owned(), inputs: vec![], - id: Function::calc_function_id("no_input()v2") & 0x7FFFFFFF - }); + id: Function::calc_function_id("no_input()v2") & 0x7FFFFFFF, + }, + ); events.insert( "has_id".to_owned(), @@ -188,8 +228,9 @@ fn test_abi_parse() { abi_version: abi_version.clone(), name: "has_id".to_owned(), inputs: vec![], - id: 0x89abcdef - }); + id: 0x89abcdef, + }, + ); let mut data = HashMap::new(); @@ -198,17 +239,34 @@ fn test_abi_parse() { DataItem { value: Param { name: "a".to_owned(), - kind: ParamType::Uint(256) + kind: ParamType::Uint(256), }, - key: 100 - }); + key: 100, + }, + ); let fields = vec![ - Param { name: "a".into(), kind: ParamType::Uint(32)}, - Param { name: "b".into(), kind:ParamType::Int(128)}, + Param { + name: "a".into(), + kind: ParamType::Uint(32), + }, + Param { + name: "b".into(), + kind: ParamType::Int(128), + }, ]; - let expected_contract = Contract { abi_version, header, functions, events, data, fields }; + let init_fields = vec!["b".to_owned()].into_iter().collect(); + + let expected_contract = Contract { + abi_version, + header, + functions, + events, + data, + fields, + init_fields, + }; assert_eq!(parsed_contract, expected_contract); } @@ -237,4 +295,3 @@ fn print_function_singnatures() { println!("{:X?}\n", id); } } - diff --git a/src/token/deserialize.rs b/src/token/deserialize.rs index 6ba872cc..16764112 100644 --- a/src/token/deserialize.rs +++ b/src/token/deserialize.rs @@ -11,21 +11,32 @@ * limitations under the License. */ -use crate::{contract::{ABI_VERSION_1_0, AbiVersion}, error::AbiError, int::{Int, Uint}, param::Param, param_type::ParamType, token::{Token, TokenValue}}; +use crate::{ + contract::{AbiVersion, ABI_VERSION_1_0}, + error::AbiError, + int::{Int, Uint}, + param::Param, + param_type::ParamType, + token::{Token, TokenValue}, +}; use num_bigint::{BigInt, BigUint}; use num_traits::ToPrimitive; use serde_json; use std::collections::BTreeMap; -use ton_block::{MsgAddress, types::Grams}; +use ton_block::{types::Grams, MsgAddress}; use ton_types::{ - BuilderData, Cell, error, fail, HashmapE, HashmapType, IBitstring, Result, SliceData + error, fail, BuilderData, Cell, HashmapE, HashmapType, IBitstring, Result, SliceData, }; impl TokenValue { /// Deserializes value from `SliceData` to `TokenValue` pub fn read_from( - param_type: &ParamType, mut cursor: SliceData, last: bool, abi_version: &AbiVersion, allow_partial: bool + param_type: &ParamType, + mut cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { match param_type { ParamType::Uint(size) => Self::read_uint(*size, cursor), @@ -36,19 +47,24 @@ impl TokenValue { cursor = find_next_bits(cursor, 1)?; Ok((TokenValue::Bool(cursor.get_next_bit()?), cursor)) } - ParamType::Tuple(tuple_params) => - Self::read_tuple(tuple_params, cursor, last, abi_version, allow_partial), - ParamType::Array(item_type) => Self::read_array(&item_type, cursor, abi_version, allow_partial), + ParamType::Tuple(tuple_params) => { + Self::read_tuple(tuple_params, cursor, last, abi_version, allow_partial) + } + ParamType::Array(item_type) => { + Self::read_array(&item_type, cursor, abi_version, allow_partial) + } ParamType::FixedArray(item_type, size) => { Self::read_fixed_array(&item_type, *size, cursor, abi_version, allow_partial) } ParamType::Cell => Self::read_cell(cursor, last, abi_version) .map(|(cell, cursor)| (TokenValue::Cell(cell), cursor)), - ParamType::Map(key_type, value_type) => - Self::read_hashmap(key_type, value_type, cursor, abi_version, allow_partial), + ParamType::Map(key_type, value_type) => { + Self::read_hashmap(key_type, value_type, cursor, abi_version, allow_partial) + } ParamType::Address => { cursor = find_next_bits(cursor, 1)?; - let address = ::construct_from(&mut cursor)?; + let address = + ::construct_from(&mut cursor)?; Ok((TokenValue::Address(address), cursor)) } ParamType::Bytes => Self::read_bytes(None, cursor, last, abi_version), @@ -58,12 +74,16 @@ impl TokenValue { cursor = find_next_bits(cursor, 1)?; let gram = ::construct_from(&mut cursor)?; Ok((TokenValue::Token(gram), cursor)) - }, + } ParamType::Time => Self::read_time(cursor), ParamType::Expire => Self::read_expire(cursor), ParamType::PublicKey => Self::read_public_key(cursor), - ParamType::Optional(inner_type) => Self::read_optional(&inner_type, cursor, last, abi_version, allow_partial), - ParamType::Ref(inner_type) => Self::read_ref(&inner_type, cursor, last, abi_version, allow_partial), + ParamType::Optional(inner_type) => { + Self::read_optional(&inner_type, cursor, last, abi_version, allow_partial) + } + ParamType::Ref(inner_type) => { + Self::read_ref(&inner_type, cursor, last, abi_version, allow_partial) + } } } @@ -90,7 +110,7 @@ impl TokenValue { } fn read_varuint(size: usize, cursor: SliceData) -> Result<(Self, SliceData)> { - let (len, cursor) = Self::read_uint_from_chain(ParamType::varint_size_len(size), cursor)?; + let (len, cursor) = Self::read_uint_from_chain(TokenValue::varint_size_len(size), cursor)?; let len = len.to_usize().unwrap(); if len == 0 { Ok((TokenValue::VarUint(size, 0u32.into()), cursor)) @@ -101,7 +121,7 @@ impl TokenValue { } fn read_varint(size: usize, cursor: SliceData) -> Result<(Self, SliceData)> { - let (len, cursor) = Self::read_uint_from_chain(ParamType::varint_size_len(size), cursor)?; + let (len, cursor) = Self::read_uint_from_chain(TokenValue::varint_size_len(size), cursor)?; let len = len.to_usize().unwrap(); if len == 0 { Ok((TokenValue::VarInt(size, 0.into()), cursor)) @@ -112,15 +132,18 @@ impl TokenValue { } fn read_tuple( - tuple_params: &[Param], cursor: SliceData, last: bool, abi_version: &AbiVersion, allow_partial: bool + tuple_params: &[Param], + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { let mut tokens = Vec::new(); let mut cursor = cursor; for param in tuple_params { let last = last && Some(param) == tuple_params.last(); - let (token_value, new_cursor) = TokenValue::read_from( - ¶m.kind, cursor, last, abi_version, allow_partial - )?; + let (token_value, new_cursor) = + TokenValue::read_from(¶m.kind, cursor, last, abi_version, allow_partial)?; tokens.push(Token { name: param.name.clone(), value: token_value, @@ -131,7 +154,9 @@ impl TokenValue { } fn check_full_decode(allow_partial: bool, remaining: &SliceData) -> Result<()> { - if !allow_partial && (remaining.remaining_references() != 0 || remaining.remaining_bits() != 0) { + if !allow_partial + && (remaining.remaining_references() != 0 || remaining.remaining_bits() != 0) + { fail!(AbiError::IncompleteDeserializationError) } else { Ok(()) @@ -139,7 +164,11 @@ impl TokenValue { } fn read_array_from_map( - item_type: &ParamType, mut cursor: SliceData, size: usize, abi_version: &AbiVersion, allow_partial: bool + item_type: &ParamType, + mut cursor: SliceData, + size: usize, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Vec, SliceData)> { let original = cursor.clone(); cursor = find_next_bits(cursor, 1)?; @@ -150,13 +179,15 @@ impl TokenValue { index.append_u32(i as u32)?; match map.get(SliceData::load_builder(index)?) { Ok(Some(item_slice)) => { - let (token, item_slice) = Self::read_from( - item_type, item_slice, true, abi_version, allow_partial - )?; + let (token, item_slice) = + Self::read_from(item_type, item_slice, true, abi_version, allow_partial)?; Self::check_full_decode(allow_partial, &item_slice)?; result.push(token); } - _ => fail!(AbiError::DeserializationError { msg: "Array doesn't contain item with specified index", cursor: original } ) + _ => fail!(AbiError::DeserializationError { + msg: "Array doesn't contain item with specified index", + cursor: original + }), } } @@ -164,59 +195,89 @@ impl TokenValue { } fn read_array( - item_type: &ParamType, mut cursor: SliceData, abi_version: &AbiVersion, allow_partial: bool + item_type: &ParamType, + mut cursor: SliceData, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { cursor = find_next_bits(cursor, 32)?; let size = cursor.get_next_u32()?; let (result, cursor) = Self::read_array_from_map( - item_type, cursor, size as usize, abi_version, allow_partial + item_type, + cursor, + size as usize, + abi_version, + allow_partial, )?; Ok((TokenValue::Array(item_type.clone(), result), cursor)) } fn read_fixed_array( - item_type: &ParamType, size: usize, cursor: SliceData, abi_version: &AbiVersion, allow_partial: bool + item_type: &ParamType, + size: usize, + cursor: SliceData, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { - let (result, cursor) = Self::read_array_from_map( - item_type, cursor, size, abi_version, allow_partial - )?; + let (result, cursor) = + Self::read_array_from_map(item_type, cursor, size, abi_version, allow_partial)?; Ok((TokenValue::FixedArray(item_type.clone(), result), cursor)) } - fn read_cell(mut cursor: SliceData, last: bool, abi_version: &AbiVersion) -> Result<(Cell, SliceData)> { + fn read_cell( + mut cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + ) -> Result<(Cell, SliceData)> { let cell = match cursor.remaining_references() { - 1 if (abi_version == &ABI_VERSION_1_0 && cursor.cell().references_count() == BuilderData::references_capacity()) - || (abi_version != &ABI_VERSION_1_0 && !last && cursor.remaining_bits() == 0) => { + 1 if (abi_version == &ABI_VERSION_1_0 + && cursor.cell().references_count() == BuilderData::references_capacity()) + || (abi_version != &ABI_VERSION_1_0 && !last && cursor.remaining_bits() == 0) => + { cursor = SliceData::load_cell(cursor.reference(0)?)?; cursor.checked_drain_reference()? } - _ => cursor.checked_drain_reference()? + _ => cursor.checked_drain_reference()?, }; Ok((cell.clone(), cursor)) } fn read_hashmap( - key_type: &ParamType, value_type: &ParamType, mut cursor: SliceData, abi_version: &AbiVersion, allow_partial: bool + key_type: &ParamType, + value_type: &ParamType, + mut cursor: SliceData, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { cursor = find_next_bits(cursor, 1)?; let mut new_map = BTreeMap::new(); - let bit_len = key_type.get_map_key_size()?; + let bit_len = TokenValue::get_map_key_size(key_type)?; let hashmap = HashmapE::with_hashmap(bit_len, cursor.get_dictionary()?.reference_opt(0)); hashmap.iterate_slices(|key, value| { let key = Self::read_from(key_type, key, true, abi_version, allow_partial)?.0; - let key = serde_json::to_value(&key)?.as_str().ok_or(AbiError::InvalidData { - msg: "Non-ordinary key".to_owned() - })?.to_owned(); + let key = serde_json::to_value(&key)? + .as_str() + .ok_or(AbiError::InvalidData { + msg: "Non-ordinary key".to_owned(), + })? + .to_owned(); let value = Self::read_from(value_type, value, true, abi_version, allow_partial)?.0; new_map.insert(key, value); Ok(true) })?; - Ok((TokenValue::Map(key_type.clone(), value_type.clone(), new_map), cursor)) + Ok(( + TokenValue::Map(key_type.clone(), value_type.clone(), new_map), + cursor, + )) } - fn read_bytes_from_chain(cursor: SliceData, last: bool, abi_version: &AbiVersion) -> Result<(Vec, SliceData)> { + fn read_bytes_from_chain( + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + ) -> Result<(Vec, SliceData)> { let original = cursor.clone(); let (mut cell, cursor) = Self::read_cell(cursor, last, abi_version)?; @@ -231,14 +292,19 @@ impl TokenValue { data.extend_from_slice(cell.data()); cell = match cell.reference(0) { Ok(cell) => cell.clone(), - Err(_) => break + Err(_) => break, }; } Ok((data, cursor)) } - fn read_bytes(size: Option, cursor: SliceData, last: bool, abi_version: &AbiVersion) -> Result<(Self, SliceData)> { + fn read_bytes( + size: Option, + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + ) -> Result<(Self, SliceData)> { let original = cursor.clone(); let (data, cursor) = Self::read_bytes_from_chain(cursor, last, abi_version)?; @@ -248,17 +314,20 @@ impl TokenValue { msg: "Size of fixed bytes does not correspond to expected size", cursor: original }), - None => Ok((TokenValue::Bytes(data), cursor)) + None => Ok((TokenValue::Bytes(data), cursor)), } } - fn read_string(cursor: SliceData, last: bool, abi_version: &AbiVersion) -> Result<(Self, SliceData)> { + fn read_string( + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + ) -> Result<(Self, SliceData)> { let (data, cursor) = Self::read_bytes_from_chain(cursor, last, abi_version)?; - let string = String::from_utf8(data) - .map_err(|err| AbiError::InvalidData { - msg: format!("Can not deserialize string: {}", err) - })?; + let string = String::from_utf8(data).map_err(|err| AbiError::InvalidData { + msg: format!("Can not deserialize string: {}", err), + })?; Ok((TokenValue::String(string), cursor)) } @@ -276,29 +345,45 @@ impl TokenValue { cursor = find_next_bits(cursor, 1)?; if cursor.get_next_bit()? { let (vec, cursor) = get_next_bits_from_chain(cursor, 256)?; - Ok((TokenValue::PublicKey(Some(ed25519_dalek::PublicKey::from_bytes(&vec)?)), cursor)) + Ok(( + TokenValue::PublicKey(Some(ed25519_dalek::PublicKey::from_bytes(&vec)?)), + cursor, + )) } else { Ok((TokenValue::PublicKey(None), cursor)) } } fn read_optional( - inner_type: &ParamType, cursor: SliceData, last: bool, abi_version: &AbiVersion, allow_partial: bool + inner_type: &ParamType, + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { let mut cursor = find_next_bits(cursor, 1)?; if cursor.get_next_bit()? { - if inner_type.is_large_optional() { + if Self::is_large_optional(inner_type) { let (cell, cursor) = Self::read_cell(cursor, last, abi_version)?; let (result, remaining) = Self::read_from( - inner_type, SliceData::load_cell(cell)?, true, abi_version, allow_partial + inner_type, + SliceData::load_cell(cell)?, + true, + abi_version, + allow_partial, )?; Self::check_full_decode(allow_partial, &remaining)?; - Ok((TokenValue::Optional(inner_type.clone(), Some(Box::new(result))), cursor)) + Ok(( + TokenValue::Optional(inner_type.clone(), Some(Box::new(result))), + cursor, + )) } else { - let (result, cursor) = Self::read_from( - inner_type, cursor, last, abi_version, allow_partial - )?; - Ok((TokenValue::Optional(inner_type.clone(), Some(Box::new(result))), cursor)) + let (result, cursor) = + Self::read_from(inner_type, cursor, last, abi_version, allow_partial)?; + Ok(( + TokenValue::Optional(inner_type.clone(), Some(Box::new(result))), + cursor, + )) } } else { Ok((TokenValue::Optional(inner_type.clone(), None), cursor)) @@ -306,11 +391,19 @@ impl TokenValue { } fn read_ref( - inner_type: &ParamType, cursor: SliceData, last: bool, abi_version: &AbiVersion, allow_partial: bool + inner_type: &ParamType, + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result<(Self, SliceData)> { let (cell, cursor) = Self::read_cell(cursor, last, abi_version)?; let (result, remaining) = Self::read_from( - inner_type, SliceData::load_cell(cell)?, true, abi_version, allow_partial + inner_type, + SliceData::load_cell(cell)?, + true, + abi_version, + allow_partial, )?; Self::check_full_decode(allow_partial, &remaining)?; Ok((TokenValue::Ref(Box::new(result)), cursor)) @@ -318,23 +411,28 @@ impl TokenValue { /// Decodes provided params from SliceData pub fn decode_params( - params: &Vec, mut cursor: SliceData, abi_version: &AbiVersion, allow_partial: bool + params: &Vec, + mut cursor: SliceData, + abi_version: &AbiVersion, + allow_partial: bool, ) -> Result> { let mut tokens = vec![]; for param in params { // println!("{:?}", param); let last = Some(param) == params.last(); - let (token_value, new_cursor) = Self::read_from( - ¶m.kind, cursor, last, abi_version, allow_partial - )?; + let (token_value, new_cursor) = + Self::read_from(¶m.kind, cursor, last, abi_version, allow_partial)?; cursor = new_cursor; - tokens.push(Token { name: param.name.clone(), value: token_value }); + tokens.push(Token { + name: param.name.clone(), + value: token_value, + }); } Self::check_full_decode(allow_partial, &cursor)?; - + Ok(tokens) } } @@ -353,11 +451,11 @@ fn find_next_bits(mut cursor: SliceData, bits: usize) -> Result { } cursor = SliceData::load_cell(cursor.reference(0)?)?; } - match cursor.remaining_bits() >= bits { + match cursor.remaining_bits() >= bits { true => Ok(cursor), - false => fail!(AbiError::DeserializationError { - msg: "Not enough remaining bits in the cell", + false => fail!(AbiError::DeserializationError { + msg: "Not enough remaining bits in the cell", cursor: original - }) + }), } } diff --git a/src/token/detokenizer.rs b/src/token/detokenizer.rs index 2d6f9445..f20b60be 100644 --- a/src/token/detokenizer.rs +++ b/src/token/detokenizer.rs @@ -11,37 +11,38 @@ * limitations under the License. */ -use crate::{ param_type::ParamType, token::{Token, TokenValue} }; +use crate::{ + param_type::ParamType, + token::{Token, TokenValue}, +}; use num_bigint::{BigInt, BigUint}; -use serde::ser::{Serialize, Serializer, SerializeMap}; -use std::collections::{HashMap, BTreeMap}; -use ton_types::{Cell, Result, write_boc}; +use serde::ser::{Serialize, SerializeMap, Serializer}; +use std::collections::{BTreeMap, HashMap}; +use ton_types::{write_boc, Cell, Result}; pub struct Detokenizer; impl Detokenizer { pub fn detokenize(tokens: &[Token]) -> Result { - Ok( - serde_json::to_string( - &Self::detokenize_to_json_value(tokens)? - )? - ) + Ok(serde_json::to_string(&Self::detokenize_to_json_value( + tokens, + )?)?) } pub fn detokenize_to_json_value(tokens: &[Token]) -> Result { - Ok(serde_json::to_value(&FunctionParams{params: tokens})?) + Ok(serde_json::to_value(&FunctionParams { params: tokens })?) } pub fn detokenize_optional(tokens: &HashMap) -> Result { - Ok( - serde_json::to_string( - &Self::detokenize_optional_to_json_value(tokens)? - )? - ) + Ok(serde_json::to_string( + &Self::detokenize_optional_to_json_value(tokens)?, + )?) } - pub fn detokenize_optional_to_json_value(tokens: &HashMap) -> Result { + pub fn detokenize_optional_to_json_value( + tokens: &HashMap, + ) -> Result { serde_json::to_value(&tokens).map_err(|err| err.into()) } } @@ -51,29 +52,35 @@ pub struct FunctionParams<'a> { } impl<'a> Serialize for FunctionParams<'a> { - fn serialize(&self, serializer: S) -> std::result::Result + fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, { let mut map = serializer.serialize_map(Some(self.params.len()))?; for token in self.params { - map.serialize_entry(&token.name, &token.value)?; - } + map.serialize_entry(&token.name, &token.value)?; + } map.end() } } impl Token { - pub fn detokenize_big_int(number: &BigInt, serializer: S) -> std::result::Result + pub fn detokenize_big_int( + number: &BigInt, + serializer: S, + ) -> std::result::Result where S: Serializer, { serializer.serialize_str(&number.to_str_radix(10)) } - pub fn detokenize_grams(number: impl ToString, serializer: S) -> std::result::Result + pub fn detokenize_grams( + number: impl ToString, + serializer: S, + ) -> std::result::Result where S: Serializer, { @@ -96,7 +103,11 @@ impl Token { serializer.serialize_str(&uint_str) } - pub fn detokenize_hashmap(_key_type: &ParamType, values: &BTreeMap, serializer: S) -> std::result::Result + pub fn detokenize_hashmap( + _key_type: &ParamType, + values: &BTreeMap, + serializer: S, + ) -> std::result::Result where S: Serializer, { @@ -111,8 +122,7 @@ impl Token { where S: Serializer, { - let data = write_boc(cell) - .map_err(|err| serde::ser::Error::custom(err.to_string()))?; + let data = write_boc(cell).map_err(|err| serde::ser::Error::custom(err.to_string()))?; let data = base64::encode(&data); serializer.serialize_str(&data) @@ -126,7 +136,10 @@ impl Token { serializer.serialize_str(&data) } - pub fn detokenize_public_key(value: &Option, serializer: S) -> std::result::Result + pub fn detokenize_public_key( + value: &Option, + serializer: S, + ) -> std::result::Result where S: Serializer, { @@ -137,7 +150,10 @@ impl Token { } } - pub fn detokenize_optional(value: &Option, serializer: S) -> std::result::Result + pub fn detokenize_optional( + value: &Option, + serializer: S, + ) -> std::result::Result where S: Serializer, { @@ -164,14 +180,13 @@ impl Serialize for TokenValue { } TokenValue::VarInt(_, int) => Token::detokenize_big_int(&int, serializer), TokenValue::Bool(b) => serializer.serialize_bool(b.clone()), - TokenValue::Tuple(tokens) => { - FunctionParams {params: tokens}.serialize(serializer) - }, + TokenValue::Tuple(tokens) => FunctionParams { params: tokens }.serialize(serializer), TokenValue::Array(_, ref tokens) => tokens.serialize(serializer), TokenValue::FixedArray(_, ref tokens) => tokens.serialize(serializer), TokenValue::Cell(ref cell) => Token::detokenize_cell(cell, serializer), - TokenValue::Map(key_type, _, ref map) => - Token::detokenize_hashmap(key_type, map, serializer), + TokenValue::Map(key_type, _, ref map) => { + Token::detokenize_hashmap(key_type, map, serializer) + } TokenValue::Address(ref address) => serializer.serialize_str(&address.to_string()), TokenValue::Bytes(ref arr) => Token::detokenize_bytes(arr, serializer), TokenValue::FixedBytes(ref arr) => Token::detokenize_bytes(arr, serializer), diff --git a/src/token/mod.rs b/src/token/mod.rs index caeff934..6ef997a3 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -13,25 +13,33 @@ //! TON ABI params. use crate::{ - error::AbiError, int::{Int, Uint}, param::Param, param_type::ParamType + error::AbiError, + int::{Int, Uint}, + param::Param, + param_type::ParamType, }; +use chrono::prelude::Utc; +use num_bigint::{BigInt, BigUint}; use std::collections::BTreeMap; use std::fmt; use ton_block::{Grams, MsgAddress}; -use ton_types::{Result, Cell}; -use chrono::prelude::Utc; -use num_bigint::{BigInt, BigUint}; +use ton_types::{BuilderData, Cell, Result}; -mod tokenizer; +mod deserialize; mod detokenizer; mod serialize; -mod deserialize; +mod tokenizer; -pub use self::tokenizer::*; +pub use self::deserialize::*; pub use self::detokenizer::*; pub use self::serialize::*; -pub use self::deserialize::*; +pub use self::tokenizer::*; + +#[cfg(test)] +mod test_encoding; +#[cfg(test)] +mod tests; pub const STD_ADDRESS_BIT_LENGTH: usize = 267; pub const MAX_HASH_MAP_INFO_ABOUT_KEY: usize = 12; @@ -45,7 +53,10 @@ pub struct Token { impl Token { pub fn new(name: &str, value: TokenValue) -> Self { - Self { name: name.to_string(), value } + Self { + name: name.to_string(), + value, + } } } @@ -100,19 +111,19 @@ pub enum TokenValue { /// Address(MsgAddress), /// Raw byte array - /// + /// /// Encoded as separate cells chain Bytes(Vec), /// Fixed sized raw byte array - /// + /// /// Encoded as separate cells chain FixedBytes(Vec), /// UTF8 string - /// + /// /// Encoded similar to `Bytes` String(String), /// Nanograms - /// + /// Token(Grams), /// Timestamp Time(u64), @@ -169,15 +180,19 @@ impl fmt::Display for TokenValue { TokenValue::Time(time) => write!(f, "{}", time), TokenValue::Expire(expire) => write!(f, "{}", expire), TokenValue::Ref(value) => write!(f, "{}", value), - TokenValue::PublicKey(key) => if let Some(key) = key { - write!(f, "{}", hex::encode(&key.to_bytes())) - } else { - write!(f, "None") - }, - TokenValue::Optional(_, value) => if let Some(value) = value { - write!(f, "{}", value) - } else { - write!(f, "None") + TokenValue::PublicKey(key) => { + if let Some(key) = key { + write!(f, "{}", hex::encode(&key.to_bytes())) + } else { + write!(f, "None") + } + } + TokenValue::Optional(_, value) => { + if let Some(value) = value { + write!(f, "{}", value) + } else { + write!(f, "None") + } } } } @@ -205,7 +220,7 @@ impl TokenValue { TokenValue::Array(inner_type, ref tokens) => { if let ParamType::Array(ref param_type) = *param_type { inner_type == param_type.as_ref() - && tokens.iter().all(|t| t.type_check(param_type)) + && tokens.iter().all(|t| t.type_check(param_type)) } else { false } @@ -213,22 +228,22 @@ impl TokenValue { TokenValue::FixedArray(inner_type, ref tokens) => { if let ParamType::FixedArray(ref param_type, size) = *param_type { size == tokens.len() - && inner_type == param_type.as_ref() - && tokens.iter().all(|t| t.type_check(param_type)) + && inner_type == param_type.as_ref() + && tokens.iter().all(|t| t.type_check(param_type)) } else { false } } TokenValue::Cell(_) => *param_type == ParamType::Cell, - TokenValue::Map(map_key_type, map_value_type, ref values) =>{ + TokenValue::Map(map_key_type, map_value_type, ref values) => { if let ParamType::Map(ref key_type, ref value_type) = *param_type { map_key_type == key_type.as_ref() - && map_value_type == value_type.as_ref() - && values.iter().all(|t| t.1.type_check(value_type)) + && map_value_type == value_type.as_ref() + && values.iter().all(|t| t.1.type_check(value_type)) } else { false } - }, + } TokenValue::Address(_) => *param_type == ParamType::Address, TokenValue::Bytes(_) => *param_type == ParamType::Bytes, TokenValue::FixedBytes(ref arr) => *param_type == ParamType::FixedBytes(arr.len()), @@ -239,12 +254,15 @@ impl TokenValue { TokenValue::PublicKey(_) => *param_type == ParamType::PublicKey, TokenValue::Optional(opt_type, opt_value) => { if let ParamType::Optional(ref param_type) = *param_type { - param_type.as_ref() == opt_type && - opt_value.as_ref().map(|val| val.type_check(param_type)).unwrap_or(true) + param_type.as_ref() == opt_type + && opt_value + .as_ref() + .map(|val| val.type_check(param_type)) + .unwrap_or(true) } else { false } - }, + } TokenValue::Ref(value) => { if let ParamType::Ref(ref param_type) = *param_type { value.type_check(param_type) @@ -257,7 +275,6 @@ impl TokenValue { /// Returns `ParamType` the token value represents pub(crate) fn get_param_type(&self) -> ParamType { - match self { TokenValue::Uint(uint) => ParamType::Uint(uint.size), TokenValue::Int(int) => ParamType::Int(int.size), @@ -272,8 +289,9 @@ impl TokenValue { ParamType::FixedArray(Box::new(param_type.clone()), tokens.len()) } TokenValue::Cell(_) => ParamType::Cell, - TokenValue::Map(key_type, value_type, _) => - ParamType::Map(Box::new(key_type.clone()), Box::new(value_type.clone())), + TokenValue::Map(key_type, value_type, _) => { + ParamType::Map(Box::new(key_type.clone()), Box::new(value_type.clone())) + } TokenValue::Address(_) => ParamType::Address, TokenValue::Bytes(_) => ParamType::Bytes, TokenValue::FixedBytes(ref arr) => ParamType::FixedBytes(arr.len()), @@ -282,10 +300,10 @@ impl TokenValue { TokenValue::Time(_) => ParamType::Time, TokenValue::Expire(_) => ParamType::Expire, TokenValue::PublicKey(_) => ParamType::PublicKey, - TokenValue::Optional(ref param_type, _) => - ParamType::Optional(Box::new(param_type.clone())), - TokenValue::Ref(value) => - ParamType::Ref(Box::new(value.get_param_type())), + TokenValue::Optional(ref param_type, _) => { + ParamType::Optional(Box::new(param_type.clone())) + } + TokenValue::Ref(value) => ParamType::Ref(Box::new(value.get_param_type())), } } @@ -294,11 +312,143 @@ impl TokenValue { ParamType::Time => Ok(TokenValue::Time(Utc::now().timestamp_millis() as u64)), ParamType::Expire => Ok(TokenValue::Expire(u32::max_value())), ParamType::PublicKey => Ok(TokenValue::PublicKey(None)), - any_type => Err( - AbiError::InvalidInputData { - msg: format!( - "Type {} doesn't have default value and must be explicitly defined", - any_type)}.into()) + any_type => Err(AbiError::InvalidInputData { + msg: format!( + "Type {} doesn't have default value and must be explicitly defined", + any_type + ), + } + .into()), + } + } + + pub fn get_map_key_size(param_type: &ParamType) -> Result { + match param_type { + ParamType::Int(size) | ParamType::Uint(size) => Ok(*size), + ParamType::Address => Ok(crate::token::STD_ADDRESS_BIT_LENGTH), + _ => Err(ton_types::error!(AbiError::InvalidData { + msg: "Only integer and std address values can be map keys".to_owned() + })), + } + } + + pub(crate) fn varint_size_len(size: usize) -> usize { + 8 - ((size - 1) as u8).leading_zeros() as usize + } + + pub(crate) fn is_large_optional(param_type: &ParamType) -> bool { + Self::max_bit_size(param_type) >= BuilderData::bits_capacity() + || Self::max_refs_count(param_type) >= BuilderData::references_capacity() + } + + pub(crate) fn max_refs_count(param_type: &ParamType) -> usize { + match param_type { + // in-cell serialized types + ParamType::Uint(_) + | ParamType::Int(_) + | ParamType::VarUint(_) + | ParamType::VarInt(_) + | ParamType::Bool + | ParamType::Address + | ParamType::Token + | ParamType::Time + | ParamType::Expire + | ParamType::PublicKey => 0, + // reference serialized types + ParamType::Array(_) + | ParamType::FixedArray(_, _) + | ParamType::Cell + | ParamType::String + | ParamType::Map(_, _) + | ParamType::Bytes + | ParamType::FixedBytes(_) + | ParamType::Ref(_) => 1, + // tuple refs is sum of inner types refs + ParamType::Tuple(params) => params + .iter() + .fold(0, |acc, param| acc + Self::max_refs_count(¶m.kind)), + // large optional is serialized into reference + ParamType::Optional(param_type) => { + if Self::is_large_optional(param_type) { + 1 + } else { + Self::max_refs_count(param_type) + } + } + } + } + + pub(crate) fn max_bit_size(param_type: &ParamType) -> usize { + match param_type { + ParamType::Uint(size) => *size, + ParamType::Int(size) => *size, + ParamType::VarUint(size) => Self::varint_size_len(*size) + (size - 1) * 8, + ParamType::VarInt(size) => Self::varint_size_len(*size) + (size - 1) * 8, + ParamType::Bool => 1, + ParamType::Array(_) => 33, + ParamType::FixedArray(_, _) => 1, + ParamType::Cell => 0, + ParamType::Map(_, _) => 1, + ParamType::Address => 591, + ParamType::Bytes | ParamType::FixedBytes(_) => 0, + ParamType::String => 0, + ParamType::Token => 124, + ParamType::Time => 64, + ParamType::Expire => 32, + ParamType::PublicKey => 257, + ParamType::Ref(_) => 0, + ParamType::Tuple(params) => params + .iter() + .fold(0, |acc, param| acc + Self::max_bit_size(¶m.kind)), + ParamType::Optional(param_type) => { + if Self::is_large_optional(¶m_type) { + 1 + } else { + 1 + Self::max_bit_size(¶m_type) + } + } + } + } + + pub(crate) fn default_value(param_type: &ParamType) -> TokenValue { + match param_type { + ParamType::Uint(size) => TokenValue::Uint(Uint::new(0, *size)), + ParamType::Int(size) => TokenValue::Int(Int::new(0, *size)), + ParamType::VarUint(size) => TokenValue::VarUint(*size, 0u32.into()), + ParamType::VarInt(size) => TokenValue::VarInt(*size, 0.into()), + ParamType::Bool => TokenValue::Bool(false), + ParamType::Array(inner) => TokenValue::Array(inner.as_ref().clone(), vec![]), + ParamType::FixedArray(inner, size) => TokenValue::FixedArray( + inner.as_ref().clone(), + std::iter::repeat(Self::default_value(inner)) + .take(*size) + .collect(), + ), + ParamType::Cell => TokenValue::Cell(Default::default()), + ParamType::Map(key, value) => TokenValue::Map( + key.as_ref().clone(), + value.as_ref().clone(), + Default::default(), + ), + ParamType::Address => TokenValue::Address(MsgAddress::AddrNone), + ParamType::Bytes => TokenValue::Bytes(vec![]), + ParamType::FixedBytes(size) => TokenValue::FixedBytes(vec![0; *size]), + ParamType::String => TokenValue::String(Default::default()), + ParamType::Token => TokenValue::Token(Default::default()), + ParamType::Time => TokenValue::Time(0), + ParamType::Expire => TokenValue::Expire(0), + ParamType::PublicKey => TokenValue::PublicKey(None), + ParamType::Ref(inner) => TokenValue::Ref(Box::new(Self::default_value(inner))), + ParamType::Tuple(params) => TokenValue::Tuple( + params + .iter() + .map(|inner| Token { + name: inner.name.clone(), + value: Self::default_value(&inner.kind), + }) + .collect(), + ), + ParamType::Optional(inner) => TokenValue::Optional(inner.as_ref().clone(), None), } } } diff --git a/src/token/serialize.rs b/src/token/serialize.rs index 30d68c73..fcc855e0 100644 --- a/src/token/serialize.rs +++ b/src/token/serialize.rs @@ -11,12 +11,18 @@ * limitations under the License. */ -use crate::{contract::{ABI_VERSION_1_0, ABI_VERSION_2_2, AbiVersion}, error::AbiError, int::{Int, Uint}, param_type::ParamType, token::{Token, Tokenizer, TokenValue}}; +use crate::{ + contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_2}, + error::AbiError, + int::{Int, Uint}, + param_type::ParamType, + token::{Token, TokenValue, Tokenizer}, +}; use num_bigint::{BigInt, BigUint, Sign}; use std::collections::BTreeMap; use ton_block::Serializable; -use ton_types::{BuilderData, Cell, SliceData, error, fail, HashmapE, IBitstring, Result}; +use ton_types::{error, fail, BuilderData, Cell, HashmapE, IBitstring, Result, SliceData}; pub struct SerializedValue { pub data: BuilderData, @@ -26,7 +32,7 @@ pub struct SerializedValue { impl From for SerializedValue { fn from(data: BuilderData) -> Self { - SerializedValue { + SerializedValue { max_bits: data.bits_used(), max_refs: data.references_used(), data, @@ -35,7 +41,11 @@ impl From for SerializedValue { } impl TokenValue { - pub fn pack_values_into_chain(tokens: &[Token], mut cells: Vec, abi_version: &AbiVersion) -> Result { + pub fn pack_values_into_chain( + tokens: &[Token], + mut cells: Vec, + abi_version: &AbiVersion, + ) -> Result { for token in tokens { cells.append(&mut token.value.write_to_cells(abi_version)?); } @@ -48,17 +58,25 @@ impl TokenValue { // first cell is resulting builder // every next cell: put data to root - fn pack_cells_into_chain(mut values: Vec, abi_version: &AbiVersion) -> Result { + fn pack_cells_into_chain( + mut values: Vec, + abi_version: &AbiVersion, + ) -> Result { values.reverse(); let mut packed_cells = match values.pop() { Some(cell) => vec![cell], - None => fail!(AbiError::InvalidData { msg: "No cells".to_owned() } ) + None => fail!(AbiError::InvalidData { + msg: "No cells".to_owned() + }), }; while let Some(value) = values.pop() { let builder = packed_cells.last_mut().unwrap(); let (remaining_bits, remaining_refs) = if abi_version >= &ABI_VERSION_2_2 { - (BuilderData::bits_capacity() - builder.max_bits, BuilderData::references_capacity() - builder.max_refs) + ( + BuilderData::bits_capacity() - builder.max_bits, + BuilderData::references_capacity() - builder.max_refs, + ) } else { (builder.data.bits_free(), builder.data.references_free()) }; @@ -68,21 +86,17 @@ impl TokenValue { (value.data.bits_used(), value.data.references_used()) }; - if remaining_bits < value_bits || - remaining_refs < value_refs - { + if remaining_bits < value_bits || remaining_refs < value_refs { // if not enough bits or refs - continue chain packed_cells.push(value); - } else if value_refs > 0 && - remaining_refs == value_refs - { + } else if value_refs > 0 && remaining_refs == value_refs { // if refs strictly fit into cell we should decide if we can put them into current // cell or to the next cell: if all remaining values can fit into current cell, // then use current, if not - continue chain let (refs, bits) = Self::get_remaining(&values, abi_version); // in ABI v1 last ref is always used for chaining - if abi_version != &ABI_VERSION_1_0 && - (refs == 0 && bits + value_bits <= remaining_bits) + if abi_version != &ABI_VERSION_1_0 + && (refs == 0 && bits + value_bits <= remaining_bits) { builder.data.append_builder(&value.data)?; builder.max_bits += value.max_bits; @@ -90,21 +104,23 @@ impl TokenValue { } else { packed_cells.push(value); } - } else { builder.data.append_builder(&value.data)?; builder.max_bits += value.max_bits; builder.max_refs += value.max_refs; } } - Ok(packed_cells.into_iter().rev().reduce( - |acc, mut cur| { - cur.data.checked_append_reference(acc.data.into_cell().unwrap()).unwrap(); + Ok(packed_cells + .into_iter() + .rev() + .reduce(|acc, mut cur| { + cur.data + .checked_append_reference(acc.data.into_cell().unwrap()) + .unwrap(); cur }) .unwrap() - .data - ) + .data) } fn get_remaining(values: &[SerializedValue], abi_version: &AbiVersion) -> (usize, usize) { @@ -112,9 +128,11 @@ impl TokenValue { if abi_version >= &ABI_VERSION_2_2 { (refs + value.max_refs, bits + value.max_bits) } else { - (refs + value.data.references_used(), bits + value.data.bits_used()) + ( + refs + value.data.references_used(), + bits + value.data.bits_used(), + ) } - }) } @@ -132,32 +150,38 @@ impl TokenValue { } return Ok(vec); } - TokenValue::Array(param_type, ref tokens) => - Self::write_array(param_type, tokens, abi_version), - TokenValue::FixedArray(param_type, ref tokens) => - Self::write_fixed_array(param_type, tokens, abi_version), + TokenValue::Array(param_type, ref tokens) => { + Self::write_array(param_type, tokens, abi_version) + } + TokenValue::FixedArray(param_type, ref tokens) => { + Self::write_fixed_array(param_type, tokens, abi_version) + } TokenValue::Cell(cell) => Self::write_cell(cell), - TokenValue::Map(key_type, value_type, value) => - Self::write_map(key_type, value_type, value, abi_version), + TokenValue::Map(key_type, value_type, value) => { + Self::write_map(key_type, value_type, value, abi_version) + } TokenValue::Address(address) => Ok(address.write_to_new_cell()?), - TokenValue::Bytes(ref arr) | TokenValue::FixedBytes(ref arr) => - Self::write_bytes(arr, abi_version), + TokenValue::Bytes(ref arr) | TokenValue::FixedBytes(ref arr) => { + Self::write_bytes(arr, abi_version) + } TokenValue::String(ref string) => Self::write_bytes(string.as_bytes(), abi_version), TokenValue::Token(gram) => Ok(gram.write_to_new_cell()?), TokenValue::Time(time) => Ok(time.write_to_new_cell()?), TokenValue::Expire(expire) => Ok(expire.write_to_new_cell()?), TokenValue::PublicKey(key) => Self::write_public_key(key), - TokenValue::Optional(param_type, value) => - Self::write_optional(param_type, value.as_ref().map(|val| val.as_ref()), abi_version), - TokenValue::Ref(value) => - Self::write_ref(value, abi_version), + TokenValue::Optional(param_type, value) => Self::write_optional( + param_type, + value.as_ref().map(|val| val.as_ref()), + abi_version, + ), + TokenValue::Ref(value) => Self::write_ref(value, abi_version), }?; let param_type = self.get_param_type(); Ok(vec![SerializedValue { data, - max_bits: param_type.max_bit_size(), - max_refs: param_type.max_refs_count(), + max_bits: Self::max_bit_size(¶m_type), + max_refs: Self::max_refs_count(¶m_type), }]) } @@ -193,16 +217,17 @@ impl TokenValue { } fn write_uint(value: &Uint) -> Result { - let int = Int{ + let int = Int { number: BigInt::from_biguint(Sign::Plus, value.number.clone()), - size: value.size}; + size: value.size, + }; Self::write_int(&int) } fn write_varnumber(vec: &Vec, size: usize) -> Result { let mut builder = BuilderData::new(); - let bits = ParamType::varint_size_len(size); + let bits = Self::varint_size_len(size); if vec != &[0] { builder.append_bits(vec.len(), bits as usize)?; builder.append_raw(&vec, vec.len() * 8)?; @@ -249,15 +274,20 @@ impl TokenValue { // creates dictionary with indexes of an array items as keys and items as values // and prepends dictionary to cell - fn put_array_into_dictionary(param_type: &ParamType, array: &[TokenValue], abi_version: &AbiVersion) -> Result { + fn put_array_into_dictionary( + param_type: &ParamType, + array: &[TokenValue], + abi_version: &AbiVersion, + ) -> Result { let mut map = HashmapE::with_bit_len(32); - let value_in_ref = Self::map_value_in_ref(32, param_type.max_bit_size()); + let value_in_ref = Self::map_value_in_ref(32, Self::max_bit_size(param_type)); for i in 0..array.len() { let index = SliceData::load_builder((i as u32).write_to_new_cell()?)?; - let data = Self::pack_cells_into_chain(array[i].write_to_cells(abi_version)?, abi_version)?; + let data = + Self::pack_cells_into_chain(array[i].write_to_cells(abi_version)?, abi_version)?; if value_in_ref { map.set_builder(index, &data)?; @@ -269,18 +299,26 @@ impl TokenValue { Ok(map) } - fn write_array(param_type: &ParamType, value: &Vec, abi_version: &AbiVersion) -> Result { + fn write_array( + param_type: &ParamType, + value: &Vec, + abi_version: &AbiVersion, + ) -> Result { let map = Self::put_array_into_dictionary(param_type, value, abi_version)?; let mut builder = BuilderData::new(); builder.append_u32(value.len() as u32)?; - + map.write_to(&mut builder)?; Ok(builder) } - fn write_fixed_array(param_type: &ParamType, value: &Vec, abi_version: &AbiVersion) -> Result { + fn write_fixed_array( + param_type: &ParamType, + value: &Vec, + abi_version: &AbiVersion, + ) -> Result { let map = Self::put_array_into_dictionary(param_type, value, abi_version)?; Ok(map.write_to_new_cell()?) @@ -294,7 +332,7 @@ impl TokenValue { } else { match len % cell_len { 0 => cell_len, - x => x + x => x, } }; let mut builder = BuilderData::new(); @@ -317,9 +355,14 @@ impl TokenValue { super::MAX_HASH_MAP_INFO_ABOUT_KEY + key_len + value_len <= 1023 } - fn write_map(key_type: &ParamType, value_type: &ParamType, value: &BTreeMap, abi_version: &AbiVersion) -> Result { - let key_len = key_type.get_map_key_size()?; - let value_len = value_type.max_bit_size(); + fn write_map( + key_type: &ParamType, + value_type: &ParamType, + value: &BTreeMap, + abi_version: &AbiVersion, + ) -> Result { + let key_len = Self::get_map_key_size(key_type)?; + let value_len = Self::max_bit_size(value_type); let value_in_ref = Self::map_value_in_ref(key_len, value_len); let mut hashmap = HashmapE::with_bit_len(key_len); @@ -329,15 +372,20 @@ impl TokenValue { let mut key_vec = key.write_to_cells(abi_version)?; if key_vec.len() != 1 { - fail!(AbiError::InvalidData { msg: "Map key must be 1-cell length".to_owned() } ) + fail!(AbiError::InvalidData { + msg: "Map key must be 1-cell length".to_owned() + }) }; - if &ParamType::Address == key_type && - key_vec[0].data.length_in_bits() != super::STD_ADDRESS_BIT_LENGTH + if &ParamType::Address == key_type + && key_vec[0].data.length_in_bits() != super::STD_ADDRESS_BIT_LENGTH { - fail!(AbiError::InvalidData { msg: "Only std non-anycast address can be used as map key".to_owned() } ) + fail!(AbiError::InvalidData { + msg: "Only std non-anycast address can be used as map key".to_owned() + }) } - let data = Self::pack_cells_into_chain(value.write_to_cells(abi_version)?, abi_version)?; + let data = + Self::pack_cells_into_chain(value.write_to_cells(abi_version)?, abi_version)?; let slice_key = SliceData::load_builder(key_vec.pop().unwrap().data)?; if value_in_ref { @@ -366,9 +414,13 @@ impl TokenValue { Ok(builder) } - fn write_optional(param_type: &ParamType, value: Option<&TokenValue>, abi_version: &AbiVersion) -> Result { + fn write_optional( + param_type: &ParamType, + value: Option<&TokenValue>, + abi_version: &AbiVersion, + ) -> Result { if let Some(value) = value { - if param_type.is_large_optional() { + if Self::is_large_optional(param_type) { let value = value.pack_into_chain(abi_version)?; let mut builder = BuilderData::new(); builder.append_bit_one()?; @@ -395,21 +447,44 @@ impl TokenValue { #[test] fn test_pack_cells() { let cells = vec![ - BuilderData::with_bitstring(vec![1, 2, 0x80]).unwrap().into(), - BuilderData::with_bitstring(vec![3, 4, 0x80]).unwrap().into(), + BuilderData::with_bitstring(vec![1, 2, 0x80]) + .unwrap() + .into(), + BuilderData::with_bitstring(vec![3, 4, 0x80]) + .unwrap() + .into(), ]; let builder = BuilderData::with_bitstring(vec![1, 2, 3, 4, 0x80]).unwrap(); - assert_eq!(TokenValue::pack_cells_into_chain(cells, &ABI_VERSION_1_0).unwrap(), builder); + assert_eq!( + TokenValue::pack_cells_into_chain(cells, &ABI_VERSION_1_0).unwrap(), + builder + ); let cells = vec![ - BuilderData::with_raw(vec![0x55; 100], 100 * 8).unwrap().into(), - BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap().into(), - BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap().into(), + BuilderData::with_raw(vec![0x55; 100], 100 * 8) + .unwrap() + .into(), + BuilderData::with_raw(vec![0x55; 127], 127 * 8) + .unwrap() + .into(), + BuilderData::with_raw(vec![0x55; 127], 127 * 8) + .unwrap() + .into(), ]; - + let builder = BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap(); - let builder = BuilderData::with_raw_and_refs(vec![0x55; 127], 127 * 8, vec![builder.into_cell().unwrap()]).unwrap(); - let builder = BuilderData::with_raw_and_refs(vec![0x55; 100], 100 * 8, vec![builder.into_cell().unwrap()]).unwrap(); + let builder = BuilderData::with_raw_and_refs( + vec![0x55; 127], + 127 * 8, + vec![builder.into_cell().unwrap()], + ) + .unwrap(); + let builder = BuilderData::with_raw_and_refs( + vec![0x55; 100], + 100 * 8, + vec![builder.into_cell().unwrap()], + ) + .unwrap(); let tree = TokenValue::pack_cells_into_chain(cells, &ABI_VERSION_1_0).unwrap(); assert_eq!(tree, builder); } diff --git a/src/token/test_encoding.rs b/src/token/test_encoding.rs index fafc7640..6b941aa6 100644 --- a/src/token/test_encoding.rs +++ b/src/token/test_encoding.rs @@ -11,16 +11,19 @@ * limitations under the License. */ +use num_bigint::{BigInt, BigUint}; use std::collections::BTreeMap; use std::iter::FromIterator; use std::str::FromStr; -use num_bigint::{BigInt, BigUint}; -use ton_types::{AccountId, BuilderData, Cell, IBitstring, Result, SliceData}; -use ton_types::dictionary::{HashmapE, HashmapType}; use ton_block::{AnycastInfo, Grams, MsgAddress, Serializable}; +use ton_types::dictionary::{HashmapE, HashmapType}; +use ton_types::{AccountId, BuilderData, Cell, IBitstring, Result, SliceData}; -use crate::contract::{ABI_VERSION_1_0, ABI_VERSION_2_0, ABI_VERSION_2_1, ABI_VERSION_2_2, AbiVersion, MAX_SUPPORTED_VERSION}; +use crate::contract::{ + AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_0, ABI_VERSION_2_1, ABI_VERSION_2_2, + MAX_SUPPORTED_VERSION, +}; use {Int, Param, ParamType, Token, TokenValue, Uint}; @@ -30,7 +33,8 @@ fn put_array_into_map(array: &[T]) -> HashmapE { for i in 0..array.len() { let index = (i as u32).write_to_new_cell().unwrap(); let data = array[i].write_to_new_cell().unwrap(); - map.set_builder(SliceData::load_builder(index).unwrap(), &data).unwrap(); + map.set_builder(SliceData::load_builder(index).unwrap(), &data) + .unwrap(); } map @@ -57,7 +61,8 @@ fn test_parameters_set( prefix.append_u32(0).unwrap(); // tree check - let test_tree = TokenValue::pack_values_into_chain(inputs, vec![prefix.into()], version).unwrap(); + let test_tree = + TokenValue::pack_values_into_chain(inputs, vec![prefix.into()], version).unwrap(); println!("{:#.3}", test_tree.clone().into_cell().unwrap()); println!("{:#.3}", params_tree.clone().into_cell().unwrap()); @@ -75,15 +80,14 @@ fn test_parameters_set( slice.checked_drain_reference().unwrap(); slice.get_next_u32().unwrap(); - let decoded_tokens = TokenValue::decode_params( - ¶ms, slice, &version.clone().into(), false - ).unwrap(); + let decoded_tokens = + TokenValue::decode_params(¶ms, slice, &version.clone().into(), false).unwrap(); assert_eq!(decoded_tokens, inputs); } } fn params_from_tokens(tokens: &[Token]) -> Vec { - tokens.iter().map(|ref token| token.get_param()).collect() + tokens.iter().map(|ref token| token.get_param()).collect() } fn tokens_from_values(values: Vec) -> Vec { @@ -102,6 +106,22 @@ fn tokens_from_values(values: Vec) -> Vec { .collect() } +fn params_from_types(types: Vec) -> Vec { + let param_names = vec![ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", + "s", "t", "u", "v", "w", "x", "y", "z", + ]; + + types + .into_iter() + .zip(param_names) + .map(|(kind, name)| Param { + name: name.to_owned(), + kind: kind, + }) + .collect() +} + #[test] fn test_one_input_and_output() { // test prefix with one ref and u32 @@ -151,32 +171,63 @@ fn test_with_address() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.checked_append_reference(BuilderData::with_bitstring(vec![1, 2, 3, 0x80]).unwrap().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference( + BuilderData::with_bitstring(vec![1, 2, 3, 0x80]) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); - let anycast = AnycastInfo::with_rewrite_pfx(SliceData::new(vec![0x77, 0x78, 0x79, 0x80])).unwrap(); + let anycast = + AnycastInfo::with_rewrite_pfx(SliceData::new(vec![0x77, 0x78, 0x79, 0x80])).unwrap(); let addresses = vec![ MsgAddress::AddrNone, MsgAddress::with_extern(SliceData::new(vec![0x55, 0x80])).unwrap(), MsgAddress::with_standart(Some(anycast.clone()), -1, AccountId::from([0x11; 32])).unwrap(), MsgAddress::with_standart(Some(anycast.clone()), -1, AccountId::from([0x11; 32])).unwrap(), - MsgAddress::with_variant(Some(anycast.clone()), -128, SliceData::new(vec![0x66, 0x67, 0x68, 0x69, 0x80])).unwrap(), + MsgAddress::with_variant( + Some(anycast.clone()), + -128, + SliceData::new(vec![0x66, 0x67, 0x68, 0x69, 0x80]), + ) + .unwrap(), MsgAddress::with_standart(Some(anycast.clone()), -1, AccountId::from([0x11; 32])).unwrap(), ]; let mut builder_v2_2 = builder.clone(); - let mut builders: Vec = addresses.iter().map(|address| address.write_to_new_cell().unwrap()).collect(); + let mut builders: Vec = addresses + .iter() + .map(|address| address.write_to_new_cell().unwrap()) + .collect(); builders.reverse(); - builder_v2_2.append_builder(&builders.pop().unwrap()).unwrap(); + builder_v2_2 + .append_builder(&builders.pop().unwrap()) + .unwrap(); builders.push(builder_v2_2); - let builder_v2_2 = builders.into_iter().reduce( - |acc, mut cur| { - cur.checked_append_reference(acc.into_cell().unwrap()).unwrap(); + let builder_v2_2 = builders + .into_iter() + .reduce(|acc, mut cur| { + cur.checked_append_reference(acc.into_cell().unwrap()) + .unwrap(); cur - }).unwrap(); - - addresses.iter().take(5).for_each(|address| address.write_to(&mut builder).unwrap()); - builder.checked_append_reference(addresses.last().unwrap().serialize().unwrap()).unwrap(); + }) + .unwrap(); - let mut values = vec![TokenValue::Cell(BuilderData::with_bitstring(vec![1, 2, 3, 0x80]).unwrap().into_cell().unwrap())]; + addresses + .iter() + .take(5) + .for_each(|address| address.write_to(&mut builder).unwrap()); + builder + .checked_append_reference(addresses.last().unwrap().serialize().unwrap()) + .unwrap(); + + let mut values = vec![TokenValue::Cell( + BuilderData::with_bitstring(vec![1, 2, 3, 0x80]) + .unwrap() + .into_cell() + .unwrap(), + )]; addresses.iter().for_each(|address| { values.push(TokenValue::Address(address.clone())); }); @@ -201,8 +252,11 @@ fn test_one_input_and_output_by_data() { // test prefix with one ref and u32 let mut expected_tree = BuilderData::with_bitstring(vec![ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x75, 0x0C, 0xE4, 0x7B, 0xAC, 0x80, - ]).unwrap(); - expected_tree.checked_append_reference(Cell::default()).unwrap(); + ]) + .unwrap(); + expected_tree + .checked_append_reference(Cell::default()) + .unwrap(); let values = vec![TokenValue::Int(Int { number: BigInt::from(-596784153684i64), @@ -262,9 +316,18 @@ fn test_two_params() { fn test_five_refs_v1() { let bytes = vec![0x55; 300]; // 300 = 127 + 127 + 46 let mut builder = BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap(); - builder.checked_append_reference(BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference( + BuilderData::with_raw(vec![0x55; 127], 127 * 8) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); let mut bytes_builder = BuilderData::with_raw(vec![0x55; 46], 46 * 8).unwrap(); - bytes_builder.checked_append_reference(builder.into_cell().unwrap()).unwrap(); + bytes_builder + .checked_append_reference(builder.into_cell().unwrap()) + .unwrap(); // test prefix with one ref and u32 let mut builder = BuilderData::new(); @@ -272,14 +335,24 @@ fn test_five_refs_v1() { builder.checked_append_reference(Cell::default()).unwrap(); builder.append_bit_one().unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); let mut new_builder = BuilderData::new(); new_builder.append_i32(9434567).unwrap(); - new_builder.checked_append_reference(Cell::default()).unwrap(); - new_builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(new_builder.into_cell().unwrap()).unwrap(); + new_builder + .checked_append_reference(Cell::default()) + .unwrap(); + new_builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(new_builder.into_cell().unwrap()) + .unwrap(); let values = vec![ TokenValue::Bool(true), @@ -298,14 +371,22 @@ fn test_five_refs_v1() { ); } - #[test] fn test_five_refs_v2() { let bytes = vec![0x55; 300]; // 300 = 127 + 127 + 46 let mut builder = BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap(); - builder.checked_append_reference(BuilderData::with_raw(vec![0x55; 46], 46 * 8).unwrap().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference( + BuilderData::with_raw(vec![0x55; 46], 46 * 8) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); let mut bytes_builder = BuilderData::with_raw(vec![0x55; 127], 127 * 8).unwrap(); - bytes_builder.checked_append_reference(builder.into_cell().unwrap()).unwrap(); + bytes_builder + .checked_append_reference(builder.into_cell().unwrap()) + .unwrap(); // test prefix with one ref and u32 let mut builder = BuilderData::new(); @@ -313,14 +394,24 @@ fn test_five_refs_v2() { builder.checked_append_reference(Cell::default()).unwrap(); builder.append_bit_one().unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); let mut new_builder = BuilderData::new(); new_builder.append_i32(9434567).unwrap(); - new_builder.checked_append_reference(Cell::default()).unwrap(); - new_builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(new_builder.into_cell().unwrap()).unwrap(); + new_builder + .checked_append_reference(Cell::default()) + .unwrap(); + new_builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(new_builder.into_cell().unwrap()) + .unwrap(); let values = vec![ TokenValue::Bool(true), @@ -346,7 +437,6 @@ fn test_nested_tuples_with_all_simples() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.append_bit_zero().unwrap(); builder.append_i8(-15 as i8).unwrap(); builder.append_i16(9845 as i16).unwrap(); @@ -501,7 +591,7 @@ fn test_dynamic_array_of_tuples() { add_array_as_map(&mut builder, &bitstring_array, false); let expected_tree = builder.into(); - + let values = vec![TokenValue::Array( ParamType::Tuple(vec![ Param::new("a", ParamType::Uint(32)), @@ -543,12 +633,13 @@ fn test_tuples_with_combined_types() { // test prefix with one ref and u32 let mut chain_builder = BuilderData::new(); chain_builder.append_u32(0).unwrap(); - chain_builder.checked_append_reference(Cell::default()).unwrap(); + chain_builder + .checked_append_reference(Cell::default()) + .unwrap(); // u8 chain_builder.append_u8(18).unwrap(); - // Vec<(u32, bool)> add_array_as_map(&mut chain_builder, &bitstring_array1, false); @@ -570,13 +661,19 @@ fn test_tuples_with_combined_types() { let mut chain_builder_v2 = chain_builder.clone(); chain_builder_v2.append_bit_one().unwrap(); - chain_builder_v2.checked_append_reference(map.data().unwrap().clone()).unwrap(); + chain_builder_v2 + .checked_append_reference(map.data().unwrap().clone()) + .unwrap(); let mut second_builder = BuilderData::new(); second_builder.append_bit_one().unwrap(); - second_builder.checked_append_reference(map.data().unwrap().clone()).unwrap(); + second_builder + .checked_append_reference(map.data().unwrap().clone()) + .unwrap(); - chain_builder.checked_append_reference(second_builder.into_cell().unwrap()).unwrap(); + chain_builder + .checked_append_reference(second_builder.into_cell().unwrap()) + .unwrap(); let array1_token_value = TokenValue::Array( ParamType::Tuple(vec![ @@ -610,7 +707,8 @@ fn test_tuples_with_combined_types() { array2_token_value.clone(), array2_token_value.clone(), array2_token_value.clone(), - ]); + ], + ); let values = vec![ TokenValue::Uint(Uint::new(18, 8)), @@ -649,11 +747,17 @@ fn test_four_refs_and_four_int256() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); let mut second_builder = BuilderData::new(); - second_builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + second_builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); second_builder.append_builder(&bytes_builder).unwrap(); second_builder.append_builder(&bytes_builder).unwrap(); second_builder.append_builder(&bytes_builder).unwrap(); @@ -661,17 +765,33 @@ fn test_four_refs_and_four_int256() { let mut third_builder = BuilderData::new(); third_builder.append_builder(&bytes_builder).unwrap(); - second_builder.checked_append_reference(third_builder.into_cell().unwrap()).unwrap(); - builder.checked_append_reference(second_builder.into_cell().unwrap()).unwrap(); + second_builder + .checked_append_reference(third_builder.into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(second_builder.into_cell().unwrap()) + .unwrap(); let values = vec![ TokenValue::Cell(bytes_builder.clone().into_cell().unwrap()), TokenValue::Bytes(bytes.clone()), TokenValue::Cell(bytes_builder.into_cell().unwrap()), - TokenValue::Uint(Uint{ number: BigUint::from_bytes_be(&bytes), size: 256 }), - TokenValue::Uint(Uint{ number: BigUint::from_bytes_be(&bytes), size: 256 }), - TokenValue::Uint(Uint{ number: BigUint::from_bytes_be(&bytes), size: 256 }), - TokenValue::Uint(Uint{ number: BigUint::from_bytes_be(&bytes), size: 256 }), + TokenValue::Uint(Uint { + number: BigUint::from_bytes_be(&bytes), + size: 256, + }), + TokenValue::Uint(Uint { + number: BigUint::from_bytes_be(&bytes), + size: 256, + }), + TokenValue::Uint(Uint { + number: BigUint::from_bytes_be(&bytes), + size: 256, + }), + TokenValue::Uint(Uint { + number: BigUint::from_bytes_be(&bytes), + size: 256, + }), ]; test_parameters_set( @@ -692,24 +812,37 @@ fn test_four_refs_and_one_int256() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); let mut builder_v2 = builder.clone(); - builder_v2.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + builder_v2 + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); builder_v2.append_builder(&bytes_builder).unwrap(); let mut second_builder = BuilderData::new(); - second_builder.checked_append_reference(bytes_builder.clone().into_cell().unwrap()).unwrap(); + second_builder + .checked_append_reference(bytes_builder.clone().into_cell().unwrap()) + .unwrap(); second_builder.append_builder(&bytes_builder).unwrap(); - builder.checked_append_reference(second_builder.into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(second_builder.into_cell().unwrap()) + .unwrap(); let values = vec![ TokenValue::Cell(bytes_builder.clone().into_cell().unwrap()), TokenValue::Bytes(bytes.clone()), TokenValue::Cell(bytes_builder.into_cell().unwrap()), - TokenValue::Uint(Uint{ number: BigUint::from_bytes_be(&bytes), size: 256 }), + TokenValue::Uint(Uint { + number: BigUint::from_bytes_be(&bytes), + size: 256, + }), ]; test_parameters_set( @@ -734,11 +867,14 @@ fn test_header_params() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - let public_key = ed25519_dalek::PublicKey::from_bytes(&[0u8; ed25519_dalek::PUBLIC_KEY_LENGTH]).unwrap(); + let public_key = + ed25519_dalek::PublicKey::from_bytes(&[0u8; ed25519_dalek::PUBLIC_KEY_LENGTH]).unwrap(); builder.append_bit_zero().unwrap(); builder.append_bit_one().unwrap(); - builder.append_raw(&public_key.to_bytes(), ed25519_dalek::PUBLIC_KEY_LENGTH * 8).unwrap(); + builder + .append_raw(&public_key.to_bytes(), ed25519_dalek::PUBLIC_KEY_LENGTH * 8) + .unwrap(); builder.append_u64(12345).unwrap(); builder.append_u32(67890).unwrap(); @@ -746,7 +882,7 @@ fn test_header_params() { TokenValue::PublicKey(None), TokenValue::PublicKey(Some(public_key)), TokenValue::Time(12345), - TokenValue::Expire(67890) + TokenValue::Expire(67890), ]; test_parameters_set( @@ -773,7 +909,9 @@ fn test_map() { let bytes = vec![0x55; 32]; let bytes_builder = BuilderData::with_raw(bytes.clone(), bytes.len() * 8).unwrap(); let mut builder = BuilderData::new(); - builder.checked_append_reference(bytes_builder.into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(bytes_builder.into_cell().unwrap()) + .unwrap(); let bytes_map = vec_to_map( &vec![ @@ -781,47 +919,56 @@ fn test_map() { (2u8, builder.clone()), (3u8, builder.clone()), ], - 8 + 8, ); let bytes_value = TokenValue::Map( ParamType::Uint(8), ParamType::Bytes, - BTreeMap::from_iter( - vec![ - ("1".to_owned(), TokenValue::Bytes(bytes.clone())), - ("2".to_owned(), TokenValue::Bytes(bytes.clone())), - ("3".to_owned(), TokenValue::Bytes(bytes.clone())), - ] - ) + BTreeMap::from_iter(vec![ + ("1".to_owned(), TokenValue::Bytes(bytes.clone())), + ("2".to_owned(), TokenValue::Bytes(bytes.clone())), + ("3".to_owned(), TokenValue::Bytes(bytes.clone())), + ]), ); let int_map = vec_to_map( &vec![ - (-1i16, BuilderData::with_raw((-1i128).to_be_bytes().to_vec(), 128).unwrap()), - (0i16, BuilderData::with_raw(0i128.to_be_bytes().to_vec(), 128).unwrap()), - (1i16, BuilderData::with_raw(1i128.to_be_bytes().to_vec(), 128).unwrap()), + ( + -1i16, + BuilderData::with_raw((-1i128).to_be_bytes().to_vec(), 128).unwrap(), + ), + ( + 0i16, + BuilderData::with_raw(0i128.to_be_bytes().to_vec(), 128).unwrap(), + ), + ( + 1i16, + BuilderData::with_raw(1i128.to_be_bytes().to_vec(), 128).unwrap(), + ), ], - 16 + 16, ); let int_value = TokenValue::Map( ParamType::Int(16), ParamType::Int(128), - BTreeMap::from_iter( - vec![ - ("-1".to_owned(), TokenValue::Int(Int::new(-1, 128))), - ("0".to_owned(), TokenValue::Int(Int::new(0, 128))), - ("1".to_owned(), TokenValue::Int(Int::new(1, 128))), - ] - ) + BTreeMap::from_iter(vec![ + ("-1".to_owned(), TokenValue::Int(Int::new(-1, 128))), + ("0".to_owned(), TokenValue::Int(Int::new(0, 128))), + ("1".to_owned(), TokenValue::Int(Int::new(1, 128))), + ]), ); let tuples_array: Vec<(u32, bool)> = vec![(1, true), (2, false), (3, true), (4, false), (5, true)]; - let bitstring_array: Vec<(u128, BuilderData)> = tuples_array .iter() - .map(|a| (a.0 as u128, TupleDwordBool::from(a).write_to_new_cell().unwrap())) + .map(|a| { + ( + a.0 as u128, + TupleDwordBool::from(a).write_to_new_cell().unwrap(), + ) + }) .collect(); let tuples_map = vec_to_map(&bitstring_array, 128); @@ -832,19 +979,15 @@ fn test_map() { Param::new("a", ParamType::Uint(32)), Param::new("b", ParamType::Bool), ]), - BTreeMap::from_iter( - tuples_array - .iter() - .map(|i| { - ( - i.0.to_string(), - TokenValue::Tuple(tokens_from_values(vec![ - TokenValue::Uint(Uint::new(i.0 as u128, 32)), - TokenValue::Bool(i.1), - ])) - ) - }), - ) + BTreeMap::from_iter(tuples_array.iter().map(|i| { + ( + i.0.to_string(), + TokenValue::Tuple(tokens_from_values(vec![ + TokenValue::Uint(Uint::new(i.0 as u128, 32)), + TokenValue::Bool(i.1), + ])), + ) + })), ); // test prefix with one ref and u32 @@ -852,23 +995,33 @@ fn test_map() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.append_builder(&bytes_map.write_to_new_cell().unwrap()).unwrap(); - builder.append_builder(&int_map.write_to_new_cell().unwrap()).unwrap(); + builder + .append_builder(&bytes_map.write_to_new_cell().unwrap()) + .unwrap(); + builder + .append_builder(&int_map.write_to_new_cell().unwrap()) + .unwrap(); let mut builder_v2 = builder.clone(); - builder_v2.append_builder(&tuples_map.write_to_new_cell().unwrap()).unwrap(); + builder_v2 + .append_builder(&tuples_map.write_to_new_cell().unwrap()) + .unwrap(); builder_v2.append_bit_zero().unwrap(); let mut second_builder = BuilderData::new(); - second_builder.append_builder(&tuples_map.write_to_new_cell().unwrap()).unwrap(); + second_builder + .append_builder(&tuples_map.write_to_new_cell().unwrap()) + .unwrap(); second_builder.append_bit_zero().unwrap(); - builder.checked_append_reference(second_builder.into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(second_builder.into_cell().unwrap()) + .unwrap(); let values = vec![ bytes_value, int_value, tuples_value, - TokenValue::Map(ParamType::Int(256), ParamType::Bool, BTreeMap::new()) + TokenValue::Map(ParamType::Int(256), ParamType::Bool, BTreeMap::new()), ]; test_parameters_set( @@ -886,30 +1039,35 @@ fn test_map() { ); } - #[test] +#[test] fn test_address_map_key() { - let addr1_str = "0:1111111111111111111111111111111111111111111111111111111111111111"; - let addr2_str = "0:2222222222222222222222222222222222222222222222222222222222222222"; + let addr1_str = "0:1111111111111111111111111111111111111111111111111111111111111111"; + let addr2_str = "0:2222222222222222222222222222222222222222222222222222222222222222"; let addr1 = MsgAddress::from_str(addr1_str).unwrap(); let addr2 = MsgAddress::from_str(addr2_str).unwrap(); let map = vec_to_map( &vec![ - (addr1, BuilderData::with_raw((123u32).to_be_bytes().to_vec(), 32).unwrap()), - (addr2, BuilderData::with_raw((456u32).to_be_bytes().to_vec(), 32).unwrap()), + ( + addr1, + BuilderData::with_raw((123u32).to_be_bytes().to_vec(), 32).unwrap(), + ), + ( + addr2, + BuilderData::with_raw((456u32).to_be_bytes().to_vec(), 32).unwrap(), + ), ], - crate::token::STD_ADDRESS_BIT_LENGTH); + crate::token::STD_ADDRESS_BIT_LENGTH, + ); let value = TokenValue::Map( ParamType::Address, ParamType::Uint(32), - BTreeMap::from_iter( - vec![ - (addr1_str.to_owned(), TokenValue::Uint(Uint::new(123, 32))), - (addr2_str.to_owned(), TokenValue::Uint(Uint::new(456, 32))), - ] - ) + BTreeMap::from_iter(vec![ + (addr1_str.to_owned(), TokenValue::Uint(Uint::new(123, 32))), + (addr2_str.to_owned(), TokenValue::Uint(Uint::new(456, 32))), + ]), ); // test prefix with one ref and u32 @@ -917,7 +1075,9 @@ fn test_address_map_key() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.append_builder(&map.write_to_new_cell().unwrap()).unwrap(); + builder + .append_builder(&map.write_to_new_cell().unwrap()) + .unwrap(); test_parameters_set( &tokens_from_values(vec![value]), @@ -925,17 +1085,17 @@ fn test_address_map_key() { builder, &[ABI_VERSION_1_0, ABI_VERSION_2_0, ABI_VERSION_2_2], ); - } +} - #[test] - fn test_big_map_value() { +#[test] +fn test_big_map_value() { let mut map = HashmapE::with_bit_len(256); let mut array = HashmapE::with_bit_len(32); - + let mut map_value_ref = BuilderData::new(); map_value_ref.append_u128(0).unwrap(); map_value_ref.append_u128(4).unwrap(); - + let mut map_value = BuilderData::new(); map_value.append_u128(0).unwrap(); map_value.append_u128(1).unwrap(); @@ -943,20 +1103,25 @@ fn test_address_map_key() { map_value.append_u128(2).unwrap(); map_value.append_u128(0).unwrap(); map_value.append_u128(3).unwrap(); - map_value.checked_append_reference(map_value_ref.into_cell().unwrap()).unwrap(); + map_value + .checked_append_reference(map_value_ref.into_cell().unwrap()) + .unwrap(); let mut map_key = BuilderData::new(); map_key.append_u128(0).unwrap(); map_key.append_u128(123).unwrap(); let map_key = SliceData::load_builder(map_key).unwrap(); - map.setref(map_key, &map_value.clone().into_cell().unwrap()).unwrap(); + map.setref(map_key, &map_value.clone().into_cell().unwrap()) + .unwrap(); let mut array_key = BuilderData::new(); array_key.append_u32(0).unwrap(); let array_key = SliceData::load_builder(array_key).unwrap(); - array.setref(array_key, &map_value.into_cell().unwrap()).unwrap(); + array + .setref(array_key, &map_value.into_cell().unwrap()) + .unwrap(); let tuple_tokens = tokens_from_values(vec![ TokenValue::Uint(Uint::new(1, 256)), @@ -969,17 +1134,15 @@ fn test_address_map_key() { let value_map = TokenValue::Map( ParamType::Uint(256), ParamType::Tuple(params_from_tokens(&tuple_tokens)), - BTreeMap::from_iter( - vec![( - "0x000000000000000000000000000000000000000000000000000000000000007b".to_owned(), - tuple.clone() - )] - ) + BTreeMap::from_iter(vec![( + "0x000000000000000000000000000000000000000000000000000000000000007b".to_owned(), + tuple.clone(), + )]), ); let value_array = TokenValue::Array( ParamType::Tuple(params_from_tokens(&tuple_tokens)), - vec![tuple] + vec![tuple], ); // test prefix with one ref and u32 @@ -987,9 +1150,13 @@ fn test_address_map_key() { builder.append_u32(0).unwrap(); builder.checked_append_reference(Cell::default()).unwrap(); - builder.append_builder(&map.write_to_new_cell().unwrap()).unwrap(); + builder + .append_builder(&map.write_to_new_cell().unwrap()) + .unwrap(); builder.append_u32(1).unwrap(); - builder.append_builder(&array.write_to_new_cell().unwrap()).unwrap(); + builder + .append_builder(&array.write_to_new_cell().unwrap()) + .unwrap(); test_parameters_set( &tokens_from_values(vec![value_map, value_array]), @@ -997,14 +1164,13 @@ fn test_address_map_key() { builder, &[ABI_VERSION_2_0, ABI_VERSION_2_2], ); - } +} - #[test] +#[test] fn test_abi_2_1_types() { let string = "Some string"; - let string_builder = BuilderData::with_raw( - string.as_bytes().to_vec(), string.as_bytes().len() * 8 - ).unwrap(); + let string_builder = + BuilderData::with_raw(string.as_bytes().to_vec(), string.as_bytes().len() * 8).unwrap(); let string_value = TokenValue::String(string.into()); let tuple_tokens = tokens_from_values(vec![ @@ -1021,17 +1187,15 @@ fn test_abi_2_1_types() { TokenValue::Optional(ParamType::Bool, None), TokenValue::Optional( ParamType::Uint(1022), - Some(Box::new( - TokenValue::Uint(Uint::new(1, 1022)) - ))), + Some(Box::new(TokenValue::Uint(Uint::new(1, 1022)))), + ), TokenValue::Optional( ParamType::VarUint(128), - Some(Box::new( - TokenValue::VarUint(128, 0u32.into()) - ))), + Some(Box::new(TokenValue::VarUint(128, 0u32.into()))), + ), TokenValue::Optional( ParamType::Tuple(params_from_tokens(&tuple_tokens)), - Some(Box::new(tuple)) + Some(Box::new(tuple)), ), ]; @@ -1055,11 +1219,9 @@ fn test_abi_2_1_types() { let mut varuint_builder = BuilderData::new(); varuint_builder.append_raw(&[0], 7).unwrap(); - let mut varuint_builder = BuilderData::with_raw_and_refs( - vec![0x80], - 1, - vec![varuint_builder.into_cell().unwrap()] - ).unwrap(); + let mut varuint_builder = + BuilderData::with_raw_and_refs(vec![0x80], 1, vec![varuint_builder.into_cell().unwrap()]) + .unwrap(); let tuple_builder = BuilderData::with_raw_and_refs( vec![], @@ -1069,17 +1231,20 @@ fn test_abi_2_1_types() { string_builder.clone().into_cell().unwrap(), string_builder.clone().into_cell().unwrap(), string_builder.clone().into_cell().unwrap(), - ] - ).unwrap(); - let tuple_builder = BuilderData::with_raw_and_refs( - vec![0x80], - 1, - vec![tuple_builder.into_cell().unwrap()] - ).unwrap(); + ], + ) + .unwrap(); + let tuple_builder = + BuilderData::with_raw_and_refs(vec![0x80], 1, vec![tuple_builder.into_cell().unwrap()]) + .unwrap(); varuint_builder.append_builder(&tuple_builder).unwrap(); - uint_builder.checked_append_reference(varuint_builder.into_cell().unwrap()).unwrap(); - builder.checked_append_reference(uint_builder.into_cell().unwrap()).unwrap(); + uint_builder + .checked_append_reference(varuint_builder.into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(uint_builder.into_cell().unwrap()) + .unwrap(); test_parameters_set( &tokens_from_values(values), @@ -1087,9 +1252,9 @@ fn test_abi_2_1_types() { builder, &[ABI_VERSION_2_1, ABI_VERSION_2_2], ); - } +} - #[test] +#[test] fn test_ref_type() { // test prefix with one ref and u32 let mut builder = BuilderData::new(); @@ -1098,10 +1263,16 @@ fn test_ref_type() { let mut ref_builder = BuilderData::new(); ref_builder.append_bit_one().unwrap(); - ref_builder.checked_append_reference(Cell::default()).unwrap(); + ref_builder + .checked_append_reference(Cell::default()) + .unwrap(); - builder.checked_append_reference(123u64.serialize().unwrap()).unwrap(); - builder.checked_append_reference(ref_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(123u64.serialize().unwrap()) + .unwrap(); + builder + .checked_append_reference(ref_builder.clone().into_cell().unwrap()) + .unwrap(); let values = vec![ TokenValue::Ref(Box::new(TokenValue::Int(Int::new(123, 64)))), @@ -1123,7 +1294,9 @@ fn test_ref_type() { fn test_partial_decoding() { let mut builder = BuilderData::new(); builder.append_u32(0).unwrap(); - builder.checked_append_reference(123u64.serialize().unwrap()).unwrap(); + builder + .checked_append_reference(123u64.serialize().unwrap()) + .unwrap(); builder.append_bit_one().unwrap(); let slice = SliceData::load_builder(builder).unwrap(); @@ -1133,14 +1306,18 @@ fn test_partial_decoding() { Param::new("c", ParamType::Bool), ]; - assert!(TokenValue::decode_params(¶ms, slice.clone(), &MAX_SUPPORTED_VERSION, false).is_err()); + assert!( + TokenValue::decode_params(¶ms, slice.clone(), &MAX_SUPPORTED_VERSION, false).is_err() + ); let params = vec![ Param::new("a", ParamType::Uint(32)), Param::new("b", ParamType::Ref(Box::new(ParamType::Int(64)))), ]; - assert!(TokenValue::decode_params(¶ms, slice.clone(), &MAX_SUPPORTED_VERSION, false).is_err()); + assert!( + TokenValue::decode_params(¶ms, slice.clone(), &MAX_SUPPORTED_VERSION, false).is_err() + ); assert_eq!( TokenValue::decode_params(¶ms, slice, &MAX_SUPPORTED_VERSION, true).unwrap(), @@ -1154,17 +1331,19 @@ fn test_partial_decoding() { #[test] fn test_four_optional_strings() { let string = "Some string"; - let string_builder = BuilderData::with_raw( - string.as_bytes().to_vec(), string.as_bytes().len() * 8 - ).unwrap(); - let string_some_value = TokenValue::Optional(ParamType::String, Some(Box::new(TokenValue::String(string.into())))); + let string_builder = + BuilderData::with_raw(string.as_bytes().to_vec(), string.as_bytes().len() * 8).unwrap(); + let string_some_value = TokenValue::Optional( + ParamType::String, + Some(Box::new(TokenValue::String(string.into()))), + ); let string_none_value = TokenValue::Optional(ParamType::String, None); let values = vec![ string_none_value.clone(), string_some_value.clone(), string_none_value, - string_some_value + string_some_value, ]; // test prefix with one ref and u32 @@ -1173,17 +1352,20 @@ fn test_four_optional_strings() { builder.checked_append_reference(Cell::default()).unwrap(); builder.append_bits(1, 2).unwrap(); - builder.checked_append_reference(string_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(string_builder.clone().into_cell().unwrap()) + .unwrap(); let second_builder = BuilderData::with_raw_and_refs( vec![0x40], 2, - vec![ - string_builder.clone().into_cell().unwrap(), - ] - ).unwrap(); + vec![string_builder.clone().into_cell().unwrap()], + ) + .unwrap(); - builder.checked_append_reference(second_builder.into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(second_builder.into_cell().unwrap()) + .unwrap(); test_parameters_set( &tokens_from_values(values.clone()), @@ -1197,8 +1379,12 @@ fn test_four_optional_strings() { builder.checked_append_reference(Cell::default()).unwrap(); builder.append_bits(5, 4).unwrap(); - builder.checked_append_reference(string_builder.clone().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(string_builder.clone().into_cell().unwrap()).unwrap(); + builder + .checked_append_reference(string_builder.clone().into_cell().unwrap()) + .unwrap(); + builder + .checked_append_reference(string_builder.clone().into_cell().unwrap()) + .unwrap(); test_parameters_set( &tokens_from_values(values), @@ -1206,4 +1392,123 @@ fn test_four_optional_strings() { builder, &[ABI_VERSION_2_1], ); - } +} + +#[test] +fn test_default_values() { + let param_type = ParamType::Tuple(params_from_types( + [ + ParamType::Address, + ParamType::Array(Box::new(ParamType::Uint(32))), + ParamType::Bool, + ParamType::Bytes, + ParamType::Cell, + ParamType::Expire, + ParamType::FixedArray(Box::new(ParamType::Bool), 5), + ParamType::FixedBytes(3), + ParamType::Int(10), + ParamType::Map(Box::new(ParamType::Address), Box::new(ParamType::VarInt(6))), + ParamType::Optional(Box::new(ParamType::Address)), + ParamType::PublicKey, + ParamType::Ref(Box::new(ParamType::Int(15))), + ParamType::String, + ParamType::Time, + ParamType::Token, + ParamType::Uint(1), + ParamType::VarInt(7), + ParamType::VarUint(20), + ] + .to_vec(), + )); + + let default = TokenValue::default_value(¶m_type); + + let encoded = default.pack_into_chain(&MAX_SUPPORTED_VERSION).unwrap(); + + let mut root = BuilderData::new(); + + // ParamType::Address + root.append_bit_zero().unwrap(); + root.append_bit_zero().unwrap(); + + // ParamType::Array(Box::new(ParamType::Uint(32))) + root.append_u32(0).unwrap(); + root.append_bit_zero().unwrap(); + + // ParamType::Bool + root.append_bit_zero().unwrap(); + + // ParamType::Bytes + root.checked_append_reference(Cell::default()).unwrap(); + + // ParamType::Cell + root.checked_append_reference(Cell::default()).unwrap(); + + // ParamType::Expire + root.append_u32(0).unwrap(); + + let mut second = BuilderData::new(); + + // ParamType::FixedArray(Box::new(ParamType::Bool), 5) + add_array_as_map(&mut second, &[false; 5], true); + + // ParamType::FixedBytes(3) + second + .checked_append_reference( + BuilderData::with_raw([0u8; 3].as_slice(), 3 * 8) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); + + // ParamType::Int(10) + second.append_raw(&[0u8; 2], 10).unwrap(); + + // ParamType::Map(Box::new(ParamType::Address), Box::new(ParamType::VarInt(6))) + second.append_bit_zero().unwrap(); + + // ParamType::Optional(Box::new(ParamType::Address)) + second.append_bit_zero().unwrap(); + + // ParamType::PublicKey + second.append_bit_zero().unwrap(); + + let mut third = BuilderData::new(); + + // ParamType::Ref(Box::new(ParamType::Int(15))) + third + .checked_append_reference( + BuilderData::with_raw([0u8; 2].as_slice(), 15) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); + + // ParamType::String + third.checked_append_reference(Cell::default()).unwrap(); + + // ParamType::Time + third.append_u64(0).unwrap(); + + // ParamType::Token + third.append_raw(&[0u8], 4).unwrap(); + + // ParamType::Uint(1) + third.append_bit_zero().unwrap(); + + // ParamType::VarInt(7) + third.append_raw(&[0], 3).unwrap(); + + // ParamType::VarUint(20) + third.append_raw(&[0; 2], 5).unwrap(); + + second + .checked_append_reference(third.into_cell().unwrap()) + .unwrap(); + root.checked_append_reference(second.into_cell().unwrap()) + .unwrap(); + + assert_eq!(encoded, root); +} diff --git a/src/token/tests.rs b/src/token/tests.rs index 69e51442..60f8d320 100644 --- a/src/token/tests.rs +++ b/src/token/tests.rs @@ -14,7 +14,7 @@ mod tokenize_tests { use crate::{Int, Param, ParamType, Token, TokenValue, Uint}; // use serde::Serialize; - use std::collections::{BTreeMap, HashMap}; + use std::collections::BTreeMap; use token::{Detokenizer, Tokenizer}; use ton_block::{Grams, MsgAddress}; use ton_types::{AccountId, BuilderData, Cell, SliceData}; @@ -109,7 +109,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -123,7 +124,9 @@ mod tokenize_tests { kind: ParamType::Uint(7), }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err() + ); // number doesn't fit into i64 range used in serde_json let input = r#"{ "a" : 12345678900987654321 }"#; @@ -132,7 +135,9 @@ mod tokenize_tests { kind: ParamType::Int(64), }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err() + ); // test BigInt::bits() case for -2^n values @@ -144,12 +149,14 @@ mod tokenize_tests { }]; assert!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_fit).unwrap()).is_ok() - ); - assert!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_not_fit).unwrap()) - .is_err() + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_fit).unwrap()) + .is_ok() ); + assert!(Tokenizer::tokenize_all_params( + ¶ms, + &serde_json::from_str(input_not_fit).unwrap() + ) + .is_err()); // negative values for uint let input_num = r#"{ "a" : -1 }"#; @@ -159,8 +166,14 @@ mod tokenize_tests { kind: ParamType::Uint(8), }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()).is_err()); - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()) + .is_err() + ); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()) + .is_err() + ); // varint max check let input = r#"{ "a" : "0xffffffffffffffffffffffffffffffff" }"#; @@ -169,7 +182,9 @@ mod tokenize_tests { kind: ParamType::VarInt(16), }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err() + ); // negative values for varuint let input_num = r#"{ "a" : -1 }"#; @@ -179,8 +194,14 @@ mod tokenize_tests { kind: ParamType::VarUint(8), }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()).is_err()); - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()) + .is_err() + ); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()) + .is_err() + ); } #[test] @@ -221,7 +242,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -243,7 +265,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -299,7 +322,8 @@ mod tokenize_tests { vec![ TokenValue::Array(ParamType::Bool, bool_array1), TokenValue::Array(ParamType::Bool, bool_array2), - ]), + ], + ), }, ]; @@ -312,7 +336,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -387,10 +412,11 @@ mod tokenize_tests { value: TokenValue::Array( ParamType::Int(16), vec![ - TokenValue::Int(Int::new(-123, 16)), - TokenValue::Int(Int::new(456, 16)), + TokenValue::Int(Int::new(-123, 16)), + TokenValue::Int(Int::new(456, 16)), TokenValue::Int(Int::new(0x789, 16)), - ]), + ], + ), }, Token { name: "b".to_owned(), @@ -413,7 +439,7 @@ mod tokenize_tests { Param { name: "b".to_owned(), kind: ParamType::Int(8), - } + }, ]), vec![ TokenValue::Tuple(vec![ @@ -446,7 +472,8 @@ mod tokenize_tests { value: TokenValue::Int(Int::new(0x56, 8)), }, ]), - ]), + ], + ), }, ]; @@ -459,7 +486,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -478,9 +506,26 @@ mod tokenize_tests { let mut expected_tokens = vec![]; let mut builder = BuilderData::with_bitstring(vec![1, 2, 3, 4, 5, 6, 7, 8, 0x80]).unwrap(); - builder.checked_append_reference(BuilderData::with_bitstring(vec![11, 12, 13, 14, 15, 16, 17, 18, 0x80]).unwrap().into_cell().unwrap()).unwrap(); - builder.checked_append_reference(BuilderData::with_bitstring(vec![21, 22, 23, 24, 25, 26, 27, 28, 0x80]).unwrap().into_cell().unwrap()).unwrap(); - expected_tokens.push(Token::new("a", TokenValue::Cell(builder.into_cell().unwrap()))); + builder + .checked_append_reference( + BuilderData::with_bitstring(vec![11, 12, 13, 14, 15, 16, 17, 18, 0x80]) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); + builder + .checked_append_reference( + BuilderData::with_bitstring(vec![21, 22, 23, 24, 25, 26, 27, 28, 0x80]) + .unwrap() + .into_cell() + .unwrap(), + ) + .unwrap(); + expected_tokens.push(Token::new( + "a", + TokenValue::Cell(builder.into_cell().unwrap()), + )); expected_tokens.push(Token::new("b", TokenValue::Cell(Cell::default()))); assert_eq!( @@ -492,7 +537,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -525,56 +571,99 @@ mod tokenize_tests { }"#; let params = vec![ - Param::new("a", ParamType::Map(Box::new(ParamType::Int(8)), Box::new(ParamType::Uint(32)))), - Param::new("b", ParamType::Map(Box::new(ParamType::Uint(32)), Box::new(ParamType::Uint(32)))), - Param::new("c", ParamType::Map(Box::new(ParamType::Int(8)), Box::new(ParamType::Tuple(vec![ - Param::new("q1", ParamType::Uint(32)), Param::new("q2", ParamType::Int(8)) - ])))), - Param::new("d", ParamType::Map(Box::new(ParamType::Address), Box::new(ParamType::Uint(32)))), + Param::new( + "a", + ParamType::Map(Box::new(ParamType::Int(8)), Box::new(ParamType::Uint(32))), + ), + Param::new( + "b", + ParamType::Map(Box::new(ParamType::Uint(32)), Box::new(ParamType::Uint(32))), + ), + Param::new( + "c", + ParamType::Map( + Box::new(ParamType::Int(8)), + Box::new(ParamType::Tuple(vec![ + Param::new("q1", ParamType::Uint(32)), + Param::new("q2", ParamType::Int(8)), + ])), + ), + ), + Param::new( + "d", + ParamType::Map(Box::new(ParamType::Address), Box::new(ParamType::Uint(32))), + ), ]; let mut expected_tokens = vec![]; let mut map = BTreeMap::::new(); - map.insert(format!("{}", -12i8), TokenValue::Uint(Uint::new(42, 32))); - map.insert(format!("{}", 127i8), TokenValue::Uint(Uint::new(37, 32))); + map.insert(format!("{}", -12i8), TokenValue::Uint(Uint::new(42, 32))); + map.insert(format!("{}", 127i8), TokenValue::Uint(Uint::new(37, 32))); map.insert(format!("{}", -128i8), TokenValue::Uint(Uint::new(56, 32))); - expected_tokens.push(Token::new("a", TokenValue::Map(ParamType::Int(8), ParamType::Uint(32), map))); + expected_tokens.push(Token::new( + "a", + TokenValue::Map(ParamType::Int(8), ParamType::Uint(32), map), + )); let mut map = BTreeMap::::new(); - map.insert(format!("{}", 0xFFFFFFFFu32), TokenValue::Uint(Uint::new(777, 32))); - map.insert(format!("{}", 0x0000FFFFu32), TokenValue::Uint(Uint::new( 0, 32))); - expected_tokens.push(Token::new("b", TokenValue::Map(ParamType::Uint(32), ParamType::Uint(32), map))); - + map.insert( + format!("{}", 0xFFFFFFFFu32), + TokenValue::Uint(Uint::new(777, 32)), + ); + map.insert( + format!("{}", 0x0000FFFFu32), + TokenValue::Uint(Uint::new(0, 32)), + ); + expected_tokens.push(Token::new( + "b", + TokenValue::Map(ParamType::Uint(32), ParamType::Uint(32), map), + )); let mut map = BTreeMap::::new(); - map.insert(format!("{}", 1i8), TokenValue::Tuple(vec![ - Token::new("q1", TokenValue::Uint(Uint::new(314, 32))), - Token::new("q2", TokenValue::Int(Int::new(15, 8))), - ])); - map.insert(format!("{}", 2i8), TokenValue::Tuple(vec![ - Token::new("q1", TokenValue::Uint(Uint::new(92, 32))), - Token::new("q2", TokenValue::Int(Int::new(6, 8))), - ])); - expected_tokens.push(Token::new("c", TokenValue::Map( - ParamType::Int(8), - ParamType::Tuple(vec![ - Param { - name: "q1".to_owned(), - kind: ParamType::Uint(32), - }, - Param { - name: "q2".to_owned(), - kind: ParamType::Int(8), - }, + map.insert( + format!("{}", 1i8), + TokenValue::Tuple(vec![ + Token::new("q1", TokenValue::Uint(Uint::new(314, 32))), + Token::new("q2", TokenValue::Int(Int::new(15, 8))), ]), - map - ))); + ); + map.insert( + format!("{}", 2i8), + TokenValue::Tuple(vec![ + Token::new("q1", TokenValue::Uint(Uint::new(92, 32))), + Token::new("q2", TokenValue::Int(Int::new(6, 8))), + ]), + ); + expected_tokens.push(Token::new( + "c", + TokenValue::Map( + ParamType::Int(8), + ParamType::Tuple(vec![ + Param { + name: "q1".to_owned(), + kind: ParamType::Uint(32), + }, + Param { + name: "q2".to_owned(), + kind: ParamType::Int(8), + }, + ]), + map, + ), + )); let mut map = BTreeMap::::new(); map.insert( - format!("{}", MsgAddress::with_standart(None, 0, AccountId::from([0x11; 32])).unwrap()), - TokenValue::Uint(Uint::new(123, 32))); - expected_tokens.push(Token::new("d", TokenValue::Map(ParamType::Address, ParamType::Uint(32), map))); + format!( + "{}", + MsgAddress::with_standart(None, 0, AccountId::from([0x11; 32])).unwrap() + ), + TokenValue::Uint(Uint::new(123, 32)), + ); + expected_tokens.push(Token::new( + "d", + TokenValue::Map(ParamType::Address, ParamType::Uint(32), map), + )); assert_eq!( Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).unwrap(), @@ -585,7 +674,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -605,13 +695,15 @@ mod tokenize_tests { let expected_tokens = vec![ Token { name: "std".to_owned(), - value: TokenValue::Address(MsgAddress::with_standart( - None, -17, AccountId::from([0x55; 32])).unwrap()) + value: TokenValue::Address( + MsgAddress::with_standart(None, -17, AccountId::from([0x55; 32])).unwrap(), + ), }, Token { name: "var".to_owned(), - value: TokenValue::Address(MsgAddress::with_variant( - None, -177, SliceData::new(vec![0x55, 0x50])).unwrap()) + value: TokenValue::Address( + MsgAddress::with_variant(None, -177, SliceData::new(vec![0x55, 0x50])).unwrap(), + ), }, ]; @@ -624,7 +716,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -658,7 +751,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -688,7 +782,7 @@ mod tokenize_tests { Param { name: "d".to_owned(), kind: ParamType::Time, - } + }, ]; let expected_tokens = vec![ @@ -707,7 +801,7 @@ mod tokenize_tests { Token { name: "d".to_owned(), value: TokenValue::Time(0xffffffffffffffff), - } + }, ]; assert_eq!( @@ -719,12 +813,12 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } - #[test] fn test_time_checks() { // number doesn't fit into parameter size @@ -734,7 +828,9 @@ mod tokenize_tests { kind: ParamType::Time, }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err() + ); // negative values for time let input_num = r#"{ "a" : -1 }"#; @@ -744,8 +840,14 @@ mod tokenize_tests { kind: ParamType::Time, }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()).is_err()); - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()) + .is_err() + ); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()) + .is_err() + ); } #[test] @@ -773,7 +875,7 @@ mod tokenize_tests { Param { name: "d".to_owned(), kind: ParamType::Expire, - } + }, ]; let expected_tokens = vec![ @@ -792,7 +894,7 @@ mod tokenize_tests { Token { name: "d".to_owned(), value: TokenValue::Expire(0xffffffff), - } + }, ]; assert_eq!( @@ -804,7 +906,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -818,7 +921,9 @@ mod tokenize_tests { kind: ParamType::Expire, }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input).unwrap()).is_err() + ); // negative values for expire let input_num = r#"{ "a" : -1 }"#; @@ -828,11 +933,16 @@ mod tokenize_tests { kind: ParamType::Expire, }]; - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()).is_err()); - assert!(Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()).is_err()); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_num).unwrap()) + .is_err() + ); + assert!( + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(input_str).unwrap()) + .is_err() + ); } - #[test] fn test_tokenize_pubkey() { let input = r#"{ @@ -842,12 +952,17 @@ mod tokenize_tests { let params = vec![ Param::new("a", ParamType::PublicKey), - Param::new("b", ParamType::PublicKey) + Param::new("b", ParamType::PublicKey), ]; let expected_tokens = vec![ - Token::new("a", TokenValue::PublicKey(Some(ed25519_dalek::PublicKey::from_bytes(&[0xcc; 32]).unwrap()))), - Token::new("b", TokenValue::PublicKey(None)) + Token::new( + "a", + TokenValue::PublicKey(Some( + ed25519_dalek::PublicKey::from_bytes(&[0xcc; 32]).unwrap(), + )), + ), + Token::new("b", TokenValue::PublicKey(None)), ]; assert_eq!( @@ -859,7 +974,8 @@ mod tokenize_tests { let input = Detokenizer::detokenize(&expected_tokens).unwrap(); println!("{}", input); assert_eq!( - Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()).unwrap(), + Tokenizer::tokenize_all_params(¶ms, &serde_json::from_str(&input).unwrap()) + .unwrap(), expected_tokens ); } @@ -880,7 +996,10 @@ mod tokenize_tests { let expected_tokens = vec![ Token { name: "a".to_owned(), - value: TokenValue::Optional(ParamType::VarUint(32), Some(Box::new(TokenValue::VarUint(32, 123u32.into())))), + value: TokenValue::Optional( + ParamType::VarUint(32), + Some(Box::new(TokenValue::VarUint(32, 123u32.into()))), + ), }, Token { name: "b".to_owned(), @@ -910,12 +1029,13 @@ mod tokenize_tests { let params = vec![ Param::new("a", ParamType::Ref(Box::new(ParamType::VarUint(32)))), - Param::new("b", ParamType::Ref( - Box::new(ParamType::Tuple(vec![ + Param::new( + "b", + ParamType::Ref(Box::new(ParamType::Tuple(vec![ Param::new("c", ParamType::Bool), Param::new("d", ParamType::String), - ])) - )), + ]))), + ), ]; let expected_tokens = vec![ @@ -925,18 +1045,16 @@ mod tokenize_tests { }, Token { name: "b".to_owned(), - value: TokenValue::Ref( - Box::new(TokenValue::Tuple(vec![ - Token { - name: "c".to_owned(), - value: TokenValue::Bool(true), - }, - Token { - name: "d".to_owned(), - value: TokenValue::String("some string".to_owned()), - }, - ])) - ) + value: TokenValue::Ref(Box::new(TokenValue::Tuple(vec![ + Token { + name: "c".to_owned(), + value: TokenValue::Bool(true), + }, + Token { + name: "d".to_owned(), + value: TokenValue::String("some string".to_owned()), + }, + ]))), }, ]; @@ -953,25 +1071,21 @@ mod tokenize_tests { "b": 456 }"#; - let params = vec![ - Param::new("a", ParamType::Time) - ]; + let params = vec![Param::new("a", ParamType::Time)]; - assert!( - Tokenizer::tokenize_optional_params( - ¶ms, - &serde_json::from_str(input).unwrap(), - &HashMap::new() - ).is_err(), - ); + assert!(Tokenizer::tokenize_optional_params( + ¶ms, + &serde_json::from_str(input).unwrap(), + ) + .is_err(),); } } mod types_check_tests { - use {Int, Param, ParamType, Token, TokenValue, Uint}; + use std::collections::BTreeMap; use ton_block::MsgAddress; use ton_types::Cell; - use std::collections::BTreeMap; + use {Int, Param, ParamType, Token, TokenValue, Uint}; #[test] fn test_type_check() { @@ -1013,7 +1127,7 @@ mod types_check_tests { name: "f".to_owned(), value: TokenValue::Array( ParamType::Bool, - vec![TokenValue::Bool(false), TokenValue::Bool(true)] + vec![TokenValue::Bool(false), TokenValue::Bool(true)], ), }, Token { @@ -1023,7 +1137,8 @@ mod types_check_tests { vec![ TokenValue::Int(big_int.clone()), TokenValue::Int(big_int.clone()), - ]), + ], + ), }, Token { name: "j".to_owned(), @@ -1044,43 +1159,47 @@ mod types_check_tests { }, Token { name: "l".to_owned(), - value: TokenValue::Address(MsgAddress::AddrNone) + value: TokenValue::Address(MsgAddress::AddrNone), }, Token { name: "m1".to_owned(), - value: TokenValue::Map(ParamType::Int(8), ParamType::Bool, BTreeMap::::new()) + value: TokenValue::Map( + ParamType::Int(8), + ParamType::Bool, + BTreeMap::::new(), + ), }, Token { name: "m2".to_owned(), - value: TokenValue::Map(ParamType::Int(8), ParamType::Uint(32), map) + value: TokenValue::Map(ParamType::Int(8), ParamType::Uint(32), map), }, Token { name: "n".to_owned(), - value: TokenValue::Bytes(vec![1]) + value: TokenValue::Bytes(vec![1]), }, Token { name: "o".to_owned(), - value: TokenValue::FixedBytes(vec![1, 2, 3]) + value: TokenValue::FixedBytes(vec![1, 2, 3]), }, Token { name: "p".to_owned(), - value: TokenValue::Token(17u64.into()) + value: TokenValue::Token(17u64.into()), }, Token { name: "q".to_owned(), - value: TokenValue::Time(123) + value: TokenValue::Time(123), }, Token { name: "r".to_owned(), - value: TokenValue::Expire(456) + value: TokenValue::Expire(456), }, Token { name: "s".to_owned(), - value: TokenValue::PublicKey(None) + value: TokenValue::PublicKey(None), }, Token { name: "t".to_owned(), - value: TokenValue::String("123".to_owned()) + value: TokenValue::String("123".to_owned()), }, Token { name: "u".to_owned(), @@ -1088,7 +1207,10 @@ mod types_check_tests { }, Token { name: "v".to_owned(), - value: TokenValue::Optional(ParamType::Bool, Some(Box::new(TokenValue::Bool(true)))), + value: TokenValue::Optional( + ParamType::Bool, + Some(Box::new(TokenValue::Bool(true))), + ), }, Token { name: "w".to_owned(), @@ -1223,8 +1345,8 @@ mod types_check_tests { name: "g".to_owned(), value: TokenValue::FixedArray( ParamType::Int(64), - vec![TokenValue::Int(big_int.clone()) - ]), + vec![TokenValue::Int(big_int.clone())], + ), }; assert_not_type_check(&tokens_wrong_fixed_array_size, ¶ms); @@ -1233,10 +1355,8 @@ mod types_check_tests { name: "f".to_owned(), value: TokenValue::Array( ParamType::Bool, - vec![ - TokenValue::Bool(false), - TokenValue::Int(big_int.clone()), - ]), + vec![TokenValue::Bool(false), TokenValue::Int(big_int.clone())], + ), }; assert_not_type_check(&tokens_wrong_array_type, ¶ms); @@ -1259,12 +1379,14 @@ mod types_check_tests { } mod default_values_tests { - use {ParamType, TokenValue}; use chrono::prelude::Utc; + use {ParamType, TokenValue}; #[test] fn test_time_default_value() { - if let TokenValue::Time(time) = TokenValue::get_default_value_for_header(&ParamType::Time).unwrap() { + if let TokenValue::Time(time) = + TokenValue::get_default_value_for_header(&ParamType::Time).unwrap() + { let now = Utc::now().timestamp_millis() as u64; assert!(time <= now && time >= now - 1000); } else { @@ -1278,7 +1400,10 @@ mod default_values_tests { let default_values = vec![TokenValue::Expire(0xffffffff), TokenValue::PublicKey(None)]; for (param_type, value) in param_types.iter().zip(default_values) { - assert_eq!(TokenValue::get_default_value_for_header(¶m_type).unwrap(), value); + assert_eq!( + TokenValue::get_default_value_for_header(¶m_type).unwrap(), + value + ); } } } diff --git a/src/token/tokenizer.rs b/src/token/tokenizer.rs index a3ac4bac..f28fdc41 100644 --- a/src/token/tokenizer.rs +++ b/src/token/tokenizer.rs @@ -28,7 +28,7 @@ use std::{ str::FromStr, }; use ton_block::{Grams, MsgAddress}; -use ton_types::{read_single_root_boc, error, fail, Cell, Result}; +use ton_types::{error, fail, read_single_root_boc, Cell, Result}; /// This struct should be used to parse string values as tokens. pub struct Tokenizer; @@ -89,7 +89,6 @@ impl Tokenizer { pub fn tokenize_optional_params( params: &[Param], values: &Value, - default_values: &HashMap, ) -> Result> { if let Value::Object(map) = values { let mut map = map.clone(); @@ -98,8 +97,6 @@ impl Tokenizer { if let Some(value) = map.remove(¶m.name) { let token_value = Self::tokenize_parameter(¶m.kind, &value, ¶m.name)?; tokens.insert(param.name.clone(), token_value); - } else if let Some(value) = default_values.get(¶m.name) { - tokens.insert(param.name.clone(), value.clone()); } } if !map.is_empty() { @@ -116,7 +113,7 @@ impl Tokenizer { Ok(tokens) } else { fail!(AbiError::InvalidInputData { - msg: "Contract function parameters should be passed as a JSON object".to_string() + msg: "Contract parameters should be passed as a JSON object".to_string() }) } } @@ -361,12 +358,10 @@ impl Tokenizer { name: name.to_string(), err: format!("can not decode base64: {}", err), })?; - let cell = read_single_root_boc(&data).map_err(|err| { - AbiError::InvalidParameterValue { - val: value.clone(), - name: name.to_string(), - err: format!("can not deserialize cell: {}", err), - } + let cell = read_single_root_boc(&data).map_err(|err| AbiError::InvalidParameterValue { + val: value.clone(), + name: name.to_string(), + err: format!("can not deserialize cell: {}", err), })?; Ok(TokenValue::Cell(cell)) } @@ -446,7 +441,7 @@ impl Tokenizer { expected: "JSON object".to_string() }) } - + let tokens = Self::tokenize_all_params(params, value)?; Ok(TokenValue::Tuple(tokens)) From 59cba78e6e0d4828af47780477af08e0f268391b Mon Sep 17 00:00:00 2001 From: tonjen Date: Fri, 28 Jul 2023 09:24:08 +0000 Subject: [PATCH 04/15] Update CHANGELOG.md file #nolog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e5489e..f500aa88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## Version 2.3.130 +- ABI v2.4 implemented + +## Version 2.3.130 + - Revert tests ## Version 2.3.124 From 0fdd7240833deb2d9db0e1630fa4d51109574981 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Fri, 28 Jul 2023 12:30:26 +0300 Subject: [PATCH 05/15] Fix build after merge --- src/lib.rs | 2 -- src/token/mod.rs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 03265be9..dc0427e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,8 +50,6 @@ pub use param::Param; pub use param_type::ParamType; pub use token::{Token, TokenValue}; -#[cfg(test)] -extern crate rand; extern crate byteorder; #[cfg(test)] extern crate rand; diff --git a/src/token/mod.rs b/src/token/mod.rs index 2aff8e00..6ef997a3 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -41,11 +41,6 @@ mod test_encoding; #[cfg(test)] mod tests; -#[cfg(test)] -mod tests; -#[cfg(test)] -mod test_encoding; - pub const STD_ADDRESS_BIT_LENGTH: usize = 267; pub const MAX_HASH_MAP_INFO_ABOUT_KEY: usize = 12; From 85744b8f8ac6b47914e61fa3703d4c954837d3fa Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Fri, 28 Jul 2023 12:32:12 +0300 Subject: [PATCH 06/15] Update project version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a5f6bf47..d04e59c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] build = 'common/build/build.rs' name = 'ton_abi' -version = '2.3.130' +version = '2.4.0' [dependencies] base64 = '0.10.0' From 9a0c2d3ff0d7b6a5e330b5711c67a772ccee94f1 Mon Sep 17 00:00:00 2001 From: Alexander Gapak Date: Mon, 31 Jul 2023 08:13:28 -0400 Subject: [PATCH 07/15] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd548974..b95784dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. ## Version 2.4 - Param in fields section extended with `init: boolean` +- `fixedbytes` – type is deprecated +- `ref(T)` – new type added - Default values for parameter types: - - `int` – `N` zero bits. From 92a8ab5395f772666f2f6adbd21fc54ecfa835d8 Mon Sep 17 00:00:00 2001 From: Alexander Gapak Date: Mon, 31 Jul 2023 08:26:30 -0400 Subject: [PATCH 08/15] =?UTF-8?q?Update=20ABI.md=20=E2=80=93=20add=20ref(T?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ABI.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/ABI.md b/docs/ABI.md index 9fec223a..221f5d72 100644 --- a/docs/ABI.md +++ b/docs/ABI.md @@ -160,7 +160,8 @@ If a function has no input parameters or does not return any values, the corresp - [`string`](#string) - a type containing UTF-8 string data, encoded like `bytes`. - [`optional`](#optionalinnertype) - value of optional type `optional(innerType)` can store a value of `innerType` or be empty. - [`itemType[]`](#itemtype) is a dynamic array of `itemType` type elements. It is encoded as a TVM dictionary. `uint32` defines the array elements count placed into the cell body. `HashmapE` (see TL-B schema in TVM spec) struct is then added (one bit as a dictionary root and one reference with data if the dictionary is not empty). The dictionary key is a serialized `uint32` index of the array element, and the value is a serialized array element as `itemType` type. - - `T[k]` is a static size array of `T` type elements. Encoding is equivalent to `T[]` without elements count. +- `T[k]` is a static size array of `T` type elements. Encoding is equivalent to `T[]` without elements count. +- [`ref(T)`](#reft) indicates that `T` will be stored in a separate cell ## Default values for parameter types @@ -179,7 +180,7 @@ Starting from API 2.4 the specification defines default values for parameter typ - [`optional(T)`](#optionalinnertype) – 1 zero bit, i.e. `b{0}`. - [`T[]`](#itemtype) – `x{00000000} b{0}`, i.e. 33 zero bits. - `T[k]` – encoded as an array with `k` default values of type `T` -- `ref(T)` – reference to a cell, cell is encoded as the default value of type `T`. +- [`ref(T)`](#reft) – reference to a cell, cell is encoded as the default value of type `T`. ## Encoding of function ID and its arguments @@ -648,6 +649,15 @@ Analog of `bytes` in Solidity. In C lang can be used as `void*`. | Cell | cell with data stored in a ref | | 0 bit | 1 ref | | JSON object | binary daya represented as hex string | `"313233"` | | | +#### `ref(T)` + +The auxiliary type `ref(T)` helps to explicitly say that `T` will be encoded into a separate cell and stored in the current cell as a reference. And `T` can be of any ABI types, including [`tuple(T1, T2, ..., Tn)`](#tuple), or contain itself like `ref(ref(...)`. + +| Usage | Value | Examples | Max bit size | Max ref size | +|-----------------|--------------------------------------------|-----------|--------------|--------------| +| Cell | cell with data stored in a ref | | 0 bit | 1 ref | +| JSON object | according to `T` or `null` if it is empty | `"hello"` | | | + #### `string` UTF-8 String data. Encoded like `bytes`. In JSON is represented as a sting. From 9e08d089948f88fe7384787411074dae6f233884 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Tue, 1 Aug 2023 15:20:26 +0300 Subject: [PATCH 09/15] Update CHANGELOG.md --- CHANGELOG.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c157feb..5ddd912a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,11 @@ All notable changes to this project will be documented in this file. -## Version 2.4 +## Version 2.4.0 +### New +- ABI v2.4 specification implemented - Param in fields section extended with `init: boolean` -- `fixedbytes` – type is deprecated - `ref(T)` – new type added - Default values for parameter types: @@ -24,9 +25,9 @@ All notable changes to this project will be documented in this file. - - `T[k]` – encoded as an array with `k` default values of type `T` - - `ref(T)` – reference to a cell, cell is encoded as the default value of type `T`. -## Version 2.3.130 - -- ABI v2.4 implemented +### Breaking +- `data` section in ABI JSON is removed. +- `fixedbytes` – type is deprecated ## Version 2.3.130 From 6a6848aec9489acf6bd7cf2253ffc97f9b67f6a7 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Tue, 3 Oct 2023 12:00:51 +0300 Subject: [PATCH 10/15] Fix build after merge --- src/contract.rs | 2 +- src/param.rs | 2 +- src/param_type/param_type.rs | 4 ++-- src/tests/v1/test_contract.rs | 1 - src/tests/v2/test_contract.rs | 1 - src/token/mod.rs | 4 ++-- src/token/tests.rs | 3 +-- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index aa916528..aad65017 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -23,7 +23,7 @@ use crate::{ }; use serde::de::Error as SerdeError; use serde_json; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Display; use std::io; use ton_block::{MsgAddressInt, Serializable}; diff --git a/src/param.rs b/src/param.rs index 60713b5c..4e53bc7a 100644 --- a/src/param.rs +++ b/src/param.rs @@ -48,7 +48,7 @@ impl Param { } #[derive(Debug, Clone, PartialEq, serde::Deserialize)] -struct SerdeParam { +pub(crate) struct SerdeParam { /// Param name. pub name: String, /// Param type. diff --git a/src/param_type/param_type.rs b/src/param_type/param_type.rs index 69c60c43..d173fb7f 100644 --- a/src/param_type/param_type.rs +++ b/src/param_type/param_type.rs @@ -13,8 +13,8 @@ //! Function and event param types. -use crate::contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_1, ABI_VERSION_2_4}; -use crate::{contract::ABI_VERSION_2_0, AbiError}; +use crate::{AbiError, Param}; +use crate::contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_0, ABI_VERSION_2_1, ABI_VERSION_2_4}; use std::fmt; use ton_types::{error, Result}; diff --git a/src/tests/v1/test_contract.rs b/src/tests/v1/test_contract.rs index 27e66348..7f99d6c6 100644 --- a/src/tests/v1/test_contract.rs +++ b/src/tests/v1/test_contract.rs @@ -13,7 +13,6 @@ use crate::{Contract, DataItem, Event, Function, Param, ParamType}; use std::collections::HashMap; -use {Contract, DataItem, Event, Function, Param, ParamType}; const TEST_ABI: &str = r#" { diff --git a/src/tests/v2/test_contract.rs b/src/tests/v2/test_contract.rs index 7b3d4e29..170d25df 100644 --- a/src/tests/v2/test_contract.rs +++ b/src/tests/v2/test_contract.rs @@ -13,7 +13,6 @@ use crate::{Contract, DataItem, Event, Function, Param, ParamType}; use std::collections::HashMap; -use {Contract, DataItem, Event, Function, Param, ParamType}; use crate::contract::ABI_VERSION_2_4; diff --git a/src/token/mod.rs b/src/token/mod.rs index f8663ee5..bfef518c 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -25,7 +25,7 @@ use num_bigint::{BigInt, BigUint}; use std::collections::BTreeMap; use std::fmt; use ton_block::{Grams, MsgAddress}; -use ton_types::{Cell, Result}; +use ton_types::{Cell, Result, BuilderData}; mod deserialize; mod detokenizer; @@ -212,7 +212,7 @@ impl TokenValue { TokenValue::VarInt(size, _) => *param_type == ParamType::VarInt(*size), TokenValue::Bool(_) => *param_type == ParamType::Bool, TokenValue::Tuple(ref arr) => { - if let ParamType::Tuple(ref params) = *param_type { + if let ParamType::Tuple(params) = param_type { Token::types_check(arr, ¶ms) } else { false diff --git a/src/token/tests.rs b/src/token/tests.rs index b2c51636..1c45dba1 100644 --- a/src/token/tests.rs +++ b/src/token/tests.rs @@ -14,8 +14,7 @@ mod tokenize_tests { use crate::token::{Detokenizer, Tokenizer}; use crate::{Int, Param, ParamType, Token, TokenValue, Uint}; - use token::{Detokenizer, Tokenizer}; - use std::collections::{BTreeMap, HashMap}; + use std::collections::BTreeMap; use ton_block::{Grams, MsgAddress}; use ton_types::{AccountId, BuilderData, Cell, SliceData, ED25519_PUBLIC_KEY_LENGTH}; From e3337fd95bcfd4bfe0619bb1b3d784f87c915e34 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Tue, 3 Oct 2023 14:41:43 +0300 Subject: [PATCH 11/15] Replace key slice with named type --- src/contract.rs | 9 +++++---- src/tests/v1/full_stack_tests.rs | 2 +- src/tests/v2/full_stack_tests.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index aad65017..250074de 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -533,14 +533,15 @@ impl Contract { } // Gets public key from contract data - pub fn get_pubkey(data: &SliceData) -> Result>> { + pub fn get_pubkey(data: &SliceData) -> Result> { let map = HashmapE::with_hashmap(Self::DATA_MAP_KEYLEN, data.reference_opt(0)); - map.get(SliceData::load_builder(0u64.write_to_new_cell()?)?) - .map(|opt| opt.map(|slice| slice.get_bytestring(0))) + Ok(map.get(SliceData::load_builder(0u64.write_to_new_cell()?)?)? + .map(|slice| slice.get_bytestring(0).as_slice().try_into()) + .transpose()?) } /// Sets public key into contract data - pub fn insert_pubkey(data: SliceData, pubkey: &[u8]) -> Result { + pub fn insert_pubkey(data: SliceData, pubkey: &PublicKeyData) -> Result { let pubkey_vec = pubkey.to_vec(); let pubkey_len = pubkey_vec.len() * 8; let value = BuilderData::with_raw(pubkey_vec, pubkey_len)?; diff --git a/src/tests/v1/full_stack_tests.rs b/src/tests/v1/full_stack_tests.rs index 8091ef7f..158ecf4e 100644 --- a/src/tests/v1/full_stack_tests.rs +++ b/src/tests/v1/full_stack_tests.rs @@ -317,7 +317,7 @@ fn test_find_event() { #[test] fn test_store_pubkey() { let mut test_map = HashmapE::with_bit_len(Contract::DATA_MAP_KEYLEN); - let test_pubkey = vec![11u8; 32]; + let test_pubkey = [11u8; 32]; test_map .set_builder( SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), diff --git a/src/tests/v2/full_stack_tests.rs b/src/tests/v2/full_stack_tests.rs index eb5cffb3..c9205c0e 100644 --- a/src/tests/v2/full_stack_tests.rs +++ b/src/tests/v2/full_stack_tests.rs @@ -380,7 +380,7 @@ fn test_find_event() { #[test] fn test_store_pubkey() { let mut test_map = HashmapE::with_bit_len(Contract::DATA_MAP_KEYLEN); - let test_pubkey = vec![11u8; 32]; + let test_pubkey = [11u8; 32]; test_map .set_builder( SliceData::load_builder(0u64.write_to_new_cell().unwrap()).unwrap(), From 29d8be3dca99c4f649a9fd0f3ff0651fd7c26f3a Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Wed, 11 Oct 2023 18:43:56 +0300 Subject: [PATCH 12/15] Parameters layout check --- src/error.rs | 3 + src/function.rs | 39 +++---- src/token/deserialize.rs | 227 ++++++++++++++++++++++++++----------- src/token/serialize.rs | 12 +- src/token/test_encoding.rs | 77 ++++++++++++- 5 files changed, 264 insertions(+), 94 deletions(-) diff --git a/src/error.rs b/src/error.rs index 58690092..132ad0f5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -102,4 +102,7 @@ pub enum AbiError { display = "Message destination address is required to encode signed external inbound message body since ABI version 2.3" )] AddressRequired, + + #[fail(display = "Wrong data layout")] + WrongDataLayout } diff --git a/src/function.rs b/src/function.rs index 3db367bc..2747dd6c 100644 --- a/src/function.rs +++ b/src/function.rs @@ -17,7 +17,7 @@ use crate::{ contract::{AbiVersion, SerdeFunction, ABI_VERSION_1_0, ABI_VERSION_2_3}, error::AbiError, param::Param, - token::{SerializedValue, Token, TokenValue}, + token::{SerializedValue, Token, TokenValue, Cursor}, ParamType, PublicKeyData, SignatureData, }; @@ -190,12 +190,13 @@ impl Function { Err(AbiError::WrongId { id })? } - TokenValue::decode_params( + TokenValue::decode_params_with_cursor( self.input_params(), cursor, &self.abi_version, allow_partial, ) + .map(|(tokens, _)| tokens) } /// Decodes function id from contract answer @@ -285,38 +286,38 @@ impl Function { /// Encodes function header with provided header parameters pub fn decode_header( abi_version: &AbiVersion, - mut cursor: SliceData, + cursor: SliceData, header: &Vec, internal: bool, - ) -> Result<(Vec, u32, SliceData)> { + ) -> Result<(Vec, u32, Cursor)> { let mut tokens = vec![]; let mut id = 0; + let mut cursor: Cursor = cursor.into(); if abi_version == &ABI_VERSION_1_0 { - id = cursor.get_next_u32()?; + id = cursor.slice.get_next_u32()?; + cursor.used_bits += 32; } if !internal { // skip signature if abi_version == &ABI_VERSION_1_0 { - cursor.checked_drain_reference()?; + cursor.slice.checked_drain_reference()?; + cursor.used_refs += 1; } else { - if cursor.get_next_bit()? { - cursor.get_next_bytes(ED25519_SIGNATURE_LENGTH)?; + if cursor.slice.get_next_bit()? { + cursor.slice.get_next_bytes(ED25519_SIGNATURE_LENGTH)?; } + cursor.used_bits += if abi_version >= &ABI_VERSION_2_3 { + TokenValue::max_bit_size(&ParamType::Address) + } else { + 1 + ED25519_SIGNATURE_LENGTH * 8 + }; } - for param in header { - let (token_value, new_cursor) = - TokenValue::read_from(¶m.kind, cursor, false, abi_version, false)?; - - cursor = new_cursor; - tokens.push(Token { - name: param.name.clone(), - value: token_value, - }); - } + (tokens, cursor) = TokenValue::decode_params_with_cursor(header, cursor, abi_version, true)?; } if abi_version != &ABI_VERSION_1_0 { - id = cursor.get_next_u32()?; + id = cursor.slice.get_next_u32()?; + cursor.used_bits += 32; } Ok((tokens, id, cursor)) } diff --git a/src/token/deserialize.rs b/src/token/deserialize.rs index d71a4007..a3bf8978 100644 --- a/src/token/deserialize.rs +++ b/src/token/deserialize.rs @@ -12,7 +12,7 @@ */ use crate::{ - contract::{AbiVersion, ABI_VERSION_1_0}, + contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_2}, error::AbiError, int::{Int, Uint}, param::Param, @@ -29,62 +29,141 @@ use ton_types::{ error, fail, BuilderData, Cell, HashmapE, HashmapType, IBitstring, Result, SliceData, }; +#[derive(Clone, Debug, Default)] +pub struct Cursor { + pub used_bits: usize, + pub used_refs: usize, + pub slice: SliceData, +} + +impl From for Cursor { + fn from(slice: SliceData) -> Self { + Self { used_bits: 0, used_refs: 0, slice } + } +} + impl TokenValue { /// Deserializes value from `SliceData` to `TokenValue` - pub fn read_from( + fn read_from( param_type: &ParamType, - mut cursor: SliceData, + mut cursor: Cursor, last: bool, abi_version: &AbiVersion, allow_partial: bool, - ) -> Result<(Self, SliceData)> { - match param_type { - ParamType::Uint(size) => Self::read_uint(*size, cursor), - ParamType::Int(size) => Self::read_int(*size, cursor), - ParamType::VarUint(size) => Self::read_varuint(*size, cursor), - ParamType::VarInt(size) => Self::read_varint(*size, cursor), + ) -> Result<(Self, Cursor)> { + let slice = cursor.slice.clone(); + let (value, slice) = match dbg!(param_type) { + ParamType::Uint(size) => Self::read_uint(*size, slice), + ParamType::Int(size) => Self::read_int(*size, slice), + ParamType::VarUint(size) => Self::read_varuint(*size, slice), + ParamType::VarInt(size) => Self::read_varint(*size, slice), ParamType::Bool => { - cursor = find_next_bits(cursor, 1)?; - Ok((TokenValue::Bool(cursor.get_next_bit()?), cursor)) + let mut slice = find_next_bits(slice, 1)?; + Ok((TokenValue::Bool(slice.get_next_bit()?), slice)) } ParamType::Tuple(tuple_params) => { - Self::read_tuple(tuple_params, cursor, last, abi_version, allow_partial) + return Self::read_tuple(tuple_params, cursor, last, abi_version, allow_partial); } ParamType::Array(item_type) => { - Self::read_array(&item_type, cursor, abi_version, allow_partial) + Self::read_array(&item_type, slice, abi_version, allow_partial) } ParamType::FixedArray(item_type, size) => { - Self::read_fixed_array(&item_type, *size, cursor, abi_version, allow_partial) + Self::read_fixed_array(&item_type, *size, slice, abi_version, allow_partial) } - ParamType::Cell => Self::read_cell(cursor, last, abi_version) - .map(|(cell, cursor)| (TokenValue::Cell(cell), cursor)), + ParamType::Cell => Self::read_cell(slice, last, abi_version) + .map(|(cell, slice)| (TokenValue::Cell(cell), slice)), ParamType::Map(key_type, value_type) => { - Self::read_hashmap(key_type, value_type, cursor, abi_version, allow_partial) + Self::read_hashmap(key_type, value_type, slice, abi_version, allow_partial) } ParamType::Address => { - cursor = find_next_bits(cursor, 1)?; + let mut slice = find_next_bits(slice, 1)?; let address = - ::construct_from(&mut cursor)?; - Ok((TokenValue::Address(address), cursor)) + ::construct_from(&mut slice)?; + Ok((TokenValue::Address(address), slice)) } - ParamType::Bytes => Self::read_bytes(None, cursor, last, abi_version), - ParamType::FixedBytes(size) => Self::read_bytes(Some(*size), cursor, last, abi_version), - ParamType::String => Self::read_string(cursor, last, abi_version), + ParamType::Bytes => Self::read_bytes(None, slice, last, abi_version), + ParamType::FixedBytes(size) => Self::read_bytes(Some(*size), slice, last, abi_version), + ParamType::String => Self::read_string(slice, last, abi_version), ParamType::Token => { - cursor = find_next_bits(cursor, 1)?; - let gram = ::construct_from(&mut cursor)?; - Ok((TokenValue::Token(gram), cursor)) + let mut slice = find_next_bits(slice, 1)?; + let gram = ::construct_from(&mut slice)?; + Ok((TokenValue::Token(gram), slice)) } - ParamType::Time => Self::read_time(cursor), - ParamType::Expire => Self::read_expire(cursor), - ParamType::PublicKey => Self::read_public_key(cursor), + ParamType::Time => Self::read_time(slice), + ParamType::Expire => Self::read_expire(slice), + ParamType::PublicKey => Self::read_public_key(slice), ParamType::Optional(inner_type) => { - Self::read_optional(&inner_type, cursor, last, abi_version, allow_partial) + Self::read_optional(&inner_type, slice, last, abi_version, allow_partial) } ParamType::Ref(inner_type) => { - Self::read_ref(&inner_type, cursor, last, abi_version, allow_partial) + Self::read_ref(&inner_type, slice, last, abi_version, allow_partial) } + }?; + + if last { + Self::check_full_decode(allow_partial, &slice)?; } + + cursor = Self::check_layout(param_type, cursor, &slice, abi_version, last)?; + cursor.slice = slice; + + Ok((value, cursor)) + } + + fn check_layout( + param_type: &ParamType, + original_cursor: Cursor, + new_slice: &SliceData, + abi_version: &AbiVersion, + last: bool, + ) -> Result { + let mut cursor = original_cursor; + let new_cell = new_slice.cell_opt(); + let orig_cell = cursor.slice.cell_opt(); + if abi_version >= &ABI_VERSION_2_2 { + let param_max_bits = Self::max_bit_size(param_type); + let param_max_refs = Self::max_refs_count(param_type); + if new_cell != orig_cell { + if cursor.used_bits + param_max_bits <= BuilderData::bits_capacity() && + (last && cursor.used_refs + param_max_refs <= BuilderData::references_capacity() || + !last && cursor.used_refs + param_max_refs <= BuilderData::references_capacity() - 1) + { + fail!(AbiError::WrongDataLayout); + } + cursor.used_bits = param_max_bits; + cursor.used_refs = param_max_refs; + } else { + cursor.used_bits += param_max_bits; + cursor.used_refs += param_max_refs; + if cursor.used_bits > BuilderData::bits_capacity() || + cursor.used_refs > BuilderData::references_capacity() + { + fail!(AbiError::WrongDataLayout); + } + } + } else { + if new_cell != orig_cell { + // following error will never appear because SliceData::cell_opt function returns + // None only if slice contains just data without refs. And if there is no refs then + // cursor cell can not change + let orig_cell = orig_cell + .ok_or_else(|| AbiError::DeserializationError { + msg: "No original cell in layout check", cursor: cursor.slice.clone() + })?; + + let param_bits = new_slice.pos(); + let param_refs = new_slice.get_references().start; + + if param_bits <= BuilderData::bits_capacity() - orig_cell.bit_length() && + (last && param_refs + orig_cell.references_count() <= BuilderData::references_capacity() || + (!last || abi_version == &ABI_VERSION_1_0) && param_refs + orig_cell.references_count() <= BuilderData::references_capacity() - 1) + { + fail!(AbiError::WrongDataLayout); + } + } + } + + Ok(cursor) } fn read_uint_from_chain(size: usize, cursor: SliceData) -> Result<(BigUint, SliceData)> { @@ -133,23 +212,14 @@ impl TokenValue { fn read_tuple( tuple_params: &[Param], - cursor: SliceData, + cursor: Cursor, last: bool, abi_version: &AbiVersion, allow_partial: bool, - ) -> Result<(Self, SliceData)> { - let mut tokens = Vec::new(); - let mut cursor = cursor; - for param in tuple_params { - let last = last && Some(param) == tuple_params.last(); - let (token_value, new_cursor) = - TokenValue::read_from(¶m.kind, cursor, last, abi_version, allow_partial)?; - tokens.push(Token { - name: param.name.clone(), - value: token_value, - }); - cursor = new_cursor; - } + ) -> Result<(Self, Cursor)> { + let (tokens, cursor) = Self::decode_params_with_cursor( + tuple_params, cursor, abi_version, allow_partial || !last + )?; Ok((TokenValue::Tuple(tokens), cursor)) } @@ -170,18 +240,29 @@ impl TokenValue { abi_version: &AbiVersion, allow_partial: bool, ) -> Result<(Vec, SliceData)> { + let value_len = Self::max_bit_size(item_type); + let value_in_ref = Self::map_value_in_ref(32, value_len); + let original = cursor.clone(); cursor = find_next_bits(cursor, 1)?; let map = HashmapE::with_hashmap(32, cursor.get_dictionary()?.reference_opt(0)); + if map.count(size + 1)? != size { + fail!(AbiError::DeserializationError { + msg: "Array contains more items then declared", + cursor: original + }) + } let mut result = vec![]; for i in 0..size { let mut index = BuilderData::new(); index.append_u32(i as u32)?; match map.get(SliceData::load_builder(index)?) { - Ok(Some(item_slice)) => { - let (token, item_slice) = - Self::read_from(item_type, item_slice, true, abi_version, allow_partial)?; - Self::check_full_decode(allow_partial, &item_slice)?; + Ok(Some(mut item_slice)) => { + if value_in_ref { + item_slice = SliceData::load_cell(item_slice.checked_drain_reference()?)?; + } + let (token, _) = + Self::read_from(item_type, item_slice.into(), true, abi_version, allow_partial)?; result.push(token); } _ => fail!(AbiError::DeserializationError { @@ -251,19 +332,25 @@ impl TokenValue { abi_version: &AbiVersion, allow_partial: bool, ) -> Result<(Self, SliceData)> { + let bit_len = TokenValue::get_map_key_size(key_type)?; + let value_len = Self::max_bit_size(value_type); + let value_in_ref = Self::map_value_in_ref(bit_len, value_len); + cursor = find_next_bits(cursor, 1)?; let mut new_map = BTreeMap::new(); - let bit_len = TokenValue::get_map_key_size(key_type)?; let hashmap = HashmapE::with_hashmap(bit_len, cursor.get_dictionary()?.reference_opt(0)); - hashmap.iterate_slices(|key, value| { - let key = Self::read_from(key_type, key, true, abi_version, allow_partial)?.0; + hashmap.iterate_slices(|key, mut value| { + let key = Self::read_from(key_type, key.into(), true, abi_version, allow_partial)?.0; let key = serde_json::to_value(&key)? .as_str() .ok_or(AbiError::InvalidData { msg: "Non-ordinary key".to_owned(), })? .to_owned(); - let value = Self::read_from(value_type, value, true, abi_version, allow_partial)?.0; + if value_in_ref { + value = SliceData::load_cell(value.checked_drain_reference()?)?; + } + let value = Self::read_from(value_type, value.into(), true, abi_version, allow_partial)?.0; new_map.insert(key, value); Ok(true) })?; @@ -364,25 +451,24 @@ impl TokenValue { let mut cursor = find_next_bits(cursor, 1)?; if cursor.get_next_bit()? { if Self::is_large_optional(inner_type) { - let (cell, cursor) = Self::read_cell(cursor, last, abi_version)?; - let (result, remaining) = Self::read_from( + let cell = cursor.checked_drain_reference()?; + let (result, _) = Self::read_from( inner_type, - SliceData::load_cell(cell)?, + SliceData::load_cell(cell)?.into(), true, abi_version, allow_partial, )?; - Self::check_full_decode(allow_partial, &remaining)?; Ok(( TokenValue::Optional(inner_type.clone(), Some(Box::new(result))), cursor, )) } else { let (result, cursor) = - Self::read_from(inner_type, cursor, last, abi_version, allow_partial)?; + Self::read_from(inner_type, cursor.into(), last, abi_version, allow_partial)?; Ok(( TokenValue::Optional(inner_type.clone(), Some(Box::new(result))), - cursor, + cursor.slice, )) } } else { @@ -398,24 +484,33 @@ impl TokenValue { allow_partial: bool, ) -> Result<(Self, SliceData)> { let (cell, cursor) = Self::read_cell(cursor, last, abi_version)?; - let (result, remaining) = Self::read_from( + let (result, _) = Self::read_from( inner_type, - SliceData::load_cell(cell)?, + SliceData::load_cell(cell)?.into(), true, abi_version, allow_partial, )?; - Self::check_full_decode(allow_partial, &remaining)?; Ok((TokenValue::Ref(Box::new(result)), cursor)) } /// Decodes provided params from SliceData pub fn decode_params( - params: &Vec, - mut cursor: SliceData, + params: &[Param], + cursor: SliceData, abi_version: &AbiVersion, allow_partial: bool, ) -> Result> { + Self::decode_params_with_cursor(params, cursor.into(), abi_version, allow_partial) + .map(|(tokens, _)| tokens) + } + + pub fn decode_params_with_cursor( + params: &[Param], + mut cursor: Cursor, + abi_version: &AbiVersion, + allow_partial: bool, + ) -> Result<(Vec, Cursor)> { let mut tokens = vec![]; for param in params { @@ -431,9 +526,7 @@ impl TokenValue { }); } - Self::check_full_decode(allow_partial, &cursor)?; - - Ok(tokens) + Ok((tokens, cursor)) } } diff --git a/src/token/serialize.rs b/src/token/serialize.rs index d6ac476b..173d01c1 100644 --- a/src/token/serialize.rs +++ b/src/token/serialize.rs @@ -291,9 +291,9 @@ impl TokenValue { Self::pack_cells_into_chain(array[i].write_to_cells(abi_version)?, abi_version)?; if value_in_ref { - map.set_builder(index, &data)?; - } else { map.setref(index, &data.into_cell()?)?; + } else { + map.set_builder(index, &data)?; } } @@ -352,8 +352,8 @@ impl TokenValue { Ok(builder) } - fn map_value_in_ref(key_len: usize, value_len: usize) -> bool { - super::MAX_HASH_MAP_INFO_ABOUT_KEY + key_len + value_len <= 1023 + pub(crate) fn map_value_in_ref(key_len: usize, value_len: usize) -> bool { + super::MAX_HASH_MAP_INFO_ABOUT_KEY + key_len + value_len > 1023 } fn write_map( @@ -390,9 +390,9 @@ impl TokenValue { let slice_key = SliceData::load_builder(key_vec.pop().unwrap().data)?; if value_in_ref { - hashmap.set_builder(slice_key, &data)?; - } else { hashmap.setref(slice_key, &data.into_cell()?)?; + } else { + hashmap.set_builder(slice_key, &data)?; } } diff --git a/src/token/test_encoding.rs b/src/token/test_encoding.rs index 6d6723c6..422106b6 100644 --- a/src/token/test_encoding.rs +++ b/src/token/test_encoding.rs @@ -26,7 +26,8 @@ use crate::contract::{ AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_0, ABI_VERSION_2_1, ABI_VERSION_2_2, MAX_SUPPORTED_VERSION, }; -use crate::{Int, Param, ParamType, Token, TokenValue, Uint}; +use crate::token::Cursor; +use crate::{Int, Param, ParamType, Token, TokenValue, Uint, AbiError}; fn put_array_into_map(array: &[T]) -> HashmapE { let mut map = HashmapE::with_bit_len(32); @@ -80,9 +81,14 @@ fn test_parameters_set( let mut slice = SliceData::load_builder(test_tree).unwrap(); slice.checked_drain_reference().unwrap(); slice.get_next_u32().unwrap(); + let cursor = Cursor { + slice, + used_bits: 32, + used_refs: 1 + }; let decoded_tokens = - TokenValue::decode_params(¶ms, slice, &version.clone().into(), false).unwrap(); + TokenValue::decode_params_with_cursor(¶ms, cursor, version, false).unwrap().0; assert_eq!(decoded_tokens, inputs); } } @@ -1512,3 +1518,70 @@ fn test_default_values() { assert_eq!(encoded, root); } + +#[test] +fn test_wrong_layout() { + let mut builder = BuilderData::new(); + builder.append_u32(123).unwrap(); + builder.checked_append_reference( + BuilderData::with_raw(456u64.to_be_bytes().as_slice(), 64).unwrap().into_cell().unwrap(), + ).unwrap(); + + let slice = SliceData::load_builder(builder).unwrap(); + + let params = params_from_types(vec![ + ParamType::Uint(32), + ParamType::Uint(64), + ]); + + assert!( + matches!( + TokenValue::decode_params(¶ms, slice.clone(), &ABI_VERSION_1_0, false) + .unwrap_err() + .downcast::() + .unwrap(), + AbiError::WrongDataLayout, + ) + ); + assert!( + matches!( + TokenValue::decode_params(¶ms, slice.clone(), &ABI_VERSION_2_1, false) + .unwrap_err() + .downcast::() + .unwrap(), + AbiError::WrongDataLayout, + ) + ); + assert!( + matches!( + TokenValue::decode_params(¶ms, slice.clone(), &ABI_VERSION_2_2, false) + .unwrap_err() + .downcast::() + .unwrap(), + AbiError::WrongDataLayout, + ) + ); + + let addr = MsgAddress::AddrStd(Default::default()); + + let mut builder = BuilderData::new(); + builder.append_builder(&addr.write_to_new_cell().unwrap().into()).unwrap(); + builder.append_builder(&addr.write_to_new_cell().unwrap().into()).unwrap(); + + let slice = SliceData::load_builder(builder).unwrap(); + + let params = params_from_types(vec![ + ParamType::Address, + ParamType::Address, + ]); + + assert!( + matches!( + TokenValue::decode_params(¶ms, slice.clone(), &ABI_VERSION_2_2, false) + .unwrap_err() + .downcast::() + .unwrap(), + AbiError::WrongDataLayout, + ) + ); +} \ No newline at end of file From 7291fa3dbc7a2f09b2452bc7f4ebbbdf9e4ff54f Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Mon, 23 Oct 2023 13:10:39 +0300 Subject: [PATCH 13/15] Check init fields support --- src/contract.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/contract.rs b/src/contract.rs index 250074de..b4321e35 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -491,6 +491,25 @@ impl Contract { Ok(()) } + pub fn init_fields_supported_in_version(abi_version: &AbiVersion) -> bool { + abi_version >= &ABI_VERSION_2_4 + } + + pub fn init_fields_supported(&self) -> bool { + Self::init_fields_supported_in_version(&self.abi_version) + } + + fn check_init_fields_support(&self) -> Result<()> { + if !self.init_fields_supported() { + return Err(AbiError::NotSupported { + subject: "Initial storage fields".to_owned(), + version: self.abi_version, + } + .into()); + } + Ok(()) + } + /// Changes initial values for public contract variables pub fn update_data(&self, data: SliceData, tokens: &[Token]) -> Result { self.check_data_map_support()?; @@ -566,6 +585,8 @@ impl Contract { &self, mut init_fields: HashMap, ) -> Result { + self.check_init_fields_support()?; + let mut tokens = vec![]; for param in &self.fields { let token = init_fields From f8e18bc099d0b33d3d7ac145af7b4c94e89a08b6 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Fri, 3 Nov 2023 17:51:09 +0300 Subject: [PATCH 14/15] Fix tuple last item deserialization --- src/function.rs | 3 ++- src/tests/v2/FairNFTCollection.tvc | Bin 0 -> 1921 bytes src/tests/v2/full_stack_tests.rs | 33 ++++++++++++++++++++++++++++- src/token/deserialize.rs | 9 ++++---- src/token/test_encoding.rs | 2 +- 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 src/tests/v2/FairNFTCollection.tvc diff --git a/src/function.rs b/src/function.rs index 2747dd6c..81871d67 100644 --- a/src/function.rs +++ b/src/function.rs @@ -195,6 +195,7 @@ impl Function { cursor, &self.abi_version, allow_partial, + true, ) .map(|(tokens, _)| tokens) } @@ -313,7 +314,7 @@ impl Function { }; } - (tokens, cursor) = TokenValue::decode_params_with_cursor(header, cursor, abi_version, true)?; + (tokens, cursor) = TokenValue::decode_params_with_cursor(header, cursor, abi_version, true, false)?; } if abi_version != &ABI_VERSION_1_0 { id = cursor.slice.get_next_u32()?; diff --git a/src/tests/v2/FairNFTCollection.tvc b/src/tests/v2/FairNFTCollection.tvc new file mode 100644 index 0000000000000000000000000000000000000000..aa412a025078662d3a59ad608798bde613a18172 GIT binary patch literal 1921 zcma)6Z%i9y7=PcpGU_T&{>=?KdPi%)ny_P0T$Va6(3p%ri-|TtjSzw0K;TLd%@!9* zA#t#zMsa3we(-}I<`Rt`X5!|E#AWenOaJ5=S;Y9K{ooRViDyk-O0+(A1!+%cxTNXt z-g}95EpW-dy$B&5egz*rrEB*VjDU|uyWLJx1KP0t(4Mrnpw^y zDQ5YfBqbXf4;0yHaorhRJ$(n0p?NaQt~n%x{pMNy>S2a7is5iz17TQtSIW?Mks@u0 z4WoSRGOWnw6H+TC?d(nY216_}^!;l-eeLV~hwx4YBr_(K+wx8LUvtQgI#V%y{=bdt zqY|CwIWqk52}GRJ!h}zMm_>2wj zzw!8&(1Fj`=lq_}qH~#{6+lPI^lFIbg_F8lYKUj7^i&opA%IE@572RrJSG3!McT%b zNHP;=k1$0BV`~o9f5Z}%Fc51NBX2TcpK$|{B8Hhz!0m4$PDZF?g$i=oMG56%WX}67 zip=q0oTgI9I}+iEQ>cy@%%K2)s>CKrA{p5ZS`kB>Zo%UgCT0&Py^$_Du_u9}c?2dR zhL^AySq$7bkq)l_iROYNAbc2$TQnd(m&b!v)Z#N1{q+lSZ&DgU9YQ%hmqSUF!oE9` z(fbuxu=FxPa~O-^6(F^V-sTc&uccO`BOSV@!I~#W#%Hp+_w@fsXVHs&&oyO{VjQ4{ zsw<52xOQXnt{Y#d9HtZrc6KYbB0bSk&$qL2*HTj#25)J4uvJy!1;1ne>gMO?2yI2j z_mr7_P1~y_kD$+Z55$6b!-BP!3&E4y?;%CqPWoO>jr?wFG{u&{11J`phQT>ju(h1& zh3K4aY={3pzTK+96@T3MENf=9x8eCM^woMGiC~RGvl`hig9`Z``djv^{&u7JcL-Bps5a9!}Qy(#@C3*Rb{Sj0(DU32)(-X&=IE4NM%snl> literal 0 HcmV?d00001 diff --git a/src/tests/v2/full_stack_tests.rs b/src/tests/v2/full_stack_tests.rs index c9205c0e..8fc9504b 100644 --- a/src/tests/v2/full_stack_tests.rs +++ b/src/tests/v2/full_stack_tests.rs @@ -11,7 +11,7 @@ * limitations under the License. */ -use ton_block::{MsgAddressInt, Serializable}; +use ton_block::{MsgAddressInt, Serializable, Deserializable}; use ton_types::dictionary::HashmapE; use ton_types::{ ed25519_generate_private_key, ed25519_verify, BuilderData, IBitstring, SliceData, @@ -759,3 +759,34 @@ fn test_encode_storage_fields() { )) .is_err()); } + +const ABI_WRONG_STORAGE_LAYOUT: &str = r#"{ + "ABI version": 2, + "version": "2.3", + "header": ["pubkey", "time", "expire"], + "functions": [], + "data": [ + {"key":1,"name":"_collectionName","type":"bytes"} + ], + "events": [ + ], + "fields": [ + {"name":"_pubkey","type":"uint256"}, + {"name":"_timestamp","type":"uint64"}, + {"name":"_constructorFlag","type":"bool"}, + {"components":[{"name":"dtCreated","type":"uint32"},{"name":"ownerAddress","type":"address"},{"name":"kekAddress","type":"address"}],"name":"_info","type":"tuple"}, + {"components":[{"name":"contents","type":"bytes"},{"name":"extension","type":"bytes"},{"name":"name","type":"bytes"},{"name":"comment","type":"bytes"}],"name":"_media","type":"tuple"}, + {"name":"_collectionName","type":"bytes"}, + {"name":"_tokensIssued","type":"uint128"}, + {"name":"_externalMedia","type":"address"} + ] +} +"#; + +#[test] +fn test_wrong_storage_layout() { + let image = include_bytes!("FairNFTCollection.tvc"); + let image = ton_block::StateInit::construct_from_bytes(image).unwrap(); + + assert!(decode_storage_fields(ABI_WRONG_STORAGE_LAYOUT, SliceData::load_cell(image.data.unwrap()).unwrap(), false).is_ok()); +} diff --git a/src/token/deserialize.rs b/src/token/deserialize.rs index a3bf8978..b5307026 100644 --- a/src/token/deserialize.rs +++ b/src/token/deserialize.rs @@ -52,7 +52,7 @@ impl TokenValue { allow_partial: bool, ) -> Result<(Self, Cursor)> { let slice = cursor.slice.clone(); - let (value, slice) = match dbg!(param_type) { + let (value, slice) = match param_type { ParamType::Uint(size) => Self::read_uint(*size, slice), ParamType::Int(size) => Self::read_int(*size, slice), ParamType::VarUint(size) => Self::read_varuint(*size, slice), @@ -218,7 +218,7 @@ impl TokenValue { allow_partial: bool, ) -> Result<(Self, Cursor)> { let (tokens, cursor) = Self::decode_params_with_cursor( - tuple_params, cursor, abi_version, allow_partial || !last + tuple_params, cursor, abi_version, allow_partial, last )?; Ok((TokenValue::Tuple(tokens), cursor)) } @@ -501,7 +501,7 @@ impl TokenValue { abi_version: &AbiVersion, allow_partial: bool, ) -> Result> { - Self::decode_params_with_cursor(params, cursor.into(), abi_version, allow_partial) + Self::decode_params_with_cursor(params, cursor.into(), abi_version, allow_partial, true) .map(|(tokens, _)| tokens) } @@ -510,12 +510,13 @@ impl TokenValue { mut cursor: Cursor, abi_version: &AbiVersion, allow_partial: bool, + last: bool, ) -> Result<(Vec, Cursor)> { let mut tokens = vec![]; for param in params { // println!("{:?}", param); - let last = Some(param) == params.last(); + let last = Some(param) == params.last() && last; let (token_value, new_cursor) = Self::read_from(¶m.kind, cursor, last, abi_version, allow_partial)?; diff --git a/src/token/test_encoding.rs b/src/token/test_encoding.rs index 422106b6..17a59296 100644 --- a/src/token/test_encoding.rs +++ b/src/token/test_encoding.rs @@ -88,7 +88,7 @@ fn test_parameters_set( }; let decoded_tokens = - TokenValue::decode_params_with_cursor(¶ms, cursor, version, false).unwrap().0; + TokenValue::decode_params_with_cursor(¶ms, cursor, version, false, true).unwrap().0; assert_eq!(decoded_tokens, inputs); } } From 2a2a5783c7cdf935e4ee71a38b160d6014f52683 Mon Sep 17 00:00:00 2001 From: Alexey Vavilin Date: Tue, 7 Nov 2023 17:30:08 +0300 Subject: [PATCH 15/15] Serialize fixed bytes into cell body --- CHANGELOG.md | 3 ++- src/function.rs | 8 +++--- src/token/deserialize.rs | 52 +++++++++++++++++++++++++------------- src/token/mod.rs | 26 ++++++++++--------- src/token/serialize.rs | 32 ++++++++++++++++------- src/token/test_encoding.rs | 50 ++++++++++++++++++++++++++---------- 6 files changed, 114 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0827dd3..2e0f37e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ All notable changes to this project will be documented in this file. ### Breaking - `data` section in ABI JSON is removed. -- `fixedbytes` – type is deprecated +- `fixedbytes` type serialization changed. Now it is limited to 127 bytes and serialized into cell +body instead of chained cells in current cell reference ## Version 2.3.147 diff --git a/src/function.rs b/src/function.rs index 81871d67..bdba642a 100644 --- a/src/function.rs +++ b/src/function.rs @@ -308,7 +308,7 @@ impl Function { cursor.slice.get_next_bytes(ED25519_SIGNATURE_LENGTH)?; } cursor.used_bits += if abi_version >= &ABI_VERSION_2_3 { - TokenValue::max_bit_size(&ParamType::Address) + TokenValue::max_bit_size(&ParamType::Address, abi_version) } else { 1 + ED25519_SIGNATURE_LENGTH * 8 }; @@ -387,9 +387,9 @@ impl Function { if self.abi_version >= ABI_VERSION_2_3 { sign_builder.append_raw( &[0u8; MAX_DATA_BYTES], - TokenValue::max_bit_size(&ParamType::Address), + TokenValue::max_bit_size(&ParamType::Address, &self.abi_version), )?; - remove_bits = TokenValue::max_bit_size(&ParamType::Address); + remove_bits = TokenValue::max_bit_size(&ParamType::Address, &self.abi_version); } else { sign_builder.append_bit_one()?; sign_builder.append_raw( @@ -408,7 +408,7 @@ impl Function { SerializedValue { data: sign_builder, max_bits: if self.abi_version >= ABI_VERSION_2_3 { - TokenValue::max_bit_size(&ParamType::Address) + TokenValue::max_bit_size(&ParamType::Address, &self.abi_version) } else { 1 + ED25519_SIGNATURE_LENGTH * 8 }, diff --git a/src/token/deserialize.rs b/src/token/deserialize.rs index b5307026..bc761912 100644 --- a/src/token/deserialize.rs +++ b/src/token/deserialize.rs @@ -12,7 +12,7 @@ */ use crate::{ - contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_2}, + contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_2, ABI_VERSION_2_4}, error::AbiError, int::{Int, Uint}, param::Param, @@ -81,8 +81,8 @@ impl TokenValue { ::construct_from(&mut slice)?; Ok((TokenValue::Address(address), slice)) } - ParamType::Bytes => Self::read_bytes(None, slice, last, abi_version), - ParamType::FixedBytes(size) => Self::read_bytes(Some(*size), slice, last, abi_version), + ParamType::Bytes => Self::read_bytes(slice, last, abi_version), + ParamType::FixedBytes(size) => Self::read_fixed_bytes(*size, slice, last, abi_version), ParamType::String => Self::read_string(slice, last, abi_version), ParamType::Token => { let mut slice = find_next_bits(slice, 1)?; @@ -121,8 +121,8 @@ impl TokenValue { let new_cell = new_slice.cell_opt(); let orig_cell = cursor.slice.cell_opt(); if abi_version >= &ABI_VERSION_2_2 { - let param_max_bits = Self::max_bit_size(param_type); - let param_max_refs = Self::max_refs_count(param_type); + let param_max_bits = Self::max_bit_size(param_type, abi_version); + let param_max_refs = Self::max_refs_count(param_type, abi_version); if new_cell != orig_cell { if cursor.used_bits + param_max_bits <= BuilderData::bits_capacity() && (last && cursor.used_refs + param_max_refs <= BuilderData::references_capacity() || @@ -240,7 +240,7 @@ impl TokenValue { abi_version: &AbiVersion, allow_partial: bool, ) -> Result<(Vec, SliceData)> { - let value_len = Self::max_bit_size(item_type); + let value_len = Self::max_bit_size(item_type, abi_version); let value_in_ref = Self::map_value_in_ref(32, value_len); let original = cursor.clone(); @@ -333,7 +333,7 @@ impl TokenValue { allow_partial: bool, ) -> Result<(Self, SliceData)> { let bit_len = TokenValue::get_map_key_size(key_type)?; - let value_len = Self::max_bit_size(value_type); + let value_len = Self::max_bit_size(value_type, abi_version); let value_in_ref = Self::map_value_in_ref(bit_len, value_len); cursor = find_next_bits(cursor, 1)?; @@ -386,23 +386,39 @@ impl TokenValue { Ok((data, cursor)) } + fn read_fixed_bytes( + size: usize, + cursor: SliceData, + last: bool, + abi_version: &AbiVersion, + ) -> Result<(Self, SliceData)> { + if abi_version >= &ABI_VERSION_2_4 { + let (data, cursor) = get_next_bits_from_chain(cursor, size * 8)?; + Ok((TokenValue::FixedBytes(data), cursor)) + } else { + let original = cursor.clone(); + let (data, cursor) = Self::read_bytes_from_chain(cursor, last, abi_version)?; + + if size == data.len() { + Ok((TokenValue::FixedBytes(data), cursor)) + } else { + Err(error!(AbiError::DeserializationError { + msg: "Size of fixed bytes does not correspond to expected size", + cursor: original + })) + } + } + + } + fn read_bytes( - size: Option, cursor: SliceData, last: bool, abi_version: &AbiVersion, ) -> Result<(Self, SliceData)> { - let original = cursor.clone(); let (data, cursor) = Self::read_bytes_from_chain(cursor, last, abi_version)?; - match size { - Some(size) if size == data.len() => Ok((TokenValue::FixedBytes(data), cursor)), - Some(_) => fail!(AbiError::DeserializationError { - msg: "Size of fixed bytes does not correspond to expected size", - cursor: original - }), - None => Ok((TokenValue::Bytes(data), cursor)), - } + Ok((TokenValue::Bytes(data), cursor)) } fn read_string( @@ -450,7 +466,7 @@ impl TokenValue { ) -> Result<(Self, SliceData)> { let mut cursor = find_next_bits(cursor, 1)?; if cursor.get_next_bit()? { - if Self::is_large_optional(inner_type) { + if Self::is_large_optional(inner_type, abi_version) { let cell = cursor.checked_drain_reference()?; let (result, _) = Self::read_from( inner_type, diff --git a/src/token/mod.rs b/src/token/mod.rs index bfef518c..01e70552 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -17,7 +17,7 @@ use crate::{ int::{Int, Uint}, param::Param, param_type::ParamType, - PublicKeyData, + PublicKeyData, contract::{AbiVersion, ABI_VERSION_2_4}, }; use chrono::prelude::Utc; @@ -337,12 +337,12 @@ impl TokenValue { 8 - ((size - 1) as u8).leading_zeros() as usize } - pub(crate) fn is_large_optional(param_type: &ParamType) -> bool { - Self::max_bit_size(param_type) >= BuilderData::bits_capacity() - || Self::max_refs_count(param_type) >= BuilderData::references_capacity() + pub(crate) fn is_large_optional(param_type: &ParamType, abi_version: &AbiVersion) -> bool { + Self::max_bit_size(param_type, abi_version) >= BuilderData::bits_capacity() + || Self::max_refs_count(param_type, abi_version) >= BuilderData::references_capacity() } - pub(crate) fn max_refs_count(param_type: &ParamType) -> usize { + pub(crate) fn max_refs_count(param_type: &ParamType, abi_version: &AbiVersion) -> usize { match param_type { // in-cell serialized types ParamType::Uint(_) @@ -355,6 +355,7 @@ impl TokenValue { | ParamType::Time | ParamType::Expire | ParamType::PublicKey => 0, + ParamType::FixedBytes(_) if &ABI_VERSION_2_4 <= abi_version => 0, // reference serialized types ParamType::Array(_) | ParamType::FixedArray(_, _) @@ -367,19 +368,19 @@ impl TokenValue { // tuple refs is sum of inner types refs ParamType::Tuple(params) => params .iter() - .fold(0, |acc, param| acc + Self::max_refs_count(¶m.kind)), + .fold(0, |acc, param| acc + Self::max_refs_count(¶m.kind, abi_version)), // large optional is serialized into reference ParamType::Optional(param_type) => { - if Self::is_large_optional(param_type) { + if Self::is_large_optional(param_type, abi_version) { 1 } else { - Self::max_refs_count(param_type) + Self::max_refs_count(param_type, abi_version) } } } } - pub(crate) fn max_bit_size(param_type: &ParamType) -> usize { + pub(crate) fn max_bit_size(param_type: &ParamType, abi_version: &AbiVersion) -> usize { match param_type { ParamType::Uint(size) => *size, ParamType::Int(size) => *size, @@ -391,6 +392,7 @@ impl TokenValue { ParamType::Cell => 0, ParamType::Map(_, _) => 1, ParamType::Address => 591, + ParamType::FixedBytes(size) if &ABI_VERSION_2_4 <= abi_version => size * 8, ParamType::Bytes | ParamType::FixedBytes(_) => 0, ParamType::String => 0, ParamType::Token => 124, @@ -400,12 +402,12 @@ impl TokenValue { ParamType::Ref(_) => 0, ParamType::Tuple(params) => params .iter() - .fold(0, |acc, param| acc + Self::max_bit_size(¶m.kind)), + .fold(0, |acc, param| acc + Self::max_bit_size(¶m.kind, abi_version)), ParamType::Optional(param_type) => { - if Self::is_large_optional(¶m_type) { + if Self::is_large_optional(¶m_type, abi_version) { 1 } else { - 1 + Self::max_bit_size(¶m_type) + 1 + Self::max_bit_size(¶m_type, abi_version) } } } diff --git a/src/token/serialize.rs b/src/token/serialize.rs index 173d01c1..f5740283 100644 --- a/src/token/serialize.rs +++ b/src/token/serialize.rs @@ -12,7 +12,7 @@ */ use crate::{ - contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_2}, + contract::{AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_2, ABI_VERSION_2_4}, error::AbiError, int::{Int, Uint}, param_type::ParamType, @@ -162,9 +162,8 @@ impl TokenValue { Self::write_map(key_type, value_type, value, abi_version) } TokenValue::Address(address) => Ok(address.write_to_new_cell()?), - TokenValue::Bytes(ref arr) | TokenValue::FixedBytes(ref arr) => { - Self::write_bytes(arr, abi_version) - } + TokenValue::Bytes(ref arr) => Self::write_bytes(arr, abi_version), + TokenValue::FixedBytes(ref arr) => Self::write_fixed_bytes(arr, abi_version), TokenValue::String(ref string) => Self::write_bytes(string.as_bytes(), abi_version), TokenValue::Token(gram) => Ok(gram.write_to_new_cell()?), TokenValue::Time(time) => Ok(time.write_to_new_cell()?), @@ -181,8 +180,8 @@ impl TokenValue { let param_type = self.get_param_type(); Ok(vec![SerializedValue { data, - max_bits: Self::max_bit_size(¶m_type), - max_refs: Self::max_refs_count(¶m_type), + max_bits: Self::max_bit_size(¶m_type, abi_version), + max_refs: Self::max_refs_count(¶m_type, abi_version), }]) } @@ -282,7 +281,7 @@ impl TokenValue { ) -> Result { let mut map = HashmapE::with_bit_len(32); - let value_in_ref = Self::map_value_in_ref(32, Self::max_bit_size(param_type)); + let value_in_ref = Self::map_value_in_ref(32, Self::max_bit_size(param_type, abi_version)); for i in 0..array.len() { let index = SliceData::load_builder((i as u32).write_to_new_cell()?)?; @@ -325,6 +324,21 @@ impl TokenValue { Ok(map.write_to_new_cell()?) } + fn write_fixed_bytes(data: &[u8], abi_version: &AbiVersion) -> Result { + if abi_version >= &ABI_VERSION_2_4 { + if data.len() * 8 > BuilderData::bits_capacity() { + fail!(AbiError::InvalidData { + msg: "FixedBytes value size is limited to 127 bytes".to_owned() + }) + } + let mut builder = BuilderData::new(); + builder.append_raw(data, data.len() * 8)?; + Ok(builder) + } else { + Self::write_bytes(data, abi_version) + } + } + fn write_bytes(data: &[u8], abi_version: &AbiVersion) -> Result { let cell_len = BuilderData::bits_capacity() / 8; let mut len = data.len(); @@ -363,7 +377,7 @@ impl TokenValue { abi_version: &AbiVersion, ) -> Result { let key_len = Self::get_map_key_size(key_type)?; - let value_len = Self::max_bit_size(value_type); + let value_len = Self::max_bit_size(value_type, abi_version); let value_in_ref = Self::map_value_in_ref(key_len, value_len); let mut hashmap = HashmapE::with_bit_len(key_len); @@ -419,7 +433,7 @@ impl TokenValue { abi_version: &AbiVersion, ) -> Result { if let Some(value) = value { - if Self::is_large_optional(param_type) { + if Self::is_large_optional(param_type, abi_version) { let value = value.pack_into_chain(abi_version)?; let mut builder = BuilderData::new(); builder.append_bit_one()?; diff --git a/src/token/test_encoding.rs b/src/token/test_encoding.rs index 17a59296..90b7be10 100644 --- a/src/token/test_encoding.rs +++ b/src/token/test_encoding.rs @@ -24,7 +24,7 @@ use ton_types::{ use crate::contract::{ AbiVersion, ABI_VERSION_1_0, ABI_VERSION_2_0, ABI_VERSION_2_1, ABI_VERSION_2_2, - MAX_SUPPORTED_VERSION, + MAX_SUPPORTED_VERSION, ABI_VERSION_2_4, ABI_VERSION_2_3, }; use crate::token::Cursor; use crate::{Int, Param, ParamType, Token, TokenValue, Uint, AbiError}; @@ -1459,14 +1459,7 @@ fn test_default_values() { add_array_as_map(&mut second, &[false; 5], true); // ParamType::FixedBytes(3) - second - .checked_append_reference( - BuilderData::with_raw([0u8; 3].as_slice(), 3 * 8) - .unwrap() - .into_cell() - .unwrap(), - ) - .unwrap(); + second.append_raw(&[0u8; 3], 24).unwrap(); // ParamType::Int(10) second.append_raw(&[0u8; 2], 10).unwrap(); @@ -1480,10 +1473,8 @@ fn test_default_values() { // ParamType::PublicKey second.append_bit_zero().unwrap(); - let mut third = BuilderData::new(); - // ParamType::Ref(Box::new(ParamType::Int(15))) - third + second .checked_append_reference( BuilderData::with_raw([0u8; 2].as_slice(), 15) .unwrap() @@ -1492,6 +1483,8 @@ fn test_default_values() { ) .unwrap(); + let mut third = BuilderData::new(); + // ParamType::String third.checked_append_reference(Cell::default()).unwrap(); @@ -1584,4 +1577,35 @@ fn test_wrong_layout() { AbiError::WrongDataLayout, ) ); -} \ No newline at end of file +} + +#[test] +fn test_fixed_bytes() { + // test prefix with one ref and u32 + let mut builder = BuilderData::new(); + builder.append_u32(0).unwrap(); + builder.checked_append_reference(Cell::default()).unwrap(); + + let bytes = vec![0u8; 32]; + let bytes_builder = BuilderData::with_raw(bytes.clone(), 256).unwrap(); + + let mut builder_v24 = builder.clone(); + builder_v24.append_builder(&bytes_builder).unwrap(); + + builder.checked_append_reference(bytes_builder.into_cell().unwrap()).unwrap(); + + let values = vec![TokenValue::FixedBytes(bytes)]; + + test_parameters_set( + &tokens_from_values(values.clone()), + None, + builder, + &[ABI_VERSION_1_0, ABI_VERSION_2_3], + ); + test_parameters_set( + &tokens_from_values(values), + None, + builder_v24, + &[ABI_VERSION_2_4], + ); +}