saito_core/core/consensus/
golden_ticket.rs1use std::convert::TryInto;
2
3use serde::{Deserialize, Serialize};
4
5use crate::core::defs::{SaitoHash, SaitoPublicKey};
6use crate::core::util::crypto::hash;
7
8#[serde_with::serde_as]
9#[derive(Serialize, Deserialize, Debug, Clone)]
10pub struct GoldenTicket {
11 pub target: SaitoHash,
12 pub(crate) random: SaitoHash,
13 #[serde_as(as = "[_; 33]")]
14 pub(crate) public_key: SaitoPublicKey,
15}
16
17impl GoldenTicket {
18 #[allow(clippy::new_without_default)]
19
20 pub fn new(target: SaitoHash, random: SaitoHash, public_key: SaitoPublicKey) -> Self {
21 return Self {
22 target,
23 random,
24 public_key,
25 };
26 }
27
28 pub fn create(
29 previous_block_hash: SaitoHash,
30 random_bytes: SaitoHash,
31 public_key: SaitoPublicKey,
32 ) -> GoldenTicket {
33 GoldenTicket::new(previous_block_hash, random_bytes, public_key)
34 }
35
36 pub fn deserialize_from_net(bytes: &Vec<u8>) -> GoldenTicket {
37 assert_eq!(bytes.len(), 97);
38 let target: SaitoHash = bytes[0..32].try_into().unwrap();
39 let random: SaitoHash = bytes[32..64].try_into().unwrap();
40 let public_key: SaitoPublicKey = bytes[64..97].try_into().unwrap();
41 GoldenTicket::new(target, random, public_key)
42 }
43
44 pub fn serialize_for_net(&self) -> Vec<u8> {
45 let vbytes: Vec<u8> = [
46 self.target.as_slice(),
47 self.random.as_slice(),
48 self.public_key.as_slice(),
49 ]
50 .concat();
51 vbytes
52 }
53
54 pub fn validate(&self, difficulty: u64) -> bool {
55 let solution_hash = hash(&self.serialize_for_net());
56
57 GoldenTicket::validate_hashing_difficulty(&solution_hash, difficulty)
58 }
59
60 pub fn validate_hashing_difficulty(solution_hash: &SaitoHash, difficulty: u64) -> bool {
61 let solution = primitive_types::U256::from_big_endian(solution_hash);
62
63 solution.leading_zeros() >= difficulty as u32
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use crate::core::consensus::golden_ticket::GoldenTicket;
70 use crate::core::consensus::wallet::Wallet;
71 use crate::core::defs::{PrintForLog, SaitoHash, SaitoPublicKey};
72 use crate::core::util::crypto::{generate_keys, generate_random_bytes, hash};
73 use log::info;
74
75 #[test]
76 fn golden_ticket_validate_hashing_difficulty() {
77 let hash: SaitoHash = [0u8; 32];
78 let mut hash2: SaitoHash = [255u8; 32];
79
80 assert!(GoldenTicket::validate_hashing_difficulty(&hash, 0));
81 assert!(GoldenTicket::validate_hashing_difficulty(&hash, 10));
82 assert!(GoldenTicket::validate_hashing_difficulty(&hash, 256));
83 assert_eq!(
84 GoldenTicket::validate_hashing_difficulty(&hash, 1000000),
85 false
86 );
87
88 assert!(GoldenTicket::validate_hashing_difficulty(&hash2, 0));
89 assert_eq!(GoldenTicket::validate_hashing_difficulty(&hash2, 10), false);
90 assert_eq!(
91 GoldenTicket::validate_hashing_difficulty(&hash2, 256),
92 false
93 );
94
95 hash2[0] = 15u8;
96
97 assert!(GoldenTicket::validate_hashing_difficulty(&hash2, 3));
98 assert!(GoldenTicket::validate_hashing_difficulty(&hash2, 4));
99 assert_eq!(GoldenTicket::validate_hashing_difficulty(&hash2, 5), false);
100 }
101
102 #[tokio::test]
103 async fn golden_ticket_extremes_test() {
104 let keys = generate_keys();
105 let wallet = Wallet::new(keys.1, keys.0);
106
107 let random = hash(&generate_random_bytes(32).await);
108 let target = hash(random.as_ref());
109 let public_key = wallet.public_key;
110
111 let gt = GoldenTicket::create(target, random, public_key);
112
113 assert!(gt.validate(0));
114 assert!(!gt.validate(256));
115 }
116 #[test]
117 fn gt_against_slr() {
118 let buffer = hex::decode("844702489d49c7fb2334005b903580c7a48fe81121ff16ee6d1a528ad32f235e03bf1a4714cfc7ae33d3f6e860c23191ddea07bcb1bfa6c85bc124151ad8d4ce03cb14a56ddc769932baba62c22773aaf6d26d799b548c8b8f654fb92d25ce7610").unwrap();
119 assert_eq!(buffer.len(), 97);
120
121 let result = GoldenTicket::deserialize_from_net(&buffer);
122 assert_eq!(
123 result.target.to_hex(),
124 "844702489d49c7fb2334005b903580c7a48fe81121ff16ee6d1a528ad32f235e"
125 );
126 assert_eq!(
127 result.random.to_hex(),
128 "03bf1a4714cfc7ae33d3f6e860c23191ddea07bcb1bfa6c85bc124151ad8d4ce"
129 );
130 assert_eq!(
131 result.public_key.to_hex(),
132 "03cb14a56ddc769932baba62c22773aaf6d26d799b548c8b8f654fb92d25ce7610"
133 );
134
135 assert!(result.validate(0));
136 }
137
138 #[test]
139 fn gt_against_slr_2() {
140 assert_eq!(primitive_types::U256::one().leading_zeros(), 255);
143 assert_eq!(primitive_types::U256::zero().leading_zeros(), 256);
144 let sol =
145 SaitoHash::from_hex("4523d0eb05233434b42de74a99049decb6c4347da2e7cde9fb49330e905da1e2")
146 .unwrap();
147 info!("sss = {:?}", sol);
148 assert_eq!(
149 primitive_types::U256::from_big_endian(sol.as_ref()).leading_zeros(),
150 1
151 );
152
153 let gt = GoldenTicket {
154 target: SaitoHash::from_hex(
155 "6bc717fdd325b39383923e21c00aedf04efbc2d8ae6ba092e86b984ba45daf5f",
156 )
157 .unwrap(),
158 random: SaitoHash::from_hex(
159 "e41eed52c0d1b261654bd7bc7c15996276714e79bf837e129b022f9c04a97e49",
160 )
161 .unwrap(),
162 public_key: SaitoPublicKey::from_hex(
163 "02262b7491f6599ed3f4f60315d9345e9ef02767973663b9764b52842306da461c",
164 )
165 .unwrap(),
166 };
167
168 let result = gt.validate(1);
169
170 assert!(result);
171 }
172}