Skip to main content

jam_primitives/
types.rs

1//! # Core Types for JAM Protocol
2//!
3//! This module defines the fundamental types used throughout the JAM protocol.
4
5use crate::utils::codec::{Decode, Encode};
6use serde::{Deserialize, Serialize};
7
8/// Represents public key bytes as unique account identifier
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Decode, Encode)]
10pub struct AccountId(pub [u8; 32]);
11
12impl From<[u8; 32]> for AccountId {
13    fn from(bytes: [u8; 32]) -> Self {
14        Self(bytes)
15    }
16}
17
18impl AsRef<[u8]> for AccountId {
19    fn as_ref(&self) -> &[u8] {
20        &self.0
21    }
22}
23
24impl AccountId {
25    /// Create a new AccountId from bytes
26    pub fn new(bytes: [u8; 32]) -> Self {
27        Self(bytes)
28    }
29
30    /// Get account ID as bytes slice
31    pub fn as_bytes(&self) -> &[u8] {
32        &self.0
33    }
34}
35
36/// Blake 3 hash as bytes
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Decode, Encode)]
38pub struct Hash(pub [u8; 32]);
39
40impl From<[u8; 32]> for Hash {
41    fn from(bytes: [u8; 32]) -> Self {
42        Self(bytes)
43    }
44}
45
46impl AsRef<[u8]> for Hash {
47    fn as_ref(&self) -> &[u8] {
48        &self.0
49    }
50}
51
52impl Hash {
53    /// Create a zero hash (all bytes are zero)
54    pub fn zero() -> Self {
55        Self([0u8; 32])
56    }
57
58    /// Check if hash is all zeros
59    pub fn is_zero(&self) -> bool {
60        self.0.iter().all(|&b| b == 0)
61    }
62}
63
64/// Block number in the chain
65#[derive(
66    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Decode, Encode,
67)]
68pub struct BlockNumber(pub u32);
69
70impl From<u32> for BlockNumber {
71    fn from(number: u32) -> Self {
72        Self(number)
73    }
74}
75
76impl From<BlockNumber> for u32 {
77    fn from(block_number: BlockNumber) -> Self {
78        block_number.0
79    }
80}
81
82impl std::ops::Add<u32> for BlockNumber {
83    type Output = Self;
84
85    fn add(self, other: u32) -> Self {
86        Self(self.0 + other)
87    }
88}
89
90/// Unix timestamp in milliseconds
91#[derive(
92    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Decode, Encode,
93)]
94pub struct Timestamp(pub u64);
95
96impl From<u64> for Timestamp {
97    fn from(timestamp: u64) -> Self {
98        Self(timestamp)
99    }
100}
101
102impl From<Timestamp> for u64 {
103    fn from(timestamp: Timestamp) -> Self {
104        timestamp.0
105    }
106}
107
108impl Timestamp {
109    /// Saturating subtraction
110    pub fn saturating_sub(self, other: Self) -> u64 {
111        self.0.saturating_sub(other.0)
112    }
113}
114
115/// Weight represents computational cost
116#[derive(
117    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Decode, Encode,
118)]
119pub struct Weight(pub u64);
120
121impl From<u64> for Weight {
122    fn from(weight: u64) -> Self {
123        Self(weight)
124    }
125}
126
127impl From<Weight> for u64 {
128    fn from(weight: Weight) -> Self {
129        weight.0
130    }
131}
132
133impl std::ops::Add for Weight {
134    type Output = Self;
135
136    fn add(self, other: Self) -> Self {
137        Self(self.0 + other.0)
138    }
139}
140
141impl std::ops::Mul<u64> for Weight {
142    type Output = Self;
143
144    fn mul(self, other: u64) -> Self {
145        Self(self.0 * other)
146    }
147}
148
149impl std::iter::Sum for Weight {
150    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
151        iter.fold(Self(0), |acc, x| acc + x)
152    }
153}
154
155impl std::iter::Sum<u64> for Weight {
156    fn sum<I: Iterator<Item = u64>>(iter: I) -> Self {
157        Self(iter.sum())
158    }
159}
160
161/// Gas amount for computation
162#[derive(
163    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Decode, Encode,
164)]
165pub struct Gas(pub u64);
166
167impl From<u64> for Gas {
168    fn from(gas: u64) -> Self {
169        Self(gas)
170    }
171}
172
173impl From<Gas> for u64 {
174    fn from(gas: Gas) -> Self {
175        gas.0
176    }
177}
178
179impl Gas {
180    /// Saturating multiplication with u64
181    pub fn saturating_mul(self, other: u64) -> Balance {
182        Balance(self.0 as u128 * other as u128)
183    }
184
185    /// Convert to little endian bytes
186    pub fn to_le_bytes(self) -> [u8; 8] {
187        self.0.to_le_bytes()
188    }
189}
190
191/// Balance type for token amounts
192#[derive(
193    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Decode, Encode,
194)]
195pub struct Balance(pub u128);
196
197impl From<u128> for Balance {
198    fn from(balance: u128) -> Self {
199        Self(balance)
200    }
201}
202
203impl From<Balance> for u128 {
204    fn from(balance: Balance) -> Self {
205        balance.0
206    }
207}
208
209impl Balance {
210    /// Zero balance
211    pub const ZERO: Self = Self(0);
212
213    /// Maximum balance
214    pub const MAX: Self = Self(u128::MAX);
215
216    /// Saturating multiplication
217    pub fn saturating_mul(self, other: u64) -> Self {
218        Self(self.0.saturating_mul(other as u128))
219    }
220
221    /// Saturating addition
222    pub fn saturating_add(self, other: Self) -> Self {
223        Self(self.0.saturating_add(other.0))
224    }
225
226    /// Saturating subtraction
227    pub fn saturating_sub(self, other: Self) -> Self {
228        Self(self.0.saturating_sub(other.0))
229    }
230}
231
232/// Service identifier
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Decode, Encode)]
234pub struct ServiceId(pub u32);
235
236impl ServiceId {
237    /// Create a new ServiceId
238    pub fn new(id: u32) -> Self {
239        Self(id)
240    }
241}
242
243impl From<u32> for ServiceId {
244    fn from(id: u32) -> Self {
245        Self(id)
246    }
247}
248
249impl From<ServiceId> for u32 {
250    fn from(service_id: ServiceId) -> Self {
251        service_id.0
252    }
253}
254
255/// Time slot in the JAM protocol
256#[derive(
257    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Decode, Encode,
258)]
259pub struct TimeSlot(pub u32);
260
261impl From<u32> for TimeSlot {
262    fn from(slot: u32) -> Self {
263        Self(slot)
264    }
265}
266
267impl From<TimeSlot> for u32 {
268    fn from(time_slot: TimeSlot) -> Self {
269        time_slot.0
270    }
271}
272
273/// All supported signatures types generic enum
274#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
275pub enum Signature {
276    /// Ed25519 signature (64 bytes)
277    Ed25519([u8; 64]),
278    /// ECDSA signature (65 bytes with recovery)
279    Ecdsa([u8; 65]),
280    /// Bandersnatch signature (for VRF operations)
281    Bandersnatch([u8; 64]),
282}
283
284impl serde::Serialize for Signature {
285    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
286    where
287        S: serde::Serializer,
288    {
289        match self {
290            Signature::Ed25519(bytes) => {
291                serializer.serialize_newtype_variant("Signature", 0, "Ed25519", &bytes.as_slice())
292            }
293            Signature::Ecdsa(bytes) => {
294                serializer.serialize_newtype_variant("Signature", 1, "Ecdsa", &bytes.as_slice())
295            }
296            Signature::Bandersnatch(bytes) => serializer.serialize_newtype_variant(
297                "Signature",
298                2,
299                "Bandersnatch",
300                &bytes.as_slice(),
301            ),
302        }
303    }
304}
305
306impl<'de> serde::Deserialize<'de> for Signature {
307    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308    where
309        D: serde::Deserializer<'de>,
310    {
311        use serde::de::{self, Error, Visitor};
312
313        struct SignatureVisitor;
314
315        impl<'de> Visitor<'de> for SignatureVisitor {
316            type Value = Signature;
317
318            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
319                formatter.write_str("a signature variant")
320            }
321
322            fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
323            where
324                A: de::EnumAccess<'de>,
325            {
326                use serde::de::VariantAccess;
327
328                let (variant, variant_access): (String, _) = data.variant()?;
329                match variant.as_str() {
330                    "Ed25519" => {
331                        let bytes: Vec<u8> = variant_access.newtype_variant()?;
332                        if bytes.len() != 64 {
333                            return Err(Error::custom("Invalid Ed25519 signature length"));
334                        }
335                        let mut array = [0u8; 64];
336                        array.copy_from_slice(&bytes);
337                        Ok(Signature::Ed25519(array))
338                    }
339                    "Ecdsa" => {
340                        let bytes: Vec<u8> = variant_access.newtype_variant()?;
341                        if bytes.len() != 65 {
342                            return Err(Error::custom("Invalid ECDSA signature length"));
343                        }
344                        let mut array = [0u8; 65];
345                        array.copy_from_slice(&bytes);
346                        Ok(Signature::Ecdsa(array))
347                    }
348                    "Bandersnatch" => {
349                        let bytes: Vec<u8> = variant_access.newtype_variant()?;
350                        if bytes.len() != 64 {
351                            return Err(Error::custom("Invalid Bandersnatch signature length"));
352                        }
353                        let mut array = [0u8; 64];
354                        array.copy_from_slice(&bytes);
355                        Ok(Signature::Bandersnatch(array))
356                    }
357                    _ => Err(Error::unknown_variant(
358                        &variant,
359                        &["Ed25519", "Ecdsa", "Bandersnatch"],
360                    )),
361                }
362            }
363        }
364
365        deserializer.deserialize_enum(
366            "Signature",
367            &["Ed25519", "Ecdsa", "Bandersnatch"],
368            SignatureVisitor,
369        )
370    }
371}
372
373impl Signature {
374    /// Create an Ed25519 signature
375    pub fn ed25519(bytes: [u8; 64]) -> Self {
376        Self::Ed25519(bytes)
377    }
378
379    /// Create an ECDSA signature
380    pub fn ecdsa(bytes: [u8; 65]) -> Self {
381        Self::Ecdsa(bytes)
382    }
383
384    /// Create a Bandersnatch signature
385    pub fn bandersnatch(bytes: [u8; 64]) -> Self {
386        Self::Bandersnatch(bytes)
387    }
388
389    /// Get the signature as bytes
390    pub fn as_bytes(&self) -> &[u8] {
391        match self {
392            Self::Ed25519(bytes) => bytes,
393            Self::Ecdsa(bytes) => bytes,
394            Self::Bandersnatch(bytes) => bytes,
395        }
396    }
397}
398
399#[cfg(test)]
400mod tests {
401    use super::*;
402
403    #[test]
404    fn test_account_id() {
405        let bytes = [1u8; 32];
406        let account_id = AccountId::from(bytes);
407        assert_eq!(account_id.as_ref(), &bytes);
408    }
409
410    #[test]
411    fn test_hash() {
412        let bytes = [2u8; 32];
413        let hash = Hash::from(bytes);
414        assert_eq!(hash.as_ref(), &bytes);
415    }
416
417    #[test]
418    fn test_block_number() {
419        let number = 42u32;
420        let block_number = BlockNumber::from(number);
421        assert_eq!(u32::from(block_number), number);
422    }
423
424    #[test]
425    fn test_timestamp() {
426        let time = 1234567890u64;
427        let timestamp = Timestamp::from(time);
428        assert_eq!(u64::from(timestamp), time);
429    }
430
431    #[test]
432    fn test_signature_types() {
433        let ed25519_sig = Signature::ed25519([1u8; 64]);
434        let ecdsa_sig = Signature::ecdsa([2u8; 65]);
435        let bandersnatch_sig = Signature::bandersnatch([3u8; 64]);
436
437        assert_eq!(ed25519_sig.as_bytes(), &[1u8; 64]);
438        assert_eq!(ecdsa_sig.as_bytes(), &[2u8; 65]);
439        assert_eq!(bandersnatch_sig.as_bytes(), &[3u8; 64]);
440    }
441}