ergo_lib_wasm/
ast.rs

1//! Ergo constant values
2
3use crate::ast::js_conv::constant_from_js;
4use crate::ast::js_conv::constant_to_js;
5use crate::ergo_box::ErgoBox;
6use crate::error_conversion::to_js;
7use crate::utils::I64;
8use ergo_lib::ergo_chain_types::Base16DecodedBytes;
9use ergo_lib::ergo_chain_types::EcPoint;
10use ergo_lib::ergotree_ir::base16_str::Base16Str;
11use ergo_lib::ergotree_ir::chain::ergo_box::RegisterValue;
12use ergo_lib::ergotree_ir::mir::constant::{TryExtractFrom, TryExtractInto};
13use ergo_lib::ergotree_ir::serialization::SigmaSerializable;
14use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
15use gloo_utils::format::JsValueSerdeExt;
16use js_sys::Uint8Array;
17use std::convert::TryFrom;
18use wasm_bindgen::prelude::*;
19
20extern crate derive_more;
21use derive_more::{From, Into};
22use ergo_lib::ergotree_ir::bigint256::BigInt256;
23
24pub mod js_conv;
25
26/// Ergo constant(evaluated) values
27#[wasm_bindgen]
28#[derive(PartialEq, Eq, Debug, Clone, From, Into)]
29pub struct Constant(ergo_lib::ergotree_ir::mir::constant::Constant);
30
31#[wasm_bindgen]
32impl Constant {
33    /// Returns the debug representation of the type of the constant
34    pub fn dbg_tpe(&self) -> String {
35        format!("{:?}", self.0.tpe)
36    }
37
38    /// Returns the debug representation of the value of the constant
39    pub fn dbg_inner(&self) -> String {
40        format!("{:?}", self.0)
41    }
42
43    /// Decode from Base16-encoded ErgoTree serialized value
44    pub fn decode_from_base16(base16_bytes_str: String) -> Result<Constant, JsValue> {
45        let bytes = Base16DecodedBytes::try_from(base16_bytes_str.clone()).map_err(|_| {
46            JsValue::from_str(&format!(
47                "failed to decode base16 from: {}",
48                base16_bytes_str.clone()
49            ))
50        })?;
51
52        let register_value = RegisterValue::sigma_parse_bytes(bytes.as_ref());
53        register_value
54            .as_constant()
55            .cloned()
56            .map_err(to_js)
57            .map(Constant)
58    }
59
60    /// Encode as Base16-encoded ErgoTree serialized value or return an error if serialization
61    /// failed
62    pub fn encode_to_base16(&self) -> Result<String, JsValue> {
63        self.0.base16_str().map_err(to_js)
64    }
65
66    /// Returns serialized bytes or fails with error if Constant cannot be serialized
67    pub fn sigma_serialize_bytes(&self) -> Result<Vec<u8>, JsValue> {
68        self.0
69            .sigma_serialize_bytes()
70            .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))
71    }
72
73    /// Create from i32 value
74    pub fn from_i32(v: i32) -> Constant {
75        Constant(v.into())
76    }
77
78    /// Extract i32 value, returning error if wrong type
79    pub fn to_i32(&self) -> Result<i32, JsValue> {
80        i32::try_extract_from(self.0.clone()).map_err(to_js)
81    }
82
83    /// Create from i64
84    pub fn from_i64(v: &I64) -> Constant {
85        Constant(i64::from((*v).clone()).into())
86    }
87
88    /// Extract i64 value, returning error if wrong type
89    pub fn to_i64(&self) -> Result<I64, JsValue> {
90        i64::try_extract_from(self.0.clone())
91            .map_err(to_js)
92            .map(I64::from)
93    }
94
95    /// Create BigInt constant from byte array (signed bytes bit-endian)
96    pub fn from_bigint_signed_bytes_be(num: &[u8]) -> Result<Constant, JsValue> {
97        Ok(Constant(
98            ergo_lib::ergotree_ir::mir::constant::Constant::from(BigInt256::try_from(num)?),
99        ))
100    }
101
102    /// Create from byte array
103    pub fn from_byte_array(v: &[u8]) -> Constant {
104        Constant(v.to_vec().into())
105    }
106
107    /// Extract byte array, returning error if wrong type
108    pub fn to_byte_array(&self) -> Result<Uint8Array, JsValue> {
109        Vec::<u8>::try_extract_from(self.0.clone())
110            .map(|v| Uint8Array::from(v.as_slice()))
111            .map_err(to_js)
112    }
113
114    /// Create `Coll[Int]` from integer array
115    #[allow(clippy::boxed_local)]
116    pub fn from_i32_array(arr: Box<[i32]>) -> Result<Constant, JsValue> {
117        arr.iter()
118            .try_fold(vec![], |mut acc, l| {
119                acc.push(*l);
120                Ok(acc)
121            })
122            .map(|longs| longs.into())
123            .map(Constant)
124    }
125
126    /// Extract `Coll[Int]` as integer array
127    pub fn to_i32_array(&self) -> Result<Vec<i32>, JsValue> {
128        self.0
129            .clone()
130            .try_extract_into::<Vec<i32>>()
131            .map_err(|e| JsValue::from_str(&format!("Constant has wrong type: {:?}", e)))
132    }
133
134    /// Create `Coll[Long]` from string array
135    #[allow(clippy::boxed_local)]
136    pub fn from_i64_str_array(arr: Box<[JsValue]>) -> Result<Constant, JsValue> {
137        arr.iter()
138            .try_fold(vec![], |mut acc, l| {
139                let b: i64 = if l.is_string() {
140                    let l_str = l
141                        .as_string()
142                        .ok_or_else(|| JsValue::from_str("i64 as a string"))?;
143                    serde_json::from_str(l_str.as_str())
144                } else {
145                    JsValueSerdeExt::into_serde(l)
146                }
147                .map_err(|e| {
148                    JsValue::from_str(&format!(
149                        "Failed to parse i64 from JSON string: {:?} \n with error: {:?}",
150                        l, e
151                    ))
152                })?;
153                acc.push(b);
154                Ok(acc)
155            })
156            .map(|longs| longs.into())
157            .map(Constant)
158    }
159
160    /// Extract `Coll[Long]` as string array
161    #[allow(clippy::boxed_local)]
162    pub fn to_i64_str_array(&self) -> Result<Box<[JsValue]>, JsValue> {
163        let vec_i64 = self
164            .0
165            .clone()
166            .try_extract_into::<Vec<i64>>()
167            .map_err(|e| JsValue::from_str(&format!("Constant has wrong type: {:?}", e)))?;
168        Ok(vec_i64
169            .iter()
170            .map(|it| JsValue::from_str(&it.to_string()))
171            .collect())
172    }
173
174    /// Extract `Coll[Coll[Byte]]` as array of byte arrays
175    pub fn to_coll_coll_byte(&self) -> Result<Vec<Uint8Array>, JsValue> {
176        let vec_coll_byte = self
177            .0
178            .clone()
179            .try_extract_into::<Vec<Vec<u8>>>()
180            .map_err(to_js)?;
181        Ok(vec_coll_byte
182            .iter()
183            .map(|it| Uint8Array::from(it.as_slice()))
184            .collect())
185    }
186
187    /// Create `Coll[Coll[Byte]]` from array byte array
188    pub fn from_coll_coll_byte(arr: Vec<Uint8Array>) -> Constant {
189        let mut acc: Vec<Vec<u8>> = vec![];
190        for bytes in arr.iter() {
191            acc.push(bytes.to_vec());
192        }
193        let c = ergo_lib::ergotree_ir::mir::constant::Constant::from(acc);
194        c.into()
195    }
196
197    /// Parse raw `EcPoint` value from bytes and make `ProveDlog` constant
198    pub fn from_ecpoint_bytes(bytes: &[u8]) -> Result<Constant, JsValue> {
199        let ecp = EcPoint::sigma_parse_bytes(bytes).map_err(to_js)?;
200        let c: ergo_lib::ergotree_ir::mir::constant::Constant = ProveDlog::new(ecp).into();
201        Ok(c.into())
202    }
203
204    /// Parse raw `EcPoint` value from bytes and make `GroupElement` constant
205    pub fn from_ecpoint_bytes_group_element(bytes: &[u8]) -> Result<Constant, JsValue> {
206        let ecp = EcPoint::sigma_parse_bytes(bytes).map_err(to_js)?;
207        let c = ergo_lib::ergotree_ir::mir::constant::Constant::from(ecp);
208        Ok(c.into())
209    }
210
211    /// Create `(Coll[Byte], Coll[Byte])` tuple Constant
212    pub fn from_tuple_coll_bytes(bytes1: &[u8], bytes2: &[u8]) -> Constant {
213        let t = (bytes1.to_vec(), bytes2.to_vec());
214        let c: ergo_lib::ergotree_ir::mir::constant::Constant = t.into();
215        c.into()
216    }
217
218    /// Extract `(Coll[Byte], Coll[Byte])` tuple from Constant as array of Uint8Array
219    pub fn to_tuple_coll_bytes(&self) -> Result<Vec<Uint8Array>, JsValue> {
220        let (bytes1, bytes2) = self
221            .0
222            .clone()
223            .try_extract_into::<(Vec<u8>, Vec<u8>)>()
224            .map_err(to_js)?;
225        Ok(vec![
226            Uint8Array::from(bytes1.as_slice()),
227            Uint8Array::from(bytes2.as_slice()),
228        ])
229    }
230
231    /// Create `(Int, Int)` tuple Constant
232    pub fn to_tuple_i32(&self) -> Result<Vec<JsValue>, JsValue> {
233        let (i1, i2) = self
234            .0
235            .clone()
236            .try_extract_into::<(i32, i32)>()
237            .map_err(to_js)?;
238        Ok(vec![
239            JsValue::from_str(&i1.to_string()),
240            JsValue::from_str(&i2.to_string()),
241        ])
242    }
243
244    /// Create `(Long, Long)` tuple Constant
245    pub fn from_tuple_i64(l1: &I64, l2: &I64) -> Constant {
246        let c: ergo_lib::ergotree_ir::mir::constant::Constant =
247            (i64::from((*l1).clone()), i64::from((*l2).clone())).into();
248        c.into()
249    }
250
251    /// Extract `(Long, Long)` tuple from Constant as array of strings
252    pub fn to_tuple_i64(&self) -> Result<Vec<JsValue>, JsValue> {
253        let (l1, l2) = self
254            .0
255            .clone()
256            .try_extract_into::<(i64, i64)>()
257            .map_err(to_js)?;
258        Ok(vec![
259            JsValue::from_str(&l1.to_string()),
260            JsValue::from_str(&l2.to_string()),
261        ])
262    }
263
264    /// Create from ErgoBox value
265    pub fn from_ergo_box(v: &ErgoBox) -> Constant {
266        let b: ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox = v.clone().into();
267        let c: ergo_lib::ergotree_ir::mir::constant::Constant = b.into();
268        Constant(c)
269    }
270
271    /// Extract ErgoBox value, returning error if wrong type
272    pub fn to_ergo_box(&self) -> Result<ErgoBox, JsValue> {
273        self.0
274            .clone()
275            .try_extract_into::<ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox>()
276            .map(Into::into)
277            .map_err(to_js)
278    }
279
280    /// Create Constant with Unit value
281    pub fn unit() -> Constant {
282        Constant(().into())
283    }
284
285    /// Returns true if constant value is Unit
286    pub fn is_unit(&self) -> bool {
287        self.0.tpe == ergo_lib::ergotree_ir::types::stype::SType::SUnit
288    }
289
290    /// Create a Constant from JS value
291    /// JS types are converted to the following Ergo types:
292    /// Number -> Int,
293    /// String -> Long,
294    /// BigInt -> BigInt,
295    /// use array_as_tuple() to encode Ergo tuples
296    pub fn from_js(value: &JsValue) -> Result<Constant, JsValue> {
297        constant_from_js(value).map(Into::into).map_err(to_js)
298    }
299
300    /// Extract JS value from Constant
301    /// Ergo types are converted to the following JS types:
302    /// Byte -> Number,
303    /// Short -> Number,
304    /// Int -> Number,
305    /// Long -> String,
306    /// BigInt -> BigInt,
307    /// Ergo tuples are encoded as arrays
308    pub fn to_js(&self) -> Result<JsValue, JsValue> {
309        constant_to_js(self.0.clone()).map_err(to_js)
310    }
311}