libabieos_sys/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5//use std::ptr::null;
6use rust_embed::RustEmbed;
7use std::ffi::{CStr, CString};
8
9/// embed main ABI files into client.
10#[derive(RustEmbed)]
11#[folder = "resources/"]
12pub struct AbiFiles;
13
14include!("./bindings.rs");
15
16pub mod encodeABI;
17pub mod errors;
18
19//#[macro_use]
20extern crate error_chain;
21
22use crate::errors::{Error, ErrorKind, Result};
23use std::os::raw::c_char;
24
25pub type ABIName = u64;
26
27pub struct ABIEOS {
28    context: *mut abieos_context,
29}
30impl Default for ABIEOS {
31    fn default() -> Self {
32        ABIEOS::new()
33    }
34}
35impl ABIEOS {
36    ///
37    /// # Safety
38    /// make sure you call destroy  after use
39    pub fn new() -> ABIEOS {
40        unsafe {
41            let context: *mut abieos_context = abieos_create();
42            ABIEOS { context }
43        }
44    }
45
46    /// # Safety
47    ///
48    /// make sure you call destroy  after use
49    pub fn new_with_abi(contract_name: &str, abi: &str) -> Result<ABIEOS> {
50        unsafe {
51            let context: *mut abieos_context = abieos_create();
52            let abi_obj = ABIEOS { context };
53            abi_obj.set_abi(contract_name, abi).map_err(|e| {
54                abi_obj.destroy();
55                Error::with_chain(e, "new_with_abi")
56            })?;
57
58            Ok(abi_obj)
59        }
60    }
61
62    /// # Safety
63    ///
64    /// after destroy, don't use any other function
65    pub fn destroy(&self) {
66        unsafe {
67            abieos_destroy(self.context);
68        }
69        //  self.context = null();
70    }
71
72    pub fn set_abi(&self, contract_name: &str, abi: &str) -> Result<bool> {
73        let name = self.str_to_name(contract_name)?;
74        let abi_cs = CString::new(abi)?;
75        let result = unsafe { abieos_set_abi(self.context, name, abi_cs.as_ptr() as *const i8) };
76        if result == 0 {
77            self.abieos_error()?;
78        }
79
80        Ok(true)
81    }
82
83    pub fn str_to_name(&self, str_name: &str) -> Result<ABIName> {
84        let cs = CString::new(str_name)?;
85        let result = unsafe { abieos_string_to_name(self.context, cs.as_ptr() as *const i8) };
86        Ok(result)
87    }
88
89    pub fn hex_to_json(&self, contract_name: &str, type_str: &str, hex: &[u8]) -> Result<String> {
90        let name = self.str_to_name(contract_name)?;
91        let typeCS = CString::new(type_str).unwrap();
92        let hexCS = CString::new(hex).unwrap();
93        let json_p = unsafe {
94            abieos_hex_to_json(
95                self.context,
96                name,
97                typeCS.as_ptr(),
98                hexCS.as_ptr() as *const i8,
99            )
100        };
101
102        if json_p.is_null() {
103            self.abieos_error()?;
104            Err("FAIL".into()) // not reached
105        } else {
106            let json = unsafe { ABIEOS::fix_json(CStr::from_ptr(json_p).to_str()?)? };
107            Ok(json)
108        }
109    }
110
111    pub fn bin_to_json(&self, contract_name: &str, type_str: &str, hex: &[u8]) -> Result<String> {
112        let name = self.str_to_name(contract_name)?;
113        let typeCS = CString::new(type_str).unwrap();
114        // let hexCS = CString::new(hex).unwrap();
115        let json_p = unsafe {
116            abieos_bin_to_json(
117                self.context,
118                name,
119                typeCS.as_ptr(),
120                hex.as_ptr() as *const i8,
121                hex.len() as u64,
122            )
123        };
124
125        if json_p.is_null() {
126            self.abieos_error()?;
127            Err("FAIL".into()) // not reached
128        } else {
129            unsafe {
130                let json = ABIEOS::fix_json(CStr::from_ptr(json_p).to_str()?)?;
131                Ok(json.clone())
132            }
133        }
134    }
135    fn json_to_xx(&self, contract_name: &str, type_str: &str, json: &str) -> Result<i32> {
136        let name = self.str_to_name(contract_name)?;
137        let typeCS = CString::new(type_str).unwrap();
138        let jsonCS = CString::new(json).unwrap();
139        let result = unsafe {
140            abieos_json_to_bin_reorderable(
141                self.context,
142                name,
143                typeCS.as_ptr(),
144                jsonCS.as_ptr() as *const i8,
145            )
146        };
147        Ok(result)
148    }
149    ///
150    /// Safety
151    ///
152    /// abieos_get_xxx calls can potentially overwrite the memory returned on the next call.
153    pub fn json_to_hex(&self, contract_name: &str, type_str: &str, json: &str) -> Result<String> {
154        match self.json_to_xx(contract_name, type_str, json) {
155            Ok(0) => {
156                self.abieos_error()
157                    .map_err(|e| Error::with_chain(e, "json_to_hex"))?;
158                Err("FAIL".into()) // not reached
159            }
160            Ok(_) => {
161                unsafe {
162                    let hex_p = abieos_get_bin_hex(self.context);
163                    let s = CStr::from_ptr(hex_p);
164                    // this should copy the memory over
165                    Ok(String::from(s.to_str()?).clone())
166                }
167            }
168            Err(e) => Err(Error::with_chain(e, "json_to_hex")),
169        }
170    }
171
172    ///
173    /// Safety
174    ///
175    /// abieos_get_xxx calls can potentially overwrite the memory returned on the next call.
176    pub fn json_to_bin(&self, contract_name: &str, type_str: &str, json: &str) -> Result<Vec<u8>> {
177        match self.json_to_xx(contract_name, type_str, json) {
178            Ok(0) => {
179                self.abieos_error()?;
180                Err("FAIL".into()) // not reached
181            }
182            Ok(_) => unsafe {
183                let bin_size: usize = abieos_get_bin_size(self.context) as usize;
184                let bin: *const ::std::os::raw::c_char = abieos_get_bin_data(self.context);
185
186                let v: &[u8] = std::slice::from_raw_parts(bin as *const u8, bin_size);
187                // deliberate clone here due to memory being able to be reused by abieos lib
188                let ve: Vec<u8> = v.to_vec().clone();
189                Ok(ve)
190            },
191            Err(e) => Err(Error::with_chain(e, "json_to_hex")),
192        }
193    }
194
195    fn abieos_error(&self) -> Result<String> {
196        unsafe {
197            let err_raw: *const c_char = abieos_get_error(self.context);
198            if !err_raw.is_null() {
199                let err_s = CStr::from_ptr(err_raw).to_str()?;
200                Err(ErrorKind::ABIEOS(String::from(err_s)).into())
201            } else {
202                Err(ErrorKind::ABIEOS_INT.into())
203            }
204        }
205    }
206
207    // for some reason json returned has a 0x10 instead of a newline.
208    // before removing this. make sure you copy the output of the API call to rust-managed memory
209    //
210    fn fix_json(in_str: &str) -> Result<String> {
211        let mut x: String;
212        let mut i = 0;
213        x = in_str.replacen("\\u000A", "\\n", 999);
214        while x.contains("\\u000A") {
215            x = x.replacen("\\u000A", "\\n", 999);
216            i += 1;
217            if i > 100 {
218                return Err(ErrorKind::ABIEOS_LOOP.into());
219            }
220        }
221        Ok(x)
222    }
223}
224fn hex_to_bin_char(c: u8) -> u8 {
225    if (b'a'..=b'z').contains(&c) {
226        let v: u8 = (c - b'a') + 10;
227        return v;
228    }
229    if (b'A'..=b'Z').contains(&c) {
230        let v: u8 = (c - b'A') + 10;
231        return v;
232    }
233    if (b'0'..=b'9').contains(&c) {
234        let v = c - b'0';
235        return v;
236    }
237    0
238}
239
240pub fn hex_to_bin(hex: &str) -> Vec<u8> {
241    let mut bin: Vec<u8> = Vec::with_capacity(hex.len() / 2);
242    let bytes = hex.as_bytes();
243    let mut i = 0;
244    while i < bytes.len() {
245        let b = hex_to_bin_char(bytes[i]).checked_shl(4).unwrap() + hex_to_bin_char(bytes[i + 1]);
246        i += 2;
247        bin.push(b);
248    }
249    bin
250}
251pub fn varuint32_from_bin(bin_str: &[u8]) -> Result<(u32, Vec<u8>)> {
252    let mut dest: u32 = 0;
253    let mut shift = 0;
254    let mut i = 0;
255    let mut b: u8;
256    while {
257        if shift >= 35 {
258            return Err(ErrorKind::ABIEOS_VARUINT_ENCODING.into());
259        }
260        b = bin_str[i];
261        dest |= ((b & 0x7f) as u32).checked_shl(shift).unwrap();
262        shift += 7;
263        i += 1;
264        i < bin_str.len() && b & 0x80 != 0
265    } {}
266
267    Ok((dest, bin_str[i..].to_vec()))
268}
269pub fn varuint64_from_bin(bin_str: &[u8]) -> Result<(u64, Vec<u8>)> {
270    let mut dest: u64 = 0;
271    let mut shift = 0;
272    let mut i = 0;
273    let mut b: u8;
274    while {
275        if shift >= 70 {
276            return Err(ErrorKind::ABIEOS_VARUINT_ENCODING.into());
277        }
278        b = bin_str[i];
279        dest |= ((b & 0x7f) as u64).checked_shl(shift).unwrap();
280        shift += 7;
281        i += 1;
282        i < bin_str.len() && b & 0x80 != 0
283    } {}
284    Ok((dest, bin_str[i..].to_vec()))
285}
286
287pub mod eosio_datetime_format {
288    use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
289    use serde::{self, Deserialize, Deserializer, Serializer};
290
291    const FORMAT: &str = "%Y-%m-%dT%H:%M:%S";
292
293    // The signature of a serialize_with function must follow the pattern:
294    //
295    //    fn serialize<S>(&T, S) -> Result<S::Ok, S::Error>
296    //    where
297    //        S: Serializer
298    //
299    // although it may also be generic over the input types T.
300    #[allow(dead_code)]
301    pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
302    where
303        S: Serializer,
304    {
305        let s = format!("{}", date.format(FORMAT));
306        serializer.serialize_str(&s)
307    }
308
309    // The signature of a deserialize_with function must follow the pattern:
310    //
311    //    fn deserialize<'de, D>(D) -> Result<T, D::Error>
312    //    where
313    //        D: Deserializer<'de>
314    //
315    // although it may also be generic over the output types T.
316    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
317    where
318        D: Deserializer<'de>,
319    {
320        let s: String = String::deserialize(deserializer)?;
321        let len = s.len();
322        let slice_len = if s.contains('.') {
323            len.saturating_sub(4)
324        } else {
325            len
326        };
327
328        // match Utc.datetime_from_str(&s, FORMAT) {
329        let sliced = &s[0..slice_len];
330        match NaiveDateTime::parse_from_str(sliced, FORMAT) {
331            Err(_e) => {
332                eprintln!("DateTime Fail {} {:#?}", sliced, _e);
333                Err(serde::de::Error::custom(_e))
334            }
335            Ok(dt) => Ok(Utc.from_utc_datetime(&dt)),
336        }
337    }
338}
339#[cfg(test)]
340mod test {
341    use super::*;
342    use std::fs;
343
344    const JSON: &str = "{ \
345  \"expiration\": \"2018-08-02T20:24:36\", \
346  \"ref_block_num\": 14207, \
347  \"ref_block_prefix\": 1438248607, \
348  \"max_net_usage_words\": 0, \
349  \"max_cpu_usage_ms\": 0, \
350  \"delay_sec\": 0, \
351  \"context_free_actions\": [], \
352  \"actions\": [{ \
353      \"account\": \"eosio\", \
354      \"name\": \"newaccount\", \
355      \"authorization\": [{ \
356          \"actor\": \"eosio\", \
357          \"permission\": \"active\"  }], \
358      \"data\": \"0000000000ea305500a6823403ea30550100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d8010000000100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d801000000\" \
359    }], \
360  \"transaction_extensions\": []}";
361
362    const PACKED: &str = "8468635b7f379feeb95500000000010000000000ea305500409e9a2264b89a010000000000ea305500000000a8ed3232660000000000ea305500a6823403ea30550100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d8010000000100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d80100000000";
363
364    #[test]
365    fn tst_abi() -> Result<()> {
366        let hex = "0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d6505087472616e7366657200040466726f6d0c6163636f756e745f6e616d6502746f0c6163636f756e745f6e616d65087175616e74697479056173736574046d656d6f06737472696e67066372656174650002066973737565720c6163636f756e745f6e616d650e6d6178696d756d5f737570706c79056173736574056973737565000302746f0c6163636f756e745f6e616d65087175616e74697479056173736574046d656d6f06737472696e67076163636f756e7400010762616c616e63650561737365740e63757272656e63795f7374617473000306737570706c790561737365740a6d61785f737570706c79056173736574066973737565720c6163636f756e745f6e616d6503000000572d3ccdcd087472616e73666572bc072d2d2d0a7469746c653a20546f6b656e205472616e736665720a73756d6d6172793a205472616e7366657220746f6b656e732066726f6d206f6e65206163636f756e7420746f20616e6f746865722e0a69636f6e3a2068747470733a2f2f63646e2e746573746e65742e6465762e62316f70732e6e65742f746f6b656e2d7472616e736665722e706e6723636535316566396639656563613334333465383535303765306564343965373666666631323635343232626465643032353566333139366561353963386230630a2d2d2d0a0a2323205472616e73666572205465726d73202620436f6e646974696f6e730a0a492c207b7b66726f6d7d7d2c20636572746966792074686520666f6c6c6f77696e6720746f206265207472756520746f207468652062657374206f66206d79206b6e6f776c656467653a0a0a312e204920636572746966792074686174207b7b7175616e746974797d7d206973206e6f74207468652070726f6365656473206f66206672617564756c656e74206f722076696f6c656e7420616374697669746965732e0a322e2049206365727469667920746861742c20746f207468652062657374206f66206d79206b6e6f776c656467652c207b7b746f7d7d206973206e6f7420737570706f7274696e6720696e6974696174696f6e206f662076696f6c656e636520616761696e7374206f74686572732e0a332e2049206861766520646973636c6f73656420616e7920636f6e747261637475616c207465726d73202620636f6e646974696f6e732077697468207265737065637420746f207b7b7175616e746974797d7d20746f207b7b746f7d7d2e0a0a4920756e6465727374616e6420746861742066756e6473207472616e736665727320617265206e6f742072657665727369626c6520616674657220746865207b7b247472616e73616374696f6e2e64656c61795f7365637d7d207365636f6e6473206f72206f746865722064656c617920617320636f6e66696775726564206279207b7b66726f6d7d7d2773207065726d697373696f6e732e0a0a4966207468697320616374696f6e206661696c7320746f20626520697272657665727369626c7920636f6e6669726d656420616674657220726563656976696e6720676f6f6473206f722073657276696365732066726f6d20277b7b746f7d7d272c204920616772656520746f206569746865722072657475726e2074686520676f6f6473206f72207365727669636573206f7220726573656e64207b7b7175616e746974797d7d20696e20612074696d656c79206d616e6e65722e0000000000a531760569737375650000000000a86cd445066372656174650002000000384f4d113203693634010863757272656e6379010675696e743634076163636f756e740000000000904dc603693634010863757272656e6379010675696e7436340e63757272656e63795f737461747300000000";
367        let jsonResult = "{\"version\":\"eosio::abi/1.0\",\"types\":[{\"new_type_name\":\"account_name\",\"type\":\"name\"}],\"structs\":[{\"name\":\"transfer\",\"base\":\"\",\"fields\":[{\"name\":\"from\",\"type\":\"account_name\"},{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"create\",\"base\":\"\",\"fields\":[{\"name\":\"issuer\",\"type\":\"account_name\"},{\"name\":\"maximum_supply\",\"type\":\"asset\"}]},{\"name\":\"issue\",\"base\":\"\",\"fields\":[{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"account\",\"base\":\"\",\"fields\":[{\"name\":\"balance\",\"type\":\"asset\"}]},{\"name\":\"currency_stats\",\"base\":\"\",\"fields\":[{\"name\":\"supply\",\"type\":\"asset\"},{\"name\":\"max_supply\",\"type\":\"asset\"},{\"name\":\"issuer\",\"type\":\"account_name\"}]}],\"actions\":[{\"name\":\"transfer\",\"type\":\"transfer\",\"ricardian_contract\":\"---\\ntitle: Token Transfer\\nsummary: Transfer tokens from one account to another.\\nicon: https://cdn.testnet.dev.b1ops.net/token-transfer.png#ce51ef9f9eeca3434e85507e0ed49e76fff1265422bded0255f3196ea59c8b0c\\n---\\n\\n## Transfer Terms & Conditions\\n\\nI, {{from}}, certify the following to be true to the best of my knowledge:\\n\\n1. I certify that {{quantity}} is not the proceeds of fraudulent or violent activities.\\n2. I certify that, to the best of my knowledge, {{to}} is not supporting initiation of violence against others.\\n3. I have disclosed any contractual terms & conditions with respect to {{quantity}} to {{to}}.\\n\\nI understand that funds transfers are not reversible after the {{$transaction.delay_sec}} seconds or other delay as configured by {{from}}'s permissions.\\n\\nIf this action fails to be irreversibly confirmed after receiving goods or services from '{{to}}', I agree to either return the goods or services or resend {{quantity}} in a timely manner.\"},{\"name\":\"issue\",\"type\":\"issue\",\"ricardian_contract\":\"\"},{\"name\":\"create\",\"type\":\"create\",\"ricardian_contract\":\"\"}],\"tables\":[{\"name\":\"accounts\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"account\"},{\"name\":\"stat\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"currency_stats\"}],\"ricardian_clauses\":[],\"error_messages\":[],\"abi_extensions\":[],\"variants\":[]}";
368        let abi = fs::read_to_string("abi.abi.json").unwrap();
369
370        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
371        let do_hex_2_json = || -> Result<String> {
372            let json = abieos.hex_to_json("eosio", "abi_def", hex.as_bytes())?;
373            assert_eq!(jsonResult, json);
374            Ok(String::from(json))
375        }();
376        let do_json_2_hex = || -> Result<String> {
377            let hex_out = abieos.json_to_hex("eosio", "abi_def", jsonResult)?;
378            assert_eq!(hex_out.to_ascii_lowercase(), hex);
379            Ok(hex_out)
380        }();
381        abieos.destroy();
382        do_hex_2_json?;
383        do_json_2_hex?;
384
385        Ok(())
386    }
387
388    #[test]
389    fn tst_txn() -> Result<()> {
390        let hex = "AE0D635CDCAC90A6DCFA000000000100A6823403EA3055000000572D3CCDCD0100AEAA4AC15CFD4500000000A8ED32323B00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C69657320686561767900";
391        let jsonResult = "{\"expiration\":\"2019-02-12T18:17:18.000\",\"ref_block_num\":44252,\"ref_block_prefix\":4208764560,\"max_net_usage_words\":0,\"max_cpu_usage_ms\":0,\"delay_sec\":0,\"context_free_actions\":[],\"actions\":[{\"account\":\"eosio.token\",\"name\":\"transfer\",\"authorization\":[{\"actor\":\"cryptkeeper\",\"permission\":\"active\"}],\"data\":\"00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C696573206865617679\"}],\"transaction_extensions\":[]}";
392        let abi = fs::read_to_string("transaction.abi.json").unwrap();
393
394        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
395        let do_hex_2_json = || -> Result<String> {
396            let json = abieos.hex_to_json("eosio", "transaction", hex.as_bytes())?;
397            assert_eq!(jsonResult, json);
398            Ok(String::from(json))
399        }();
400        let do_json_2_hex = || -> Result<String> {
401            let hex_out = abieos.json_to_hex("eosio", "transaction", jsonResult)?;
402            assert_eq!(hex_out.to_ascii_uppercase(), hex);
403
404            Ok(hex_out)
405        }();
406        abieos.destroy();
407        do_json_2_hex?;
408        do_hex_2_json?;
409
410        Ok(())
411    }
412
413    #[test]
414    pub fn test_from_example_2h() -> Result<()> {
415        let abi = fs::read_to_string("transaction.abi.json").unwrap();
416
417        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
418
419        let do_json_2_hex = || -> Result<String> {
420            let hex_out = abieos.json_to_hex("eosio", "transaction", JSON)?;
421            assert_eq!(hex_out.to_ascii_lowercase(), PACKED);
422            Ok(hex_out)
423        }();
424        abieos.destroy();
425        do_json_2_hex?;
426
427        Ok(())
428    }
429
430    #[test]
431    pub fn test_ttt_abi() -> Result<()> {
432        let test_abi = fs::read_to_string("test/good-2.abi").unwrap();
433        let abi = fs::read_to_string("abi.abi.json").unwrap();
434
435        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
436        let hex_out = abieos.json_to_hex("eosio", "abi_def", &test_abi);
437        abieos.destroy();
438        hex_out?;
439
440        Ok(())
441    }
442
443    #[test]
444    pub fn test_ttt_abi_bin() -> Result<()> {
445        let test_abi = fs::read_to_string("test/good-2.abi").unwrap();
446        let abi = fs::read_to_string("abi.abi.json").unwrap();
447
448        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
449        let hex_out = abieos.json_to_hex("eosio", "abi_def", &test_abi);
450        let bin_out = abieos.json_to_bin("eosio", "abi_def", &test_abi);
451        abieos.destroy();
452        let hex = hex_out?.to_string();
453        let bin = bin_out?;
454        let mut bin_h: String = String::with_capacity(bin.len() * 2);
455        for u in &bin {
456            let hex_bit = char_to_hex(u)?.into_bytes();
457            bin_h.push(char::from(hex_bit[0]));
458            bin_h.push(char::from(hex_bit[1]));
459        }
460        assert_eq!(hex.to_ascii_lowercase(), bin_h.to_ascii_lowercase());
461        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
462        let _json_out = abieos.bin_to_json("eosio", "abi_def", &bin).map_err(|e| {
463            abieos.destroy();
464            Error::with_chain(e, "parsing shipper abi")
465        })?;
466        //assert_eq!(test_abi,json_out);
467
468        Ok(())
469    }
470
471    #[test]
472    pub fn test_from_example_2j() -> Result<()> {
473        let abi = fs::read_to_string("transaction.abi.json").unwrap();
474
475        let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
476        let _do_hex_2_json = || -> Result<String> {
477            let json = abieos.hex_to_json("eosio", "transaction", PACKED.as_bytes())?;
478            let hex_out = abieos.json_to_hex("eosio", "transaction", &json)?;
479            assert_eq!(hex_out.to_ascii_lowercase(), PACKED);
480            Ok(json)
481        }();
482        abieos.destroy();
483
484        Ok(())
485    }
486    #[test]
487    fn test_varuint32() -> Result<()> {
488        let hex_str = "b6843d0123";
489        let hex_str2 = "01002BAD64FF47";
490        let hex_str3 = "ffffffff0faa";
491        let hex_str4 = "ffffffffff0faa";
492        let hex_str3_mixed = "FFfffFff0fAa";
493        let bin_str = hex_to_bin(&hex_str);
494        let (val, bin_str_ex) = varuint32_from_bin(&bin_str)?;
495        assert_eq!(999990, val);
496        assert_eq!(bin_str_ex.len(), 2);
497        assert_eq!(bin_str_ex[0], 01);
498        assert_eq!(bin_str_ex[1], 0x23);
499        let bin_str2 = hex_to_bin(&hex_str2);
500        let (val, bin_str_ex) = varuint32_from_bin(&bin_str2)?;
501        assert_eq!(1, val);
502        assert_eq!(bin_str_ex.len(), 6);
503        let (val64, bin_str_ex) = varuint64_from_bin(&bin_str)?;
504        assert_eq!(999990, val64);
505        assert_eq!(bin_str_ex.len(), 2);
506        assert_eq!(bin_str_ex[0], 01);
507        let bin_str3 = hex_to_bin(&hex_str3);
508        let (val, _bin_str_ex) = varuint32_from_bin(&bin_str3)?;
509        assert_eq!(0xff_ff_ff_ff, val);
510        let bin_str4 = hex_to_bin(&hex_str4);
511        let v: Result<(u32, Vec<u8>)> = varuint32_from_bin(&bin_str4);
512        assert!(v.is_err());
513        let v: Result<(u64, Vec<u8>)> = varuint64_from_bin(&bin_str4);
514        assert!(v.is_ok());
515        assert_eq!(0x7f_ff_ff_ff_ff, v?.0);
516        let bin_str3_mixed = hex_to_bin(hex_str3_mixed);
517        assert_eq!(bin_str3, bin_str3_mixed);
518
519        Ok(())
520    }
521
522    fn char_to_hex(c: &u8) -> Result<String> {
523        let mut r: [u8; 2] = [0; 2];
524        let b1_1 = c & 0xf0;
525        let b1 = b1_1.checked_shr(4).unwrap_or(0);
526        if b1 >= 10 {
527            r[0] = b1 - 10 + b'a';
528        } else {
529            r[0] = b1 + b'0';
530        }
531        let b1 = c & 0x0f;
532        if b1 >= 10 {
533            r[1] = b1 - 10 + b'a';
534        } else {
535            r[1] = b1 + b'0';
536        }
537        Ok(String::from_utf8(r.to_vec())?)
538    }
539}
540fn byte_to_char(x: u8) -> char {
541    (if x <= 9 { x + b'0' } else { x - 10 + b'a' }) as char
542}
543
544pub fn vec_u8_to_hex(out: &[u8]) -> Result<String> {
545    let mut str = String::with_capacity(out.len());
546    for x in out {
547        str.push(byte_to_char((x & 0xf0).checked_shr(4).unwrap_or(0)));
548        str.push(byte_to_char(x & 0x0f));
549    }
550    Ok(str)
551}