Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tonlib-sys"
version = "2026.5.0"
version = "2026.5.1"
edition = "2021"
description = "Rust bindings for tonlibjson library"
license = "MIT"
Expand Down
6 changes: 6 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg_attr(feature = "shared-tonlib", allow(dead_code))]

use anyhow::bail;
use cmake::Config;
use fs2::FileExt;
Expand Down Expand Up @@ -97,6 +99,10 @@ fn build_monorepo() {
println!("cargo:rustc-link-search=native={build_dir}/build/emulator");
println!("cargo:rustc-link-lib=static=emulator");
println!("cargo:rustc-link-lib=static=emulator_static");
// `emulator_version` is implemented by `emulator`, but its commit metadata lives in the
// root-level `git` target.
println!("cargo:rustc-link-search=native={build_dir}/build");
println!("cargo:rustc-link-lib=static=git");
// crypto
println!("cargo:rustc-link-search=native={build_dir}/build/crypto");
println!("cargo:rustc-link-lib=static=ton_block");
Expand Down
36 changes: 32 additions & 4 deletions src/tonlibjson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ extern "C" {
request: *const std::os::raw::c_char,
) -> *const std::os::raw::c_char;

/// Cancels all outstanding requests for a tonlib JSON client.
///
/// # Safety
///
/// `client` must be a valid, live pointer returned by [`tonlib_client_json_create`].
pub fn tonlib_client_json_cancel_requests(client: *mut std::os::raw::c_void);

pub fn tonlib_client_json_destroy(client: *mut std::os::raw::c_void);

pub fn tonlib_client_set_verbosity_level(verbosity_level: u32);
Expand All @@ -24,16 +31,37 @@ extern "C" {
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
use std::ffi::{CStr, CString};

#[test]
fn it_creates_client() {
fn test_all_tonlib_json_exports() -> Result<(), Box<dyn Error>> {
let sync_request = CString::new(r#"{"@type":"getBip39Hints","prefix":"aban"}"#)?;
let async_request = CString::new(r#"{"@type":"getBip39Hints","prefix":"ab"}"#)?;

unsafe {
let client = tonlib_client_json_create();
tonlib_client_set_verbosity_level(4);
assert!(!client.is_null());
tonlib_client_json_send(client, "123\0".as_bytes().as_ptr() as *const i8);
tonlib_client_json_receive(client, 1.0);

tonlib_client_set_verbosity_level(0);

let response = tonlib_client_json_execute(client, sync_request.as_ptr());
assert!(!response.is_null());
let response = CStr::from_ptr(response).to_str()?;
assert!(response.contains(r#""@type":"bip39Hints""#));
assert!(response.contains("abandon"));

tonlib_client_json_send(client, async_request.as_ptr());
let response = tonlib_client_json_receive(client, 1.0);
assert!(!response.is_null());
assert!(CStr::from_ptr(response)
.to_str()?
.contains(r#""@type":"bip39Hints""#));

tonlib_client_json_cancel_requests(client);
tonlib_client_json_destroy(client);
}

Ok(())
}
}
166 changes: 164 additions & 2 deletions src/tvm_emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,43 @@ extern "C" {
config: *const std::os::raw::c_char,
) -> bool;

/// Sets extra-currency balances on a TVM emulator.
///
/// `extra_currencies` uses the upstream `currency_id=balance` space-separated format.
///
/// # Safety
///
/// `tvm_emulator` must be a valid, live TVM emulator pointer and `extra_currencies` must point
/// to a valid, NUL-terminated C string for the duration of the call.
pub fn tvm_emulator_set_extra_currencies(
tvm_emulator: *mut std::os::raw::c_void,
extra_currencies: *const std::os::raw::c_char,
) -> bool;

/// Sets a previously created configuration object on a TVM emulator.
///
/// The configuration remains caller-owned and must outlive the emulator's use of it.
///
/// # Safety
///
/// `tvm_emulator` must be a valid, live TVM emulator pointer and `config` must be a valid,
/// live pointer returned by [`emulator_config_create`](crate::emulator_config_create).
pub fn tvm_emulator_set_config_object(
tvm_emulator: *mut std::os::raw::c_void,
config: *mut std::os::raw::c_void,
) -> bool;

/// Sets the previous-blocks tuple used as the thirteenth element of `c7`.
///
/// # Safety
///
/// `tvm_emulator` must be a valid, live TVM emulator pointer. If `info_boc` is non-null, it
/// must point to a valid, NUL-terminated C string for the duration of the call.
pub fn tvm_emulator_set_prev_blocks_info(
tvm_emulator: *mut std::os::raw::c_void,
info_boc: *const std::os::raw::c_char,
) -> bool;

/**
* @brief Set TVM gas limit
* @param tvm_emulator Pointer to TVM emulator
Expand Down Expand Up @@ -112,6 +149,29 @@ extern "C" {
gas_limit: i64,
) -> *const std::os::raw::c_char;

/// Runs the optimized get-method emulator and returns both the response and VM log.
///
/// The returned opaque result owns both of its string fields and must be released with
/// [`run_method_detailed_result_destroy`].
///
/// # Safety
///
/// `params_boc` must point to a readable buffer of at least `len` bytes. The returned pointer
/// must not be used after it is destroyed.
pub fn tvm_emulator_emulate_run_method_detailed(
len: u32,
params_boc: *const std::os::raw::c_char,
gas_limit: i64,
) -> *mut std::os::raw::c_void;

/// Destroys a detailed get-method result and its response and log fields.
///
/// # Safety
///
/// `detailed_result` must be a valid pointer returned by
/// [`tvm_emulator_emulate_run_method_detailed`] that has not already been destroyed.
pub fn run_method_detailed_result_destroy(detailed_result: *mut std::os::raw::c_void);

/**
* @brief Send external message
* @param tvm_emulator Pointer to TVM emulator
Expand Down Expand Up @@ -173,14 +233,46 @@ extern "C" {
* @param tvm_emulator Pointer to TVM emulator object
*/
pub fn tvm_emulator_destroy(tvm_emulator: *mut std::os::raw::c_void);

/// Destroys a string allocated by the emulator library.
///
/// Use this for strings returned by emulator functions unless their documentation specifies a
/// different owner. A null pointer is accepted.
///
/// # Safety
///
/// `string` must be null or a live pointer allocated and returned by the emulator library that
/// has not already been destroyed. It must not be used after this call.
pub fn string_destroy(string: *const std::os::raw::c_char);

/// Returns JSON containing the emulator library's commit hash and commit date.
///
/// The returned string must be released with [`string_destroy`].
///
/// # Safety
///
/// The returned pointer must be checked for null before dereferencing and must not be used after
/// it is passed to [`string_destroy`].
pub fn emulator_version() -> *const std::os::raw::c_char;
}

#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
use std::ffi::{CStr, CString};

unsafe fn take_emulator_string(
value: *const std::os::raw::c_char,
) -> Result<String, Box<dyn Error>> {
assert!(!value.is_null());
let value_owned = CStr::from_ptr(value).to_str()?.to_owned();
string_destroy(value);
Ok(value_owned)
}

#[test]
fn it_creates_tvm_emulator() {
fn test_all_tvm_emulator_exports() -> Result<(), Box<dyn Error>> {
let code = "te6cckECCwEAAe0AART/APSkE/S88sgLAQIBYgIDAgLMBAUCA3pgCQoD79mRDjgEit8GhpgYC42Eit8H0gGADpj+mf9qJofQB9IGpqGEAKqThdRxgamqiq44L5cCSA/SB9AGoYEGhAMGuQ/QAYEogaKCF4BFAqkGQoAn0BLGeLZmZk9qpwQQg97svvKThdcYEakuAB8YEYAmACcYEvgsIH+XhAYHCACT38FCIBuCoQCaoKAeQoAn0BLGeLAOeLZmSRZGWAiXoAegBlgGSQfIA4OmRlgWUD5f/k6DvADGRlgqxniygCfQEJ5bWJZmZkuP2AQA/jYD+gD6QPgoVBIIcFQgE1QUA8hQBPoCWM8WAc8WzMkiyMsBEvQA9ADLAMn5AHB0yMsCygfL/8nQUAjHBfLgShKhA1AkyFAE+gJYzxbMzMntVAH6QDAg1wsBwwCOH4IQ1TJ223CAEMjLBVADzxYi+gISy2rLH8s/yYBC+wCRW+IAMDUVxwXy4En6QDBZyFAE+gJYzxbMzMntVAAuUUPHBfLgSdQwAchQBPoCWM8WzMzJ7VQAfa289qJofQB9IGpqGDYY/BQAuCoQCaoKAeQoAn0BLGeLAOeLZmSRZGWAiXoAegBlgGT8gDg6ZGWBZQPl/+ToQAAfrxb2omh9AH0gamoYP6qQQFEAfwk=\0";

let data = "te6cckECFAEAA3wAAlFwOPUE4QoACAG/b+7lv/B/MjjfQ11sWK3b4LOpS7Bc7BSmJBVmyz5hdQECAEoBaHR0cHM6Ly90YXJhbnRpbmkuZGV2L3N0b24vbW9vbi5qc29uART/APSkE/S88sgLAwIBYgQFAgLMBgcAG6D2BdqJofQB9IH0gahhAgHUCAkCAUgKCwC7CDHAJJfBOAB0NMDAXGwlRNfA/AL4PpA+kAx+gAxcdch+gAx+gAwAtMfghAPin6lUiC6lTE0WfAI4IIQF41FGVIgupYxREQD8AngNYIQWV8HvLqTWfAK4F8EhA/y8IAARPpEMHC68uFNgAgEgDA0CASASEwH1APTP/oA+kAh8AHtRND6APpA+kDUMFE2oVIqxwXy4sEowv/y4sJUNEJwVCATVBQDyFAE+gJYzxYBzxbMySLIywES9AD0AMsAySD5AHB0yMsCygfL/8nQBPpA9AQx+gB3gBjIywVQCM8WcPoCF8trE8yCEBeNRRnIyx8ZgDgP3O1E0PoA+kD6QNQwCNM/+gBRUaAF+kD6QFNbxwVUc21wVCATVBQDyFAE+gJYzxYBzxbMySLIywES9AD0AMsAyfkAcHTIywLKB8v/ydBQDccFHLHy4sMK+gBRqKGCCJiWgIIImJaAErYIoYIImJaAoBihJ+MPJdcLAcMAI4A8QEQCayz9QB/oCIs8WUAbPFiX6AlADzxbJUAXMI5FykXHiUAioE6CCCJiWgKoAggiYloCgoBS88uLFBMmAQPsAECPIUAT6AljPFgHPFszJ7VQAcFJ5oBihghBzYtCcyMsfUjDLP1j6AlAHzxZQB88WyXGAGMjLBSTPFlAG+gIVy2oUzMlx+wAQJBAjAA4QSRA4N18EAHbCALCOIYIQ1TJ223CAEMjLBVAIzxZQBPoCFstqEssfEss/yXL7AJM1bCHiA8hQBPoCWM8WAc8WzMntVADbO1E0PoA+kD6QNQwB9M/+gD6QDBRUaFSSccF8uLBJ8L/8uLCggiYloCqABagFrzy4sOCEHvdl97Iyx8Vyz9QA/oCIs8WAc8WyXGAGMjLBSTPFnD6AstqzMmAQPsAQBPIUAT6AljPFgHPFszJ7VSAAgyAINch7UTQ+gD6QPpA1DAE0x+CEBeNRRlSILqCEHvdl94TuhKx8uLF0z8x+gAwE6BQI8hQBPoCWM8WAc8WzMntVIH++ZZY=\0";
Expand All @@ -189,13 +281,83 @@ mod tests {
let data_slice = data.as_bytes();
let code_packed = code_slice.as_ptr();
let data_packed = data_slice.as_ptr();
let empty_cell = CString::new("te6cckEBAQEAAgAAAEysuc0=")?;
let address = CString::new(format!("0:{}", "F".repeat(64)))?;
let rand_seed = CString::new("F".repeat(64))?;
let extra_currencies = CString::new("100=20000 200=1")?;
let invalid = CString::new("invalid")?;

unsafe {
assert!(emulator_set_verbosity_level(0));

let emulator =
tvm_emulator_create(code_packed as *const i8, data_packed as *const i8, 2);
tvm_emulator_run_get_method(emulator, 11111123, data_packed as *const i8);
assert!(!emulator.is_null());

assert!(tvm_emulator_set_libraries(emulator, empty_cell.as_ptr()));
assert!(tvm_emulator_set_c7(
emulator,
address.as_ptr(),
1_337,
1_000,
rand_seed.as_ptr(),
std::ptr::null(),
));
assert!(tvm_emulator_set_extra_currencies(
emulator,
extra_currencies.as_ptr(),
));
assert!(tvm_emulator_set_prev_blocks_info(
emulator,
std::ptr::null(),
));
assert!(tvm_emulator_set_gas_limit(emulator, 1_000_000));
assert!(tvm_emulator_set_debug_enabled(emulator, 1));

let result = take_emulator_string(tvm_emulator_run_get_method(
emulator,
11111123,
invalid.as_ptr(),
))?;
assert!(result.contains(r#""success":false"#));

let raw_result = tvm_emulator_emulate_run_method(
invalid.as_bytes().len() as u32,
invalid.as_ptr(),
1_000_000,
);
assert!(raw_result.is_null());

let detailed_result = tvm_emulator_emulate_run_method_detailed(
invalid.as_bytes().len() as u32,
invalid.as_ptr(),
1_000_000,
);
assert!(!detailed_result.is_null());
run_method_detailed_result_destroy(detailed_result);

let result = take_emulator_string(tvm_emulator_send_external_message(
emulator,
invalid.as_ptr(),
))?;
assert!(result.contains(r#""success":false"#));

let result = take_emulator_string(tvm_emulator_send_internal_message(
emulator,
invalid.as_ptr(),
1_000,
))?;
assert!(result.contains(r#""success":false"#));

tvm_emulator_destroy(emulator);

let version = take_emulator_string(emulator_version())?;
assert!(version.contains("emulatorLibCommitHash"));
assert!(version.contains("emulatorLibCommitDate"));

string_destroy(std::ptr::null());
}

Ok(())
}
}
106 changes: 102 additions & 4 deletions src/tx_emulator.rs

Large diffs are not rendered by default.

Loading