saito_core/core/consensus/
slip.rs1use 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
13pub 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 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 utxoset_key: [0; UTXO_KEY_LENGTH],
71 is_utxoset_key_set: false,
72 }
73 }
74}
75
76impl Slip {
77 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(), self.block_id.to_be_bytes().as_slice(), self.tx_ordinal.to_be_bytes().as_slice(), self.slip_index.to_be_bytes().as_slice(), self.amount.to_be_bytes().as_slice(), self.slip_type.to_u8().unwrap().to_be_bytes().as_slice(), ]
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.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.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!(
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!(
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 slip.on_chain_reorganization(&mut blockchain.utxoset, true);
373 assert!(blockchain.utxoset.contains_key(&slip.get_utxoset_key()));
374
375 }
383}