saito_core/core/consensus/
golden_ticket.rs

1use 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        // pretty_env_logger::init();
141
142        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}