ergo/
ergo_box.rs

1//! Box (aka coin, or an unspent output) is a basic concept of a UTXO-based cryptocurrency.
2//! In Bitcoin, such an object is associated with some monetary value (arbitrary,
3//! but with predefined precision, so we use integer arithmetic to work with the value),
4//! and also a guarding script (aka proposition) to protect the box from unauthorized opening.
5//!
6//! In other way, a box is a state element locked by some proposition (ErgoTree).
7//!
8//! In Ergo, box is just a collection of registers, some with mandatory types and semantics,
9//! others could be used by applications in any way.
10//! We add additional fields in addition to amount and proposition~(which stored in the registers R0 and R1).
11//! Namely, register R2 contains additional tokens (a sequence of pairs (token identifier, value)).
12//! Register R3 contains height when block got included into the blockchain and also transaction
13//! identifier and box index in the transaction outputs.
14//! Registers R4-R9 are free for arbitrary usage.
15//!
16//! A transaction is unsealing a box. As a box can not be open twice, any further valid transaction
17//! can not be linked to the same box.
18use ergo_lib_c_core::{
19    collections::{CollectionPtr, ConstCollectionPtr},
20    constant::ConstantPtr,
21    contract::ConstContractPtr,
22    ergo_box::*,
23    ergo_tree::ErgoTreePtr,
24    token::Token,
25    transaction::ConstTxIdPtr,
26    Error,
27};
28use paste::paste;
29
30use crate::{ErrorPtr, ReturnOption};
31use std::{
32    ffi::{CStr, CString},
33    os::raw::c_char,
34};
35
36use crate::delete_ptr;
37
38// `BoxId` bindings --------------------------------------------------------------------------------
39
40/// Parse box id (32 byte digest) from base16-encoded string
41#[no_mangle]
42pub unsafe extern "C" fn ergo_lib_box_id_from_str(
43    box_id_str: *const c_char,
44    box_id_out: *mut BoxIdPtr,
45) -> ErrorPtr {
46    let box_id_str = CStr::from_ptr(box_id_str).to_string_lossy();
47    let res = box_id_from_str(&box_id_str, box_id_out);
48    Error::c_api_from(res)
49}
50
51/// Base16 encoded string
52#[no_mangle]
53pub unsafe extern "C" fn ergo_lib_box_id_to_str(
54    box_id_ptr: ConstBoxIdPtr,
55    _box_id_str: *mut *const c_char,
56) {
57    #[allow(clippy::unwrap_used)]
58    {
59        let s = box_id_to_str(box_id_ptr).unwrap();
60        *_box_id_str = CString::new(s).unwrap().into_raw();
61    }
62}
63
64/// Returns byte array (32 bytes) Note: it's imperative that `output` points to a valid block of
65/// memory of 32 bytes.
66#[no_mangle]
67pub unsafe extern "C" fn ergo_lib_box_id_to_bytes(box_id_ptr: ConstBoxIdPtr, output: *mut u8) {
68    #[allow(clippy::unwrap_used)]
69    box_id_to_bytes(box_id_ptr, output).unwrap();
70}
71
72/// Drop `BoxId`
73#[no_mangle]
74pub unsafe extern "C" fn ergo_lib_box_id_delete(ptr: BoxIdPtr) {
75    delete_ptr(ptr)
76}
77
78make_ffi_eq!(BoxId);
79
80// `BoxValue` bindings ------------------------------------------------------------------------------
81
82/// Recommended (safe) minimal box value to use in case box size estimation is unavailable.
83/// Allows box size upto 2777 bytes with current min box value per byte of 360 nanoERGs
84#[no_mangle]
85pub unsafe extern "C" fn ergo_lib_box_value_safe_user_min(box_value_out: *mut BoxValuePtr) {
86    #[allow(clippy::unwrap_used)]
87    box_value_safe_user_min(box_value_out).unwrap();
88}
89
90/// Number of units inside one ERGO (i.e. one ERG using nano ERG representation)
91#[no_mangle]
92pub extern "C" fn ergo_lib_box_value_units_per_ergo() -> i64 {
93    box_value_units_per_ergo()
94}
95
96/// Create from i64 with bounds check
97#[no_mangle]
98pub unsafe extern "C" fn ergo_lib_box_value_from_i64(
99    amount: i64,
100    box_value_out: *mut BoxValuePtr,
101) -> ErrorPtr {
102    let res = box_value_from_i64(amount, box_value_out);
103    Error::c_api_from(res)
104}
105
106/// Get value as signed 64-bit long
107#[no_mangle]
108pub unsafe extern "C" fn ergo_lib_box_value_as_i64(box_value_ptr: ConstBoxValuePtr) -> i64 {
109    #[allow(clippy::unwrap_used)]
110    box_value_as_i64(box_value_ptr).unwrap()
111}
112
113/// Create a new box value which is the sum of the arguments, with bounds check.
114#[no_mangle]
115pub unsafe extern "C" fn ergo_lib_box_value_sum_of(
116    box_value0_ptr: ConstBoxValuePtr,
117    box_value1_ptr: ConstBoxValuePtr,
118    sum_of_out: *mut BoxValuePtr,
119) -> ErrorPtr {
120    let res = box_value_sum_of(box_value0_ptr, box_value1_ptr, sum_of_out);
121    Error::c_api_from(res)
122}
123
124/// Drop `BoxValue`
125#[no_mangle]
126pub unsafe extern "C" fn ergo_lib_box_value_delete(ptr: BoxValuePtr) {
127    delete_ptr(ptr)
128}
129
130make_ffi_eq!(BoxValue);
131
132// `ErgoBoxCandidate` bindings ----------------------------------------------------------------------
133
134/// Returns value (ErgoTree constant) stored in the register or None if the register is empty
135#[no_mangle]
136pub unsafe extern "C" fn ergo_lib_ergo_box_candidate_register_value(
137    ergo_box_candidate_ptr: ConstErgoBoxCandidatePtr,
138    register_id: NonMandatoryRegisterId,
139    constant_out: *mut ConstantPtr,
140) -> ReturnOption {
141    match ergo_box_candidate_register_value(ergo_box_candidate_ptr, register_id, constant_out) {
142        Ok(is_some) => ReturnOption {
143            is_some,
144            error: std::ptr::null_mut(),
145        },
146        Err(e) => ReturnOption {
147            is_some: false, // Just a dummy value
148            error: Error::c_api_from(Err(e)),
149        },
150    }
151}
152
153/// Get box creation height
154#[no_mangle]
155pub unsafe extern "C" fn ergo_lib_ergo_box_candidate_creation_height(
156    ergo_box_candidate_ptr: ConstErgoBoxCandidatePtr,
157) -> u32 {
158    #[allow(clippy::unwrap_used)]
159    ergo_box_candidate_creation_height(ergo_box_candidate_ptr).unwrap()
160}
161
162/// Get tokens for box
163#[no_mangle]
164pub unsafe extern "C" fn ergo_lib_ergo_box_candidate_tokens(
165    ergo_box_candidate_ptr: ConstErgoBoxCandidatePtr,
166    tokens_out: *mut CollectionPtr<Token>,
167) {
168    #[allow(clippy::unwrap_used)]
169    ergo_box_candidate_tokens(ergo_box_candidate_ptr, tokens_out).unwrap();
170}
171
172/// Get ergo tree for box
173#[no_mangle]
174pub unsafe extern "C" fn ergo_lib_ergo_box_candidate_ergo_tree(
175    ergo_box_candidate_ptr: ConstErgoBoxCandidatePtr,
176    ergo_tree_out: *mut ErgoTreePtr,
177) {
178    #[allow(clippy::unwrap_used)]
179    ergo_box_candidate_ergo_tree(ergo_box_candidate_ptr, ergo_tree_out).unwrap();
180}
181
182/// Get box value in nanoERGs
183#[no_mangle]
184pub unsafe extern "C" fn ergo_lib_ergo_box_candidate_box_value(
185    ergo_box_candidate_ptr: ConstErgoBoxCandidatePtr,
186    box_value_out: *mut BoxValuePtr,
187) {
188    #[allow(clippy::unwrap_used)]
189    ergo_box_candidate_box_value(ergo_box_candidate_ptr, box_value_out).unwrap();
190}
191
192/// Drop `ErgoBoxCandidate`
193#[no_mangle]
194pub unsafe extern "C" fn ergo_lib_ergo_box_candidate_delete(ptr: ErgoBoxCandidatePtr) {
195    delete_ptr(ptr)
196}
197
198make_collection!(ErgoBoxCandidates, ErgoBoxCandidate);
199make_ffi_eq!(ErgoBoxCandidate);
200
201// `ErgoBox` bindings ------------------------------------------------------------------------------
202
203/// Make a new box with:
204/// `value` - amount of money associated with the box
205/// `contract` - guarding contract, which should be evaluated to true in order
206/// to open(spend) this box
207/// `creation_height` - height when a transaction containing the box is created.
208/// `tx_id` - transaction id in which this box was "created" (participated in outputs)
209/// `index` - index (in outputs) in the transaction
210#[no_mangle]
211pub unsafe extern "C" fn ergo_lib_ergo_box_new(
212    value_ptr: ConstBoxValuePtr,
213    creation_height: u32,
214    contract_ptr: ConstContractPtr,
215    tx_id_ptr: ConstTxIdPtr,
216    index: u16,
217    tokens_ptr: ConstCollectionPtr<Token>,
218    ergo_box_out: *mut ErgoBoxPtr,
219) -> ErrorPtr {
220    let res = ergo_box_new(
221        value_ptr,
222        creation_height,
223        contract_ptr,
224        tx_id_ptr,
225        index,
226        tokens_ptr,
227        ergo_box_out,
228    );
229    Error::c_api_from(res)
230}
231
232/// Get box id
233#[no_mangle]
234pub unsafe extern "C" fn ergo_lib_ergo_box_id(
235    ergo_box_ptr: ConstErgoBoxPtr,
236    box_id_out: *mut BoxIdPtr,
237) {
238    #[allow(clippy::unwrap_used)]
239    ergo_box_box_id(ergo_box_ptr, box_id_out).unwrap();
240}
241
242/// Get box creation height
243#[no_mangle]
244pub unsafe extern "C" fn ergo_lib_ergo_box_creation_height(ergo_box_ptr: ConstErgoBoxPtr) -> u32 {
245    #[allow(clippy::unwrap_used)]
246    ergo_box_creation_height(ergo_box_ptr).unwrap()
247}
248
249/// Get tokens for box
250#[no_mangle]
251pub unsafe extern "C" fn ergo_lib_ergo_box_tokens(
252    ergo_box_ptr: ConstErgoBoxPtr,
253    tokens_out: *mut CollectionPtr<Token>,
254) {
255    #[allow(clippy::unwrap_used)]
256    ergo_box_tokens(ergo_box_ptr, tokens_out).unwrap();
257}
258
259/// Get ergo tree for box
260#[no_mangle]
261pub unsafe extern "C" fn ergo_lib_ergo_box_ergo_tree(
262    ergo_box_ptr: ConstErgoBoxPtr,
263    ergo_tree_out: *mut ErgoTreePtr,
264) {
265    #[allow(clippy::unwrap_used)]
266    ergo_box_ergo_tree(ergo_box_ptr, ergo_tree_out).unwrap();
267}
268
269/// Get box value in nanoERGs
270#[no_mangle]
271pub unsafe extern "C" fn ergo_lib_ergo_box_value(
272    ergo_box_ptr: ConstErgoBoxPtr,
273    box_value_out: *mut BoxValuePtr,
274) {
275    #[allow(clippy::unwrap_used)]
276    ergo_box_value(ergo_box_ptr, box_value_out).unwrap();
277}
278
279/// Returns value (ErgoTree constant) stored in the register or None if the register is empty
280#[no_mangle]
281pub unsafe extern "C" fn ergo_lib_ergo_box_register_value(
282    ergo_box_ptr: ConstErgoBoxPtr,
283    register_id: NonMandatoryRegisterId,
284    constant_out: *mut ConstantPtr,
285) -> ReturnOption {
286    match ergo_box_register_value(ergo_box_ptr, register_id, constant_out) {
287        Ok(is_some) => ReturnOption {
288            is_some,
289            error: std::ptr::null_mut(),
290        },
291        Err(e) => ReturnOption {
292            is_some: false, // Just a dummy value
293            error: Error::c_api_from(Err(e)),
294        },
295    }
296}
297
298/// Parse from JSON.  Supports Ergo Node/Explorer API and box values and token amount encoded as
299/// strings
300#[no_mangle]
301pub unsafe extern "C" fn ergo_lib_ergo_box_from_json(
302    json_str: *const c_char,
303    ergo_box_out: *mut ErgoBoxPtr,
304) -> ErrorPtr {
305    let json = CStr::from_ptr(json_str).to_string_lossy();
306    let res = ergo_box_from_json(&json, ergo_box_out);
307    Error::c_api_from(res)
308}
309
310/// JSON representation as text (compatible with Ergo Node/Explorer API, numbers are encoded as numbers)
311#[no_mangle]
312pub unsafe extern "C" fn ergo_lib_ergo_box_to_json(
313    ergo_box_ptr: ConstErgoBoxPtr,
314    _json_str: *mut *const c_char,
315) -> ErrorPtr {
316    #[allow(clippy::unwrap_used)]
317    let res = match ergo_box_to_json(ergo_box_ptr) {
318        Ok(s) => {
319            *_json_str = CString::new(s).unwrap().into_raw();
320            Ok(())
321        }
322        Err(e) => Err(e),
323    };
324    Error::c_api_from(res)
325}
326
327/// JSON representation according to EIP-12 <https://github.com/ergoplatform/eips/pull/23>
328#[no_mangle]
329pub unsafe extern "C" fn ergo_lib_ergo_box_to_json_eip12(
330    ergo_box_ptr: ConstErgoBoxPtr,
331    _json_str: *mut *const c_char,
332) -> ErrorPtr {
333    #[allow(clippy::unwrap_used)]
334    let res = match ergo_box_to_json_eip12(ergo_box_ptr) {
335        Ok(s) => {
336            *_json_str = CString::new(s).unwrap().into_raw();
337            Ok(())
338        }
339        Err(e) => Err(e),
340    };
341    Error::c_api_from(res)
342}
343
344/// Calculate serialized box size(in bytes)
345#[no_mangle]
346pub unsafe extern "C" fn ergo_lib_ergo_box_bytes_size(ergo_box_ptr: ConstErgoBoxPtr) -> usize {
347    #[allow(clippy::unwrap_used)]
348    ergo_box_bytes_size(ergo_box_ptr).unwrap()
349}
350
351/// Drop `ErgoBox`
352#[no_mangle]
353pub unsafe extern "C" fn ergo_lib_ergo_box_delete(ptr: ErgoBoxPtr) {
354    delete_ptr(ptr)
355}
356
357make_collection!(ErgoBoxes, ErgoBox);
358make_ffi_eq!(ErgoBox);
359
360// `ErgoBoxAssetsData` bindings ---------------------------------------------------------------------
361
362/// Create new instance
363#[no_mangle]
364pub unsafe extern "C" fn ergo_lib_ergo_box_assets_data_new(
365    value_ptr: ConstBoxValuePtr,
366    tokens_ptr: ConstCollectionPtr<Token>,
367    ergo_box_assets_data_out: *mut ErgoBoxAssetsDataPtr,
368) {
369    #[allow(clippy::unwrap_used)]
370    ergo_box_assets_data_new(value_ptr, tokens_ptr, ergo_box_assets_data_out).unwrap();
371}
372
373/// Value part of the box
374#[no_mangle]
375pub unsafe extern "C" fn ergo_lib_ergo_box_assets_data_value(
376    ergo_box_assets_data_ptr: ConstErgoBoxAssetsDataPtr,
377    value_out: *mut BoxValuePtr,
378) {
379    #[allow(clippy::unwrap_used)]
380    ergo_box_assets_data_value(ergo_box_assets_data_ptr, value_out).unwrap();
381}
382
383/// Tokens part of the box
384#[no_mangle]
385pub unsafe extern "C" fn ergo_lib_ergo_box_assets_data_tokens(
386    ergo_box_assets_data_ptr: ConstErgoBoxAssetsDataPtr,
387    tokens_out: *mut CollectionPtr<Token>,
388) {
389    #[allow(clippy::unwrap_used)]
390    ergo_box_assets_data_tokens(ergo_box_assets_data_ptr, tokens_out).unwrap();
391}
392
393/// Drop `ErgoBoxAssetsData`
394#[no_mangle]
395pub unsafe extern "C" fn ergo_lib_ergo_box_assets_data_delete(ptr: ErgoBoxAssetsDataPtr) {
396    delete_ptr(ptr)
397}
398
399make_collection!(ErgoBoxAssetsDataList, ErgoBoxAssetsData);
400make_ffi_eq!(ErgoBoxAssetsData);