saito_core/core/consensus/
slip.rs

1use std::fmt::{Display, Formatter};
2use std::io::{Error, ErrorKind};
3
4use log::{debug, error, trace};
5use num_derive::{FromPrimitive, ToPrimitive};
6use num_traits::{FromPrimitive, ToPrimitive};
7use serde::{Deserialize, Serialize};
8
9use crate::core::defs::{
10    Currency, PrintForLog, SaitoPublicKey, SaitoUTXOSetKey, UtxoSet, UTXO_KEY_LENGTH,
11};
12
13/// The size of a serialized slip in bytes.
14pub const SLIP_SIZE: usize = 59;
15
16#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, FromPrimitive, ToPrimitive)]
17pub enum SlipType {
18    Normal = 0,
19    ATR = 1,
20    VipInput = 2,
21    VipOutput = 3,
22    MinerInput = 4,
23    MinerOutput = 5,
24    RouterInput = 6,
25    RouterOutput = 7,
26    BlockStake = 8,
27    Bound = 9,
28}
29
30#[serde_with::serde_as]
31#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
32pub struct Slip {
33    #[serde_as(as = "[_; 33]")]
34    pub public_key: SaitoPublicKey,
35    pub amount: Currency,
36    pub slip_index: u8,
37    pub block_id: u64,
38    pub tx_ordinal: u64,
39    pub slip_type: SlipType,
40    #[serde_as(as = "[_; 59]")]
41    pub utxoset_key: SaitoUTXOSetKey,
42    // TODO : Check if this can be removed with Option<>
43    pub is_utxoset_key_set: bool,
44}
45impl Display for Slip {
46    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47        writeln!(
48            f,
49            "Slip {{ key: {}, type: {:?}, amount: {}, location: {}-{}-{}, utxoset_key: {} }}",
50            self.public_key.to_base58(),
51            self.slip_type,
52            self.amount,
53            self.block_id,
54            self.tx_ordinal,
55            self.slip_index,
56            self.utxoset_key.to_hex()
57        )
58    }
59}
60impl Default for Slip {
61    fn default() -> Self {
62        Self {
63            public_key: [0; 33],
64            amount: 0,
65            slip_index: 0,
66            block_id: 0,
67            tx_ordinal: 0,
68            slip_type: SlipType::Normal,
69            // uuid: [0; 32],
70            utxoset_key: [0; UTXO_KEY_LENGTH],
71            is_utxoset_key_set: false,
72        }
73    }
74}
75
76impl Slip {
77    /// runs when block is purged for good or staking slip deleted
78    pub fn delete(&self, utxoset: &mut UtxoSet) -> bool {
79        if self.get_utxoset_key() == [0; UTXO_KEY_LENGTH] {
80            error!("ERROR 572034: asked to remove a slip without its utxoset_key properly set!");
81            return false;
82        }
83        debug!("deleting slip from utxo : {}", self);
84        utxoset.remove_entry(&self.get_utxoset_key());
85        true
86    }
87
88    pub fn deserialize_from_net(bytes: &Vec<u8>) -> Result<Slip, Error> {
89        if bytes.len() != SLIP_SIZE {
90            return Err(Error::from(ErrorKind::InvalidInput));
91        }
92        let public_key: SaitoPublicKey = bytes[..33]
93            .try_into()
94            .or(Err(Error::from(ErrorKind::InvalidData)))?;
95        let amount: Currency = Currency::from_be_bytes(
96            bytes[33..41]
97                .try_into()
98                .or(Err(Error::from(ErrorKind::InvalidData)))?,
99        );
100        let block_id: u64 = u64::from_be_bytes(
101            bytes[41..49]
102                .try_into()
103                .or(Err(Error::from(ErrorKind::InvalidData)))?,
104        );
105        let tx_ordinal: u64 = u64::from_be_bytes(
106            bytes[49..57]
107                .try_into()
108                .or(Err(Error::from(ErrorKind::InvalidData)))?,
109        );
110        let slip_index: u8 = bytes[57];
111        let slip_type: SlipType =
112            SlipType::from_u8(bytes[58]).ok_or(Error::from(ErrorKind::InvalidData))?;
113        let mut slip = Slip::default();
114
115        slip.public_key = public_key;
116        slip.amount = amount;
117        slip.block_id = block_id;
118        slip.tx_ordinal = tx_ordinal;
119        slip.slip_index = slip_index;
120        slip.slip_type = slip_type;
121
122        Ok(slip)
123    }
124
125    pub fn generate_utxoset_key(&mut self) {
126        self.utxoset_key = self.get_utxoset_key();
127        self.is_utxoset_key_set = true;
128    }
129
130    pub fn get_utxoset_key(&self) -> SaitoUTXOSetKey {
131        [
132            self.public_key.as_slice(),                               // length = 33
133            self.block_id.to_be_bytes().as_slice(),                   // length = 8
134            self.tx_ordinal.to_be_bytes().as_slice(),                 // length = 8
135            self.slip_index.to_be_bytes().as_slice(),                 // length = 1
136            self.amount.to_be_bytes().as_slice(),                     // length = 8
137            self.slip_type.to_u8().unwrap().to_be_bytes().as_slice(), // length = 1
138        ]
139        .concat()
140        .try_into()
141        .unwrap()
142    }
143
144    pub fn parse_slip_from_utxokey(key: &SaitoUTXOSetKey) -> Result<Slip, Error> {
145        let mut slip = Slip::default();
146        slip.public_key = key[0..33].to_vec().try_into().unwrap();
147        slip.block_id = u64::from_be_bytes(key[33..41].try_into().unwrap());
148        slip.tx_ordinal = u64::from_be_bytes(key[41..49].try_into().unwrap());
149        slip.slip_index = key[49];
150        slip.amount = u64::from_be_bytes(key[50..58].try_into().unwrap());
151        slip.slip_type = SlipType::from_u8(key[58]).ok_or(Error::from(ErrorKind::InvalidData))?;
152
153        slip.utxoset_key = *key;
154        slip.is_utxoset_key_set = true;
155
156        Ok(slip)
157    }
158
159    pub fn on_chain_reorganization(&self, utxoset: &mut UtxoSet, spendable: bool) {
160        if self.amount > 0 {
161            if spendable {
162                trace!(
163                    "adding slip to utxo : {:?}-{:?}-{:?} with value : {:?} key: {:?}",
164                    self.block_id,
165                    self.tx_ordinal,
166                    self.slip_index,
167                    self.amount,
168                    self.utxoset_key.to_hex()
169                );
170                utxoset.insert(self.utxoset_key, spendable);
171            } else {
172                trace!(
173                    "removing slip from utxo : {:?}-{:?}-{:?} with value : {:?} key: {:?}",
174                    self.block_id,
175                    self.tx_ordinal,
176                    self.slip_index,
177                    self.amount,
178                    self.utxoset_key.to_hex()
179                );
180                utxoset.remove(&self.utxoset_key);
181            }
182        }
183    }
184
185    pub fn serialize_for_net(&self) -> Vec<u8> {
186        let bytes: Vec<u8> = [
187            self.public_key.as_slice(),
188            self.amount.to_be_bytes().as_slice(),
189            self.block_id.to_be_bytes().as_slice(),
190            self.tx_ordinal.to_be_bytes().as_slice(),
191            self.slip_index.to_be_bytes().as_slice(),
192            self.slip_type.to_u8().unwrap().to_be_bytes().as_slice(),
193        ]
194        .concat();
195        assert_eq!(bytes.len(), SLIP_SIZE);
196        bytes
197    }
198
199    pub fn serialize_input_for_signature(&self) -> Vec<u8> {
200        [
201            self.public_key.as_slice(),
202            self.amount.to_be_bytes().as_slice(),
203            // self.block_id.to_be_bytes().as_slice(),
204            // self.tx_ordinal.to_be_bytes().as_slice(),
205            self.slip_index.to_be_bytes().as_slice(),
206            self.slip_type.to_u8().unwrap().to_be_bytes().as_slice(),
207        ]
208        .concat()
209    }
210
211    pub fn serialize_output_for_signature(&self) -> Vec<u8> {
212        [
213            self.public_key.as_slice(),
214            self.amount.to_be_bytes().as_slice(),
215            // self.block_id.to_be_bytes().as_slice(),
216            // self.tx_ordinal.to_be_bytes().as_slice(),
217            self.slip_index.to_be_bytes().as_slice(),
218            self.slip_type.to_u8().unwrap().to_be_bytes().as_slice(),
219        ]
220        .concat()
221    }
222
223    pub fn validate(&self, utxoset: &UtxoSet) -> bool {
224        if self.amount > 0 {
225            match utxoset.get(&self.utxoset_key) {
226                Some(value) => {
227                    if *value {
228                        true
229                    } else {
230                        // debug!() since method is used to check when cleaning up mempool
231                        debug!(
232                            "in utxoset but invalid: value is {} at {:?}, block : {:?} tx : {:?} index : {:?}",
233                            *value,
234                            self.utxoset_key.to_hex(),
235                            self.block_id,
236                            self.tx_ordinal,
237                            self.slip_index
238                        );
239                        false
240                    }
241                }
242                None => {
243                    // debug!() since method is used to check when cleaning up mempool
244                    debug!(
245                        "not in utxoset so invalid. value is returned false: {:?} slip type : {:?} block : {:?} tx : {:?} index : {:?} and amount : {:?}",
246                        self.utxoset_key.to_hex(),
247                        self.slip_type,
248                        self.block_id,
249                        self.tx_ordinal,
250                        self.slip_index,
251                        self.amount
252                    );
253                    false
254                }
255            }
256        } else {
257            true
258        }
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use std::sync::Arc;
265
266    use tokio::sync::RwLock;
267
268    use crate::core::consensus::blockchain::Blockchain;
269    use crate::core::consensus::wallet::Wallet;
270    use crate::core::util::crypto::generate_keys;
271
272    use super::*;
273
274    #[test]
275    fn slip_new_test() {
276        let mut slip = Slip::default();
277        assert_eq!(slip.public_key, [0; 33]);
278        assert_eq!(slip.block_id, 0);
279        assert_eq!(slip.tx_ordinal, 0);
280        assert_eq!(slip.amount, 0);
281        assert_eq!(slip.slip_type, SlipType::Normal);
282        assert_eq!(slip.slip_index, 0);
283
284        slip.public_key = [1; 33];
285        assert_eq!(slip.public_key, [1; 33]);
286
287        slip.amount = 100;
288        assert_eq!(slip.amount, 100);
289
290        slip.slip_index = 1;
291        assert_eq!(slip.slip_index, 1);
292
293        slip.slip_type = SlipType::MinerInput;
294        assert_eq!(slip.slip_type, SlipType::MinerInput);
295    }
296
297    #[test]
298    fn slip_serialize_for_signature_test() {
299        let slip = Slip::default();
300        assert_eq!(
301            slip.serialize_input_for_signature(),
302            vec![0; SLIP_SIZE - 16]
303        );
304        assert_eq!(
305            slip.serialize_output_for_signature(),
306            vec![0; SLIP_SIZE - 16]
307        );
308        assert_eq!(slip.serialize_for_net(), vec![0; SLIP_SIZE]);
309    }
310
311    #[test]
312    fn slip_get_utxoset_key_test() {
313        let mut slip = Slip::default();
314        assert_eq!(slip.get_utxoset_key(), [0; UTXO_KEY_LENGTH]);
315
316        slip.slip_type = SlipType::BlockStake;
317        slip.amount = 123;
318        slip.is_utxoset_key_set = true;
319        slip.block_id = 10;
320        slip.tx_ordinal = 20;
321        slip.slip_index = 30;
322        slip.public_key = [1; 33];
323
324        let utxokey = slip.get_utxoset_key();
325
326        let slip2 = Slip::parse_slip_from_utxokey(&utxokey).unwrap();
327
328        assert_eq!(slip.slip_type, slip2.slip_type);
329        assert_eq!(slip.amount, slip2.amount);
330        assert!(slip2.is_utxoset_key_set);
331        assert_eq!(slip.block_id, slip2.block_id);
332        assert_eq!(slip.tx_ordinal, slip2.tx_ordinal);
333        assert_eq!(slip.slip_index, slip2.slip_index);
334        assert_eq!(slip.public_key, slip2.public_key);
335    }
336
337    #[test]
338    fn slip_serialization_for_net_test() {
339        let slip = Slip::default();
340        let serialized_slip = slip.serialize_for_net();
341        assert_eq!(serialized_slip.len(), SLIP_SIZE);
342        let deserilialized_slip = Slip::deserialize_from_net(&serialized_slip).unwrap();
343        assert_eq!(slip, deserilialized_slip);
344        let result = Slip::deserialize_from_net(&vec![]);
345        assert!(result.is_err());
346    }
347
348    #[tokio::test]
349    #[serial_test::serial]
350    async fn slip_addition_and_removal_from_utxoset() {
351        let keys = generate_keys();
352        let wallet_lock = Arc::new(RwLock::new(Wallet::new(keys.1, keys.0)));
353        let blockchain_lock = Arc::new(RwLock::new(Blockchain::new(
354            wallet_lock.clone(),
355            1000,
356            0,
357            60,
358        )));
359        let mut blockchain = blockchain_lock.write().await;
360
361        let mut slip = Slip::default();
362        slip.amount = 100_000;
363        slip.block_id = 10;
364        slip.tx_ordinal = 20;
365        {
366            let wallet = wallet_lock.read().await;
367            slip.public_key = wallet.public_key;
368        }
369        slip.generate_utxoset_key();
370
371        // add to utxoset
372        slip.on_chain_reorganization(&mut blockchain.utxoset, true);
373        assert!(blockchain.utxoset.contains_key(&slip.get_utxoset_key()));
374
375        // remove from utxoset
376        // TODO: Repair this test
377        // slip.purge(&mut blockchain.utxoset);
378        // assert_eq!(
379        //     blockchain.utxoset.contains_key(&slip.get_utxoset_key()),
380        //     false
381        // );
382    }
383}