thruput_sdk/lib.rs
1#![doc = include_str!("../doc/lib.md")]
2#![cfg_attr(doc, deny(warnings))]
3#![no_std]
4#![allow(clippy::result_large_err)]
5#![allow(clippy::large_enum_variant)]
6
7#[cfg(all(feature = "std", target_arch = "riscv32"))]
8compile_error!(
9 "Feature 'std' cannot be enabled when targeting guest contracts"
10);
11
12#[cfg(all(not(feature = "std"), not(target_arch = "riscv32")))]
13compile_error!("Feature 'std' is required when compiling natively");
14
15// Allow `#[exn]` to emit `::thruput_sdk::exn::Exn` and have it resolve
16// inside this crate itself. (Same pattern as serde.)
17extern crate self as thruput_sdk;
18
19// Re-export the xdrs crate so that derive macros (ReadXDR, WriteXDR) can
20// reference internal xdrs module paths via `::thruput_sdk::__xdrs::…` — this
21// lets contracts depend only on `thruput_sdk`, not on `xdrs` directly.
22#[doc(hidden)]
23pub extern crate xdrs as __xdrs;
24
25#[cfg(feature = "std")]
26extern crate std;
27
28pub use thruput_contract_spec as contract_spec;
29#[doc(hidden)]
30pub use thruput_contract_spec as __contract_spec;
31
32/// 256-bit integer arithmetic, signed and unsigned.
33///
34/// [`U256`] and [`I256`] behave like `u128` and `i128`, including on overflow:
35/// the operators wrap when `overflow-checks` is off (as in every contract
36/// profile) and panic when it is on. The native test harness builds with
37/// overflow checks *on*, so an overflow that fails a test wraps silently
38/// on-chain — see the [Arithmetic](crate#arithmetic) section of the crate
39/// docs, and prefer `checked_*` (or the storage-level
40/// [`NNInt`](crate::storage::NNInt) / [`Balance`](crate::storage::Balance)
41/// guards) for money math.
42///
43/// For the exact `a * b / d` that motivates most reaches for a 256-bit type,
44/// see [`u256::mul_div_u128`] (`u128` in, `u128` out) or [`U256::mul_div`]
45/// (256-bit throughout, on an exact 512-bit intermediate). The crate also
46/// carries OpenZeppelin's `Math.sol` and `SignedMath.sol` surfaces —
47/// `mul_shr`, `mul_mod`, `mod_exp`, `inv_mod`, `average`, `ceil_div`,
48/// `integer_sqrt`/`log2`/`log10`/`log256` with a [`Rounding`] direction —
49/// under the same names bar that one (Solidity's is `sqrt`).
50///
51/// `U256::from` takes a `u128` and `I256::from` an `i128`, both const fns, so
52/// unsuffixed literals and const items work: `const FEE: U256 =
53/// U256::from(1000)`. Narrower values go through `.into()`,
54/// [`U256::from_u32`]/[`U256::from_u64`], or
55/// [`I256::from_i32`]/[`I256::from_i64`].
56pub use thruput_u256 as u256;
57pub use thruput_u256::{
58 I256, Rounding, TryFromIntError, U256, checked_mul_div_u128, mul_div_u128,
59};
60
61/// A span of time, for arithmetic on [`TimePoint`].
62///
63/// `core`'s own type, re-exported so a contract can reach for it without
64/// naming `core` itself. `thruput-xdr` implements `+` and `-` between
65/// `TimePoint` and `Duration`, so a deadline reads the way it does in
66/// Solidity:
67///
68/// ```ignore
69/// let now = thruput_sdk::block::get_block_timepoint();
70/// let deadline = &now + 7 * Duration::from_hours(24);
71/// if now > deadline { ... }
72/// ```
73///
74/// Those operators panic on overflow in every build profile, unlike the
75/// integer primitives: a deadline that silently wrapped into the past would
76/// be a security bug. [`TimePoint::checked_add`] and `saturating_add` handle
77/// the edge without panicking. [`BlockHeight`] takes the same shape against
78/// a signed number of blocks, which needs no import.
79pub use core::time::Duration;
80
81#[cfg(feature = "alloc")]
82extern crate alloc;
83
84// Guest targets get their runtime — `#[global_allocator]`, `#[panic_handler]`,
85// and the `_start` entry — from a dedicated runtime crate, the same way a
86// hosted target gets these from std + the system. Force-link it so contracts
87// (and the rv32 bin wrapper) don't have to name it. The
88// `extern crate ... as _` is load-bearing: nothing references these items by
89// name, so without it the runtime would be pruned as an unused dependency,
90// taking the lang items with it.
91#[cfg(target_arch = "riscv32")]
92extern crate thruput_rv32_runtime as _;
93
94// Guest-dispatch plumbing for the `#[contract]` macro on rv32. The macro
95// emits `#[link_section = "thruput_rv32_entrypoints"]` Entrypoint statics;
96// this re-export gives that generated code a stable path.
97#[cfg(target_arch = "riscv32")]
98#[doc(hidden)]
99pub mod __rv32 {
100 pub use thruput_rv32_runtime::Entrypoint;
101
102 /// The runtime dispatcher's undefined-method hook: the one definition of
103 /// the symbol `thruput-rv32-runtime` tail-calls when no entrypoint
104 /// matches the invoked method id.
105 ///
106 /// The runtime cannot build a `LibraryExn` itself — it is dependency-free
107 /// by design, and `thruput_sdk` depends on *it*, not the other way round —
108 /// so the two meet at this C symbol.
109 ///
110 /// Takes no `method_id`: the caller chose the id it invoked, so echoing
111 /// it back buys nothing, and the unit variant keeps both the encoded
112 /// payload and the emitted code minimal.
113 #[unsafe(no_mangle)]
114 extern "C" fn __thruput_unknown_method() -> ! {
115 crate::exn::library_throw(crate::exn::LibraryExn::UnknownMethod)
116 }
117}
118
119#[cfg(not(target_arch = "riscv32"))]
120#[doc(hidden)]
121pub extern crate linkme as __linkme;
122
123#[doc(hidden)]
124pub mod internal;
125
126mod cell;
127mod diagnostics;
128mod local;
129mod re_throw;
130mod throw;
131mod util;
132pub mod xdr;
133
134// Primitive storage types: interfaces returning Result
135pub mod sys_result;
136
137// Primitive storage types: interfaces throwing exceptions
138#[doc(hidden)]
139pub mod sys_exn;
140
141// Currently-blessed primitive interfaces:
142pub use sys_exn::bytestring::*;
143pub use sys_exn::delete::*;
144pub use sys_exn::flags::*;
145pub use sys_exn::nnint::*;
146pub use sys_exn::rcq::*;
147
148#[doc(hidden)]
149pub mod call;
150pub use call::{ContractClient, MapExceptions};
151// Error types returned by ordinary and exception-mapped `try_*` calls.
152pub use call::{CallError, RawCallError};
153
154/// Off-chain invocation through generated `<Iface>Remote` proxies.
155#[cfg(feature = "remote")]
156pub use thruput_contract_util::remote;
157
158// Module paths that are public API: each names a topic a contract author
159// navigates to. Most are also glob re-exported below, so both
160// `thruput_sdk::block::get_block_height` and `thruput_sdk::get_block_height`
161// are supported paths.
162pub mod block;
163pub mod crypto;
164pub mod event;
165pub mod exn;
166pub mod interface_table;
167pub mod signer;
168pub mod stats;
169pub mod types;
170
171// Implementation modules, reachable only through the root re-exports below,
172// so they can be reorganized without a breaking change. Macro expansions name
173// their items by those root paths too.
174mod auth_claim;
175mod collect_auth_handles;
176mod contract_op;
177mod deploy;
178mod hash;
179mod hasher;
180mod hex;
181mod invoke_config;
182mod log;
183mod receive_auth_handles;
184mod rt;
185mod serialize;
186
187#[doc(hidden)]
188pub mod mintable;
189
190pub use auth_claim::*;
191pub use block::*;
192pub use cell::*;
193pub use collect_auth_handles::*;
194pub use contract_op::*;
195pub use deploy::*;
196pub use event::*;
197pub use exn::*;
198pub use hash::*;
199pub use hasher::*;
200pub use hex::*;
201pub use invoke_config::*;
202pub use log::*;
203// Explicit: a glob would republish #[doc(hidden)] mintable internals.
204pub use mintable::{AuthTokenPreimage, Mintable, MintableType};
205pub use re_throw::*;
206pub use receive_auth_handles::*;
207pub use rt::*;
208pub use serialize::*;
209pub use signer::*;
210pub use throw::*;
211/// Allocation-free base58 conversion for public identifiers.
212pub use thruput_xdr::base58;
213pub use thruput_xdr::base58::{Base58, base58_encode};
214pub use types::*;
215pub use util::*;
216// XDR serialization vocabulary that is part of the SDK's public API (macro
217// expansions reference these as `::thruput_sdk::…`). The rest of the xdrs
218// surface stays namespaced under `thruput_sdk::xdr`.
219pub use xdr::{
220 ReadError, ReadResult, ReadXDR, Reader, WriteError, WriteResult, WriteXDR,
221 Writer,
222};
223
224#[allow(unused_imports)]
225pub use diagnostics::*;
226
227#[doc = include_str!("../doc/macro-contract.md")]
228pub use thruput_macros::contract;
229// These must be enumerated to avoid stomping on the above docstring
230pub use thruput_macros::ContractTypeSpec;
231pub use thruput_macros::Event;
232pub use thruput_macros::contract_exception;
233pub use thruput_macros::contract_type;
234pub use thruput_macros::exn;
235pub use thruput_macros::export_interface_spec;
236pub use thruput_macros::{
237 hash_to_array, sha256_array_ident, sha256_ident, sha256_static, static_key,
238};
239
240pub use thruput_contract_spec::ContractTypeName;
241
242// "Storage" facility
243
244pub mod storage;
245
246/// Declare a native test asserting that no two contract state roots
247/// **linked into this binary** share a storage domain.
248///
249/// The per-crate test generated by `#[contract]` only sees its own crate's
250/// roots. A guest binary that links several contract crates (the rv32
251/// bin-wrapper pattern) aggregates all of them — put this in the wrapper
252/// crate's `tests/` alongside a `use <contract_crate> as _;` for each
253/// linked contract crate so their registrations are linked in:
254///
255/// ```ignore
256/// use counter as _;
257/// thruput_sdk::storage_domain_uniqueness_test!();
258/// ```
259#[macro_export]
260macro_rules! storage_domain_uniqueness_test {
261 () => {
262 #[cfg(all(test, not(target_arch = "riscv32")))]
263 #[test]
264 fn storage_domains_unique_across_linked_crates() {
265 $crate::storage::assert_unique_storage_domains();
266 }
267 };
268}
269
270pub use thruput_macros::Deletable;
271
272pub use thruput_macros::Storage;
273
274#[doc = include_str!("../doc/macro-storage.md")]
275pub use thruput_macros::storage;
276
277// Mock contract execution environment
278
279mod mock;
280
281#[cfg(feature = "dev")]
282pub use mock::*;