lyquor_primitives/
lib.rs

1extern crate self as lyquor_primitives;
2pub extern crate serde;
3
4use std::fmt;
5use std::sync::Arc;
6
7pub use alloy_primitives::hex;
8pub use alloy_primitives::{self, Address, B256, U32, U64, U128, U256, address, uint};
9pub use blake3;
10pub use bytes::{self, Bytes};
11pub use cb58;
12pub use serde::{Deserialize, Serialize};
13use typed_builder::TypedBuilder;
14
15mod id;
16pub mod oracle;
17pub use id::{LyquidID, LyquidNumber, NodeID, RequiredLyquid, SequenceBackendID, sequence_backend_id};
18
19// Custom serde module for Arc<Option<T>>
20pub mod arc_option_serde {
21    use super::*;
22    use serde::{Deserialize, Deserializer, Serialize, Serializer};
23
24    pub fn serialize<T, S>(value: &Option<Arc<T>>, serializer: S) -> Result<S::Ok, S::Error>
25    where
26        T: Serialize,
27        S: Serializer,
28    {
29        match value {
30            Some(arc) => Some(arc.as_ref()).serialize(serializer),
31            None => Option::<&T>::None.serialize(serializer),
32        }
33    }
34
35    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<Arc<T>>, D::Error>
36    where
37        T: Deserialize<'de>,
38        D: Deserializer<'de>,
39    {
40        let opt = Option::<T>::deserialize(deserializer)?;
41        Ok(opt.map(Arc::new))
42    }
43}
44
45pub type Hash = blake3::Hash;
46
47// Network definitions moved to lyquor-api::profile.
48
49/// Position of a slot in the sequencer's backend.
50///
51/// Typically, a sequencing backend may be a chain that carries Lyquid slots in some of its blocks.
52/// This means the [SlotNumber]s do not necessarily correspond to continuous [ChainPos] in the sequencing backend.
53#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
54pub struct ChainPos(u128);
55
56impl ChainPos {
57    pub const ZERO: Self = Self(0);
58    pub fn new(block_position: u64, block_index: u32) -> Self {
59        Self((block_position as u128) << 32 | (block_index as u128))
60    }
61
62    #[inline(always)]
63    pub fn block(&self) -> u64 {
64        (self.0 >> 32) as u64
65    }
66
67    #[inline(always)]
68    pub fn block_index(&self) -> u32 {
69        self.0 as u32
70    }
71
72    #[inline(always)]
73    pub fn next_block(&self) -> Self {
74        Self(((self.0 >> 32) + 1) << 32)
75    }
76}
77
78impl fmt::Display for ChainPos {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
80        write!(f, "(block={}, log={})", self.0 >> 32, self.0 as u32)
81    }
82}
83
84impl fmt::Debug for ChainPos {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
86        fmt::Display::fmt(self, f)
87    }
88}
89
90#[derive(Serialize, Deserialize)]
91#[serde(rename_all = "camelCase")]
92struct HumanReadableChainPos {
93    block_number: U64,
94    block_index: U32,
95}
96
97impl Serialize for ChainPos {
98    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99    where
100        S: serde::Serializer,
101    {
102        if serializer.is_human_readable() {
103            HumanReadableChainPos {
104                block_number: U64::from(self.block()),
105                block_index: U32::from(self.block_index()),
106            }
107            .serialize(serializer)
108        } else {
109            serializer.serialize_u128(self.0)
110        }
111    }
112}
113
114impl<'de> Deserialize<'de> for ChainPos {
115    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116    where
117        D: serde::Deserializer<'de>,
118    {
119        if deserializer.is_human_readable() {
120            let HumanReadableChainPos {
121                block_number,
122                block_index,
123            } = HumanReadableChainPos::deserialize(deserializer)?;
124            Ok(Self::new(block_number.to::<u64>(), block_index.to::<u32>()))
125        } else {
126            Ok(Self(u128::deserialize(deserializer)?))
127        }
128    }
129}
130
131#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
132pub enum InputABI {
133    Lyquor,
134    Eth,
135}
136
137#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
138pub enum TriggerMode {
139    /// Trigger repeatedly at the specified interval in milliseconds.
140    Recurrent(u64),
141    /// Trigger once, delayed by the specified milliseconds (0 for immediate).
142    Once(u64),
143    /// Trigger once immediately and wait for completion.
144    Sync,
145    /// Stop the trigger (remove it from the registry).
146    Stop,
147}
148
149#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
150pub struct HashBytes(Hash);
151
152impl HashBytes {
153    pub fn new(hash: Hash) -> Self {
154        Self(hash)
155    }
156
157    pub fn into_inner(self) -> Hash {
158        self.0
159    }
160}
161
162impl std::ops::Deref for HashBytes {
163    type Target = Hash;
164    fn deref(&self) -> &Hash {
165        &self.0
166    }
167}
168
169impl From<[u8; 32]> for HashBytes {
170    fn from(hash: [u8; 32]) -> Self {
171        Self(hash.into())
172    }
173}
174
175impl From<HashBytes> for [u8; 32] {
176    fn from(hash: HashBytes) -> Self {
177        hash.0.into()
178    }
179}
180
181impl From<Hash> for HashBytes {
182    fn from(hash: Hash) -> Self {
183        Self(hash)
184    }
185}
186
187impl From<HashBytes> for Hash {
188    fn from(hash_bytes: HashBytes) -> Self {
189        hash_bytes.0
190    }
191}
192
193impl Serialize for HashBytes {
194    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195    where
196        S: serde::Serializer,
197    {
198        let bytes: &[u8; 32] = self.0.as_bytes();
199        serializer.serialize_bytes(bytes)
200    }
201}
202
203impl<'de> Deserialize<'de> for HashBytes {
204    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205    where
206        D: serde::Deserializer<'de>,
207    {
208        use serde::de::Error;
209        let bytes = <Vec<u8>>::deserialize(deserializer)?;
210        if bytes.len() != 32 {
211            return Err(D::Error::custom(format!(
212                "Expected 32 bytes for HashBytes, got {}",
213                bytes.len()
214            )));
215        }
216        let mut array = [0u8; 32];
217        array.copy_from_slice(&bytes);
218        Ok(HashBytes(blake3::Hash::from(array)))
219    }
220}
221
222#[derive(Serialize, Deserialize, PartialEq, Clone, TypedBuilder)]
223pub struct CallParams {
224    /// The ultimate origin of the call (the transaction signer, for example if the call comes from
225    /// the chain. The default is zero address when unused.
226    #[builder(default = Address::ZERO)]
227    pub origin: Address,
228    /// The direct caller.
229    pub caller: Address,
230    #[builder(default = GROUP_DEFAULT.into())]
231    pub group: String,
232    pub method: String,
233    pub input: Bytes,
234    #[builder(default = InputABI::Lyquor)]
235    pub abi: InputABI,
236}
237
238impl Eq for CallParams {}
239
240impl fmt::Debug for CallParams {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
242        write!(
243            f,
244            "CallParams(caller={}, origin={}, group={}, method={}, input={}, abi={:?})",
245            self.caller,
246            self.origin,
247            self.group,
248            self.method,
249            hex::encode(&self.input),
250            self.abi
251        )
252    }
253}
254
255pub const GROUP_DEFAULT: &str = "main";
256pub const GROUP_NODE: &str = "node";
257pub const GROUP_UPC_PREPARE: &str = "upc::prepare";
258pub const GROUP_UPC_REQ: &str = "upc::request";
259pub const GROUP_UPC_RESP: &str = "upc::response";
260
261pub type LyteLogTopic = B256;
262
263#[derive(Serialize, Deserialize, Clone)]
264pub struct LyteLog {
265    pub topics: [Option<Box<LyteLogTopic>>; 4],
266    pub data: Bytes,
267}
268
269impl LyteLog {
270    pub fn new_from_tagged_value<V: Serialize>(tag: &str, value: &V) -> Self {
271        let topic0 = Box::new(Self::tagged_value_topic(tag));
272        Self {
273            topics: [Some(topic0), None, None, None],
274            data: encode_object(value).into(),
275        }
276    }
277
278    pub fn tagged_value_topic(tag: &str) -> LyteLogTopic {
279        let mut hasher = blake3::Hasher::new();
280        hasher.update(tag.as_bytes());
281        let topic: [u8; 32] = hasher.finalize().into();
282        topic.into()
283    }
284}
285
286#[derive(Serialize, Deserialize, Clone, Debug)]
287pub struct RegisterEvent {
288    pub id: LyquidID,
289    pub deps: Vec<LyquidID>,
290}
291
292impl fmt::Debug for LyteLog {
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
294        write!(
295            f,
296            "LyteLog(topics={}, data=<{} bytes>)",
297            self.topics
298                .iter()
299                .map(|t| match t {
300                    Some(t) => t.to_string(),
301                    None => "_".into(),
302                })
303                .collect::<Vec<_>>()
304                .join(", "),
305            self.data.len()
306        )
307    }
308}
309
310pub fn decode_object<T: for<'a> Deserialize<'a>>(raw: &[u8]) -> Option<T> {
311    postcard::from_bytes(raw).ok()
312}
313
314pub fn encode_object<T: Serialize + ?Sized>(obj: &T) -> Vec<u8> {
315    postcard::to_stdvec(obj).expect("postcard serialization failed")
316}
317
318pub fn encode_object_with_prefix<T: Serialize + ?Sized>(prefix: &[u8], obj: &T) -> Vec<u8> {
319    let mut vec = Vec::with_capacity(prefix.len() + core::mem::size_of_val(obj));
320    vec.extend_from_slice(prefix);
321    postcard::to_io(obj, &mut vec).expect("postcard serialization failed");
322    vec
323}
324
325#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
326pub enum StateCategory {
327    Network,
328    Instance,
329}
330
331impl StateCategory {
332    pub fn as_runtime(&self) -> u8 {
333        match self {
334            Self::Instance => 0x1,
335            Self::Network => 0x2,
336        }
337    }
338}
339
340#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Copy, Clone)]
341#[serde(rename_all = "camelCase")]
342pub enum ConsoleSink {
343    StdOut,
344    StdErr,
345}
346
347pub fn encode_method_name(cat_prefix: &str, group: &str, method: &str) -> String {
348    let mut output = cat_prefix.to_string();
349    output.push('_');
350    cb58::bs58::encode(group.as_bytes()).onto(&mut output).unwrap();
351    output.push('_');
352    output.push_str(method);
353    output
354}
355
356#[doc(hidden)]
357#[macro_export]
358macro_rules! object_by_fields_ {
359    ($serde_crate: tt, $($var:ident: $type:ty = $val:expr),*) => {{
360        #[allow(non_camel_case_types)]
361        #[derive($crate::Serialize, Clone)]
362        #[serde(crate = $serde_crate)]
363        struct parameters { $($var:$type),* }
364        parameters { $($var: $val),* }
365    }};
366}
367
368#[macro_export]
369macro_rules! object_by_fields {
370    ($($token: tt)*) => {{
371        $crate::object_by_fields_!("lyquor_primitives::serde", $($token)*)
372    }};
373}
374
375#[doc(hidden)]
376#[macro_export]
377macro_rules! encode_by_fields_ {
378    ($serde_crate: tt, $($var:ident: $type:ty = $val:expr),*) => {{
379        $crate::encode_object(&$crate::object_by_fields_!($serde_crate, $($var: $type = $val),*))
380    }};
381    ($serde_crate: tt, $($var:ident: $type:ty),*) => {{
382        $crate::encode_object(&$crate::object_by_fields_!($serde_crate, $($var: $type = $var),*))
383    }};
384}
385
386#[macro_export]
387macro_rules! encode_by_fields {
388    ($($token: tt)*) => {{
389        $crate::encode_by_fields_!("lyquor_primitives::serde", $($token)*)
390    }};
391}
392
393#[macro_export]
394macro_rules! decode_by_fields {
395    ($encoded:expr, $($var:ident: $type:ty),*) => {{
396        #[allow(non_camel_case_types)]
397        #[derive($crate::Deserialize)]
398        #[serde(crate = "lyquor_primitives::serde")]
399        struct parameters { $($var:$type),* }
400        $crate::decode_object::<parameters>($encoded)
401    }};
402}
403
404#[derive(Serialize, Deserialize, Debug)]
405pub struct Range<T> {
406    pub start: Option<T>,
407    pub end: Option<T>,
408}
409
410#[macro_export]
411macro_rules! debug_struct_name {
412    ($t:ty) => {
413        impl std::fmt::Debug for $t {
414            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415                f.write_str(stringify!($t))
416            }
417        }
418    };
419}
420
421/// Signature scheme used when requesting signatures from the host.
422#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
423pub enum Cipher {
424    Ed25519,
425    Secp256k1,
426}