Skip to content
This repository was archived by the owner on May 24, 2021. It is now read-only.

Commit

Permalink
Merge pull request #22 from tonlabs/0.21.0-rc
Browse files Browse the repository at this point in the history
Version 0.21.0
  • Loading branch information
diserere authored Apr 17, 2020
2 parents c9e0f61 + f4b89ce commit ee08e2d
Show file tree
Hide file tree
Showing 25 changed files with 1,047 additions and 388 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Release Notes
All notable changes to this project will be documented in this file.

## 0.21.0 - Apr 5, 2020
### Featured
- ABI version 2 supported. See specification at https://docs.ton.dev
- Message expiration implemented. Check [usage guide](https://docs.ton.dev/86757ecb2/p/88321a-message-expiration-time)

### New
- `get_deploy_data` function added

## 0.20.100 - Feb 17, 2020
### New
- `deploy` function now checks the account state before sending message and returns `alreadyDeployed = true` if account is already active.
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ton-client-rs"
version = "0.20.101"
version = "0.21.0"
description = "TON SDK Client Library for Rust"
authors = ["TON DEV SOLUTIONS LTD <support@tonlabs.io>"]
edition = "2018"
Expand All @@ -25,3 +25,7 @@ error-chain = { version = "^0.12", default-features = false }
[build-dependencies]
curl = "0.4.19"
flate2 = "1.0.4"

[dev-dependencies]
lazy_static = "1.4.0"
dirs = "2.0.2"
34 changes: 27 additions & 7 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,39 @@ use crate::{TonCrypto, TonContracts, TonQueries, TonResult};

/// `TonClient` configuration. Contains optional fields with configuration parameters.
///
/// `default_workchain` sets target workchain for deploying and running contracts
/// `base_url` is URL of the Tonlabs node to connect
///
/// `base_url` is used for deriving `requests_url`, `queries_url` and `subscriptions_url`
/// values with default suffixes if ones are not set.
/// `message_retries_count` sets message sending retries count. 0 to disable retrying. Default is 5.
///
/// `requests_url` points address for sending requests to node via http REST API
/// `message_expiration_timeout` sets time in ms used to calculate for message expiration time if `expire`
/// value is not set in contract function header params. Default is 10 seconds
///
/// `queries_url` points address of GraphQL server for quering blockchain data
/// `message_expiration_timeout_grow_factor` sets `message_expiration_timeout` multiplying coefficient
/// for retriyng messages. `message_expiration_timeout` for each retry is calculated by formula
/// `message_expiration_timeout * message_expiration_timeout_grow_factor^retry_index`. Default is 1.5
///
/// `message_processing_timeout` sets timeout in ms for processing messages to contracts which don't support
/// message expiration. It is also used for waiting blocks after message expiration time ends.assert_eq!
///
/// `message_processing_timeout_grow_factor` sets `message_processing_timeout` multiplying coefficient
/// for retriyng messages. `message_processing_timeout` for each retry is calculated by formula
/// `message_processing_timeout * message_processing_timeout_grow_factor^retry_index`. Default is 1.5
///
/// `wait_for_timeout` sets default timeout in ms for `wait_for` function
///
/// `access_key` is key for authenicating user to Tonlabs node
///
/// `subscriptions_url` points address of GraphQL server for subscripitions on blockchain data updates
#[derive(Default, Serialize)]
#[serde(rename_all="camelCase")]
pub struct TonClientConfig {
pub base_url: Option<String>,
pub message_retries_count: Option<u8>,
pub message_expiration_timeout: Option<u32>,
pub message_expiration_timeout_grow_factor: Option<f32>,
pub message_processing_timeout: Option<u32>,
pub message_processing_timeout_grow_factor: Option<f32>,
pub wait_for_timeout: Option<u32>,
pub access_key: Option<String>,
}

/// Entry point for TON blockchain interaction. Provides useful methods for TON clients
Expand Down Expand Up @@ -60,7 +79,8 @@ impl TonClient {
/// `default_workchain` is set to 0.
pub fn new_with_base_url(base_url: &str) -> TonResult<TonClient> {
Self::new(&TonClientConfig {
base_url: Some(base_url.to_string())
base_url: Some(base_url.to_string()),
..TonClientConfig::default()
})
}

Expand Down
174 changes: 113 additions & 61 deletions src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* limitations under the License.
*/

use crate::{Ed25519KeyPair, TonAddress};
use crate::{Ed25519KeyPair, Ed25519Public, TonAddress};
use crate::error::*;
use serde_json::Value;
use crate::interop::{InteropContext, Interop};
Expand All @@ -21,25 +21,47 @@ use crate::interop::{InteropContext, Interop};
#[allow(non_snake_case)]
pub(crate) struct ParamsOfDeploy {
pub abi: serde_json::Value,
pub constructorHeader: Option<serde_json::Value>,
pub constructorParams: serde_json::Value,
pub initParams: Option<serde_json::Value>,
pub imageBase64: String,
pub keyPair: Ed25519KeyPair,
pub workchainId: i32,
}

#[derive(Serialize)]
/// Result of `deploy` function running. Contains address of the contract
#[allow(non_snake_case)]
pub(crate) struct ParamsOfGetDeployAddress {
pub abi: serde_json::Value,
pub imageBase64: String,
pub keyPair: Ed25519KeyPair,
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct ResultOfDeploy {
pub address: TonAddress,
pub alreadyDeployed: bool,
}

/// Result of `deploy` and `get_deploy_address` function running. Contains address of the contract
#[derive(Serialize)]
#[allow(non_snake_case)]
pub(crate) struct ParamsOfGetDeployData {
pub abi: Option<serde_json::Value>,
pub imageBase64: Option<String>,
pub initParams: Option<serde_json::Value>,
pub publicKeyHex: Ed25519Public,
pub workchainId: Option<i32>,
}

#[derive(Serialize, Deserialize)]
pub struct ResultOfDeploy {
pub address: TonAddress,
pub alreadyDeployed: bool,
#[allow(non_snake_case)]
pub(crate) struct ResultOfGetDeployDataCore {
pub imageBase64: Option<String>,
pub address: Option<String>,
pub dataBase64: String,
}

#[derive(Debug, PartialEq)]
/// Result of `get_deploy_data` function call. Contains updated contract image, deploy address and
/// stored data
pub struct ResultOfGetDeployData {
pub image: Option<Vec<u8>>,
pub address: Option<TonAddress>,
pub data: Vec<u8>,
}

#[derive(Serialize)]
Expand All @@ -48,6 +70,7 @@ pub(crate) struct ParamsOfRun {
pub address: TonAddress,
pub abi: serde_json::Value,
pub functionName: String,
pub header: Option<serde_json::Value>,
pub input: serde_json::Value,
pub keyPair: Option<Ed25519KeyPair>,
}
Expand All @@ -59,26 +82,29 @@ pub(crate) struct ParamsOfLocalRun {
pub account: Option<serde_json::Value>,
pub abi: serde_json::Value,
pub functionName: String,
pub header: Option<serde_json::Value>,
pub input: serde_json::Value,
pub keyPair: Option<Ed25519KeyPair>,
}

/// Result of `run` function running. Contains parameters returned by contract function
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct ResultOfRun {
pub output: Value
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[allow(non_snake_case)]
pub struct ParamsOfDecodeMessageBody {
pub(crate) struct ParamsOfDecodeMessageBody {
pub abi: serde_json::Value,
pub bodyBase64: String,
}

/// Result of `decode_input_message_body` and `decode_output_message_body` functions calls.
/// Contains contract function name and decoded parameters
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct ResultOfDecodeMessageBody {
pub function: String,
pub output: serde_json::Value
Expand Down Expand Up @@ -109,44 +135,83 @@ impl TonContracts {
/// Get address for contract deploying
pub fn get_deploy_address(
&self,
code: &[u8],
keys: &Ed25519KeyPair,
abi: &str,
image: &[u8],
init_params: Option<RunParameters>,
public_key: &Ed25519Public,
workchain_id: i32,
) -> TonResult<TonAddress> {
let result: TonAddress = Interop::json_request(
self.get_deploy_data(Some(abi), Some(image), init_params, public_key, Some(workchain_id))?
.address
.ok_or(TonErrorKind::InternalError("No address in result".to_owned()).into())
}

/// Get contract deploy data: image (state init), storage data and deploying address
pub fn get_deploy_data(
&self,
abi: Option<&str>,
image: Option<&[u8]>,
init_params: Option<RunParameters>,
public_key: &Ed25519Public,
workchain_id: Option<i32>,
) -> TonResult<ResultOfGetDeployData> {
let abi = abi.map(|val| {
serde_json::from_str(val).map_err(|_| TonErrorKind::InvalidArg(val.to_owned()))
}).transpose()?;

let core_result: ResultOfGetDeployDataCore = Interop::json_request(
self.context,
"contracts.deploy.address",
ParamsOfGetDeployAddress {
abi: Value::Null,
imageBase64: base64::encode(code),
keyPair: keys.clone(),
"contracts.deploy.data",
ParamsOfGetDeployData {
abi,
imageBase64: image.map(|val| base64::encode(val)),
initParams: Self::option_params_to_value(init_params)?,
publicKeyHex: public_key.clone(),
workchainId: workchain_id,
})?;
Ok(result)

Ok(ResultOfGetDeployData {
address: core_result.address.map(|val| TonAddress::from_str(&val)).transpose()?,
image: core_result.imageBase64.map(|val| base64::decode(&val).into()).transpose()?,
data: base64::decode(&core_result.dataBase64)?
})
}

fn params_to_value(params: RunParameters) -> TonResult<Value> {
let str_params = match &params {
RunParameters::Json(string) => string
};
serde_json::from_str(str_params)
.map_err(|_| TonErrorKind::InvalidArg(str_params.to_owned()).into())
}

fn option_params_to_value(params: Option<RunParameters>) -> TonResult<Option<Value>> {
params.map(|params| Self::params_to_value(params)).transpose()
}

/// Deploy contract to TON blockchain
pub fn deploy(
&self,
abi: &str,
code: &[u8],
constructor_header: Option<RunParameters>,
constructor_params: RunParameters,
init_params: Option<RunParameters>,
keys: &Ed25519KeyPair,
workchain_id: i32,
) -> TonResult<ResultOfDeploy> {
let abi = serde_json::from_str(abi)
.map_err(|_| TonErrorKind::InvalidArg(abi.to_owned()))?;

let str_params = match &constructor_params {
RunParameters::Json(string) => string
};
let params_value = serde_json::from_str(str_params)
.map_err(|_| TonErrorKind::InvalidArg(str_params.to_owned()))?;

let result: ResultOfDeploy = Interop::json_request(self.context, "contracts.deploy", ParamsOfDeploy {
Interop::json_request(self.context, "contracts.deploy", ParamsOfDeploy {
abi,
constructorParams: params_value,
initParams: Self::option_params_to_value(init_params)?,
constructorHeader: Self::option_params_to_value(constructor_header)?,
constructorParams: Self::params_to_value(constructor_params)?,
imageBase64: base64::encode(code),
keyPair: keys.clone(),
})?;
Ok(result)
workchainId: workchain_id,
})
}

/// Run the contract function with given parameters
Expand All @@ -155,26 +220,21 @@ impl TonContracts {
address: &TonAddress,
abi: &str,
function_name: &str,
header: Option<RunParameters>,
input: RunParameters,
keys: Option<&Ed25519KeyPair>,
) -> TonResult<Value> {
let abi = serde_json::from_str(abi)
.map_err(|_| TonErrorKind::InvalidArg(abi.to_owned()))?;

let str_params = match &input {
RunParameters::Json(string) => string
};
let params_value = serde_json::from_str(str_params)
.map_err(|_| TonErrorKind::InvalidArg(str_params.to_owned()))?;

let result: ResultOfRun = Interop::json_request(self.context, "contracts.run", ParamsOfRun {
Interop::json_request(self.context, "contracts.run", ParamsOfRun {
address: address.clone(),
abi,
functionName: function_name.to_string(),
input: params_value,
header: Self::option_params_to_value(header)?,
input: Self::params_to_value(input)?,
keyPair: if let Some(keys) = keys { Some(keys.clone()) } else { None },
})?;
Ok(result.output)
})
}

/// Run the contract function with given parameters locally
Expand All @@ -184,35 +244,27 @@ impl TonContracts {
account: Option<&str>,
abi: &str,
function_name: &str,
header: Option<RunParameters>,
input: RunParameters,
keys: Option<&Ed25519KeyPair>,
) -> TonResult<Value> {
let abi = serde_json::from_str(abi)
.map_err(|_| TonErrorKind::InvalidArg(abi.to_owned()))?;

let str_params = match &input {
RunParameters::Json(string) => string
};
let params_value = serde_json::from_str(str_params)
.map_err(|_| TonErrorKind::InvalidArg(str_params.to_owned()))?;

let account = match account {
Some(acc_str) => {
Some(serde_json::from_str(acc_str)
.map_err(|_| TonErrorKind::InvalidArg(acc_str.to_owned()))?)
},
None => None
};

let result: ResultOfRun = Interop::json_request(self.context, "contracts.run.local", ParamsOfLocalRun {
let account = account.map(|acc_str| {
serde_json::from_str(acc_str)
.map_err(|_| TonErrorKind::InvalidArg(acc_str.to_owned()))
}).transpose()?;

Interop::json_request(self.context, "contracts.run.local", ParamsOfLocalRun {
address: address.clone(),
account,
abi,
functionName: function_name.to_string(),
input: params_value,
header: Self::option_params_to_value(header)?,
input: Self::params_to_value(input)?,
keyPair: if let Some(keys) = keys { Some(keys.clone()) } else { None },
})?;
Ok(result.output)
})
}

/// Decodes external inbound message body with encoded contract call parameters
Expand Down
Loading

0 comments on commit ee08e2d

Please sign in to comment.