/home/runner/work/creditcoin/creditcoin/sha3pow/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | 1 | use parity_scale_codec::{Decode, Encode};use parity_scale_codec::{Decode, Encode}; |
2 | | use primitives::Difficulty; |
3 | | use rand::{prelude::SmallRng, SeedableRng}; |
4 | | use sc_consensus_pow::{Error, PowAlgorithm}; |
5 | | use sc_keystore::LocalKeystore; |
6 | | use sha3::{Digest, Sha3_256}; |
7 | | use sp_api::{BlockId, BlockT, ProvideRuntimeApi}; |
8 | | use sp_consensus_pow::{DifficultyApi, Seal as RawSeal}; |
9 | | use sp_core::{H256, U256}; |
10 | | use std::sync::Arc; |
11 | | |
12 | | /// Determine whether the given hash satisfies the given difficulty. |
13 | | /// The test is done by multiplying the two together. If the product |
14 | | /// overflows the bounds of U256, then the product (and thus the hash) |
15 | | /// was too high. |
16 | 8 | fn hash_meets_difficulty(hash: &H256, difficulty: Difficulty) -> bool { |
17 | 8 | let num_hash = U256::from(&hash[..]); |
18 | 8 | let (_, overflowed) = num_hash.overflowing_mul(difficulty); |
19 | 8 | |
20 | 8 | !overflowed |
21 | 8 | } |
22 | | |
23 | | /// A Seal struct that will be encoded to a Vec<u8> as used as the |
24 | | /// `RawSeal` type. |
25 | 5 | #[derive(Clone0 , P3 artialEq3 , Eq, Encode, Decode, Debug0 )] |
26 | | pub struct Seal { |
27 | | pub difficulty: Difficulty, |
28 | | pub work: H256, |
29 | | pub nonce: H256, |
30 | | } |
31 | | |
32 | | /// A not-yet-computed attempt to solve the proof of work. Calling the |
33 | | /// compute method will compute the hash and return the seal. |
34 | 8 | #[derive(Clone0 , P0 artialEq0 , Eq, Encode, D0 ecode0 , Debug0 )] |
35 | | pub struct Compute { |
36 | | pub difficulty: Difficulty, |
37 | | pub pre_hash: H256, |
38 | | pub nonce: H256, |
39 | | } |
40 | | |
41 | | impl Compute { |
42 | 8 | pub fn compute(self) -> Seal { |
43 | 8 | let work = H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice()); |
44 | 8 | |
45 | 8 | Seal { nonce: self.nonce, difficulty: self.difficulty, work } |
46 | 8 | } |
47 | | } |
48 | | |
49 | | /// A complete PoW Algorithm that uses Sha3 hashing. |
50 | | /// Needs a reference to the client so it can grab the difficulty from the runtime. |
51 | | pub struct Sha3Algorithm<C> { |
52 | | client: Arc<C>, |
53 | | } |
54 | | |
55 | | impl<C> Sha3Algorithm<C> { |
56 | 4 | pub fn new(client: Arc<C>) -> Self { |
57 | 4 | Self { client } |
58 | 4 | } |
59 | | } |
60 | | |
61 | | // Manually implement clone. Deriving doesn't work because |
62 | | // it'll derive impl<C: Clone> Clone for Sha3Algorithm<C>. But C in practice isn't Clone. |
63 | | impl<C> Clone for Sha3Algorithm<C> { |
64 | 1 | fn clone(&self) -> Self { |
65 | 1 | Self::new(self.client.clone()) |
66 | 1 | } |
67 | | } |
68 | | |
69 | | pub trait GetDifficulty<B> |
70 | | where |
71 | | B: BlockT<Hash = H256>, |
72 | | { |
73 | | fn difficulty(&self, parent: B::Hash) -> Result<Difficulty, Error<B>>; |
74 | | } |
75 | | |
76 | | impl<B, C> GetDifficulty<B> for C |
77 | | where |
78 | | B: BlockT<Hash = H256>, |
79 | | C: ProvideRuntimeApi<B>, |
80 | | C::Api: DifficultyApi<B, Difficulty>, |
81 | | { |
82 | 0 | fn difficulty(&self, parent: <B as BlockT>::Hash) -> Result<Difficulty, Error<B>> { |
83 | 0 | let parent_id = BlockId::<B>::hash(parent); |
84 | 0 | self.runtime_api().difficulty(&parent_id).map_err(|err| { |
85 | 0 | sc_consensus_pow::Error::Environment(format!( |
86 | 0 | "Fetching difficulty from runtime failed: {:?}", |
87 | 0 | err |
88 | 0 | )) |
89 | 0 | }) |
90 | 0 | } |
91 | | } |
92 | | |
93 | 5 | fn verify<B: BlockT<Hash = H256>>( |
94 | 5 | _parent: &BlockId<B>, |
95 | 5 | pre_hash: &H256, |
96 | 5 | _pre_digest: Option<&[u8]>, |
97 | 5 | seal: &RawSeal, |
98 | 5 | difficulty: Difficulty, |
99 | 5 | ) -> Result<bool, Error<B>> { |
100 | | // Try to construct a seal object by decoding the raw seal given |
101 | 5 | let seal4 = match Seal::decode(&mut &seal[..]) { |
102 | 4 | Ok(seal) => seal, |
103 | 1 | Err(_) => return Ok(false), |
104 | | }; |
105 | | |
106 | | // See whether the hash meets the difficulty requirement. If not, fail fast. |
107 | 4 | if !hash_meets_difficulty(&seal.work, difficulty) { |
108 | 1 | return Ok(false); |
109 | 3 | } |
110 | 3 | |
111 | 3 | // Make sure the provided work actually comes from the correct pre_hash |
112 | 3 | let compute = Compute { difficulty, pre_hash: *pre_hash, nonce: seal.nonce }; |
113 | 3 | |
114 | 3 | if compute.compute() != seal { |
115 | 1 | return Ok(false); |
116 | 2 | } |
117 | 2 | |
118 | 2 | Ok(true) |
119 | 5 | } |
120 | | // Here we implement the general PowAlgorithm trait for our concrete Sha3Algorithm |
121 | | impl<B: BlockT<Hash = H256>, C> PowAlgorithm<B> for Sha3Algorithm<C> |
122 | | where |
123 | | C: GetDifficulty<B>, |
124 | | { |
125 | | type Difficulty = Difficulty; |
126 | | |
127 | 1 | fn difficulty(&self, parent: B::Hash) -> Result<Self::Difficulty, Error<B>> { |
128 | 1 | self.client.difficulty(parent) |
129 | 1 | } |
130 | | |
131 | 1 | fn verify( |
132 | 1 | &self, |
133 | 1 | _parent: &BlockId<B>, |
134 | 1 | pre_hash: &H256, |
135 | 1 | _pre_digest: Option<&[u8]>, |
136 | 1 | seal: &RawSeal, |
137 | 1 | difficulty: Self::Difficulty, |
138 | 1 | ) -> Result<bool, Error<B>> { |
139 | 1 | verify(_parent, pre_hash, _pre_digest, seal, difficulty) |
140 | 1 | } |
141 | | } |
142 | | |
143 | 2 | pub fn mine<B, C>( |
144 | 2 | _client: &C, |
145 | 2 | _keystore: &LocalKeystore, |
146 | 2 | pre_hash: &H256, |
147 | 2 | _pre_digest: Option<&[u8]>, |
148 | 2 | difficulty: Difficulty, |
149 | 2 | ) -> Result<Option<RawSeal>, Error<B>> |
150 | 2 | where |
151 | 2 | B: BlockT<Hash = H256>, |
152 | 2 | C: GetDifficulty<B>, |
153 | 2 | { |
154 | 2 | let mut rng = SmallRng::from_rng(&mut rand::thread_rng()).map_err(|e| { |
155 | 0 | sc_consensus_pow::Error::Environment(format!("Initialize RNG failed for mining: {:?}", e)) |
156 | 2 | })?0 ; |
157 | | |
158 | 2 | let nonce = H256::random_using(&mut rng); |
159 | 2 | let compute = Compute { difficulty, pre_hash: *pre_hash, nonce }; |
160 | 2 | |
161 | 2 | let seal = compute.compute(); |
162 | 2 | |
163 | 2 | if hash_meets_difficulty(&seal.work, difficulty) { |
164 | 1 | Ok(Some(seal.encode())) |
165 | | } else { |
166 | 1 | Ok(None) |
167 | | } |
168 | 2 | } |
169 | | |
170 | | #[cfg(test)] |
171 | | mod test { |
172 | | use primitives::Difficulty; |
173 | | use sc_keystore::LocalKeystore; |
174 | | use sp_core::H256; |
175 | | use sp_runtime::{testing::Block, OpaqueExtrinsic}; |
176 | | use std::sync::Arc; |
177 | | |
178 | | use crate::*; |
179 | | use assert_matches::assert_matches; |
180 | | |
181 | | type TestBlock = Block<OpaqueExtrinsic>; |
182 | | |
183 | 1 | #[derive(PartialEq, Debug0 , Clone)] |
184 | | struct MockDifficulty { |
185 | | value: Difficulty, |
186 | | } |
187 | | |
188 | | impl MockDifficulty { |
189 | 5 | fn new(value: impl Into<Difficulty>) -> Self { |
190 | 5 | Self { value: value.into() } |
191 | 5 | } |
192 | | } |
193 | | |
194 | | impl GetDifficulty<TestBlock> for MockDifficulty { |
195 | 1 | fn difficulty( |
196 | 1 | &self, |
197 | 1 | _parent: <TestBlock as sp_api::BlockT>::Hash, |
198 | 1 | ) -> Result<Difficulty, sc_consensus_pow::Error<TestBlock>> { |
199 | 1 | Ok(self.value) |
200 | 1 | } |
201 | | } |
202 | | |
203 | 1 | #[test] |
204 | 1 | fn mine_works() { |
205 | 1 | let mock = MockDifficulty::new(1); |
206 | 1 | let keystore = LocalKeystore::in_memory(); |
207 | 1 | let pre_hash = H256::default(); |
208 | 1 | |
209 | 1 | let pre_digest = None; |
210 | 1 | let difficulty = 1.into(); |
211 | 1 | let result = super::mine::<TestBlock, MockDifficulty>( |
212 | 1 | &Arc::new(mock), |
213 | 1 | &keystore, |
214 | 1 | &pre_hash, |
215 | 1 | pre_digest, |
216 | 1 | difficulty, |
217 | 1 | ) |
218 | 1 | .unwrap(); |
219 | 1 | |
220 | 1 | assert!(result.is_some()); |
221 | 1 | } |
222 | | |
223 | 1 | #[test] |
224 | 1 | fn mine_should_return_none_when_hash_doesnt_meet_difficulty() { |
225 | 1 | let mock = MockDifficulty::new(1); |
226 | 1 | let keystore = LocalKeystore::in_memory(); |
227 | 1 | let pre_hash = H256::default(); |
228 | 1 | |
229 | 1 | let pre_digest = None; |
230 | 1 | let difficulty = Difficulty::MAX; |
231 | 1 | let result = super::mine::<TestBlock, MockDifficulty>( |
232 | 1 | &Arc::new(mock), |
233 | 1 | &keystore, |
234 | 1 | &pre_hash, |
235 | 1 | pre_digest, |
236 | 1 | difficulty, |
237 | 1 | ) |
238 | 1 | .unwrap(); |
239 | 1 | |
240 | 1 | assert!(result.is_none()); |
241 | 1 | } |
242 | | |
243 | 1 | #[test] |
244 | 1 | fn hash_meets_difficulty_should_return_false_when_product_overflows() { |
245 | 1 | let hash = H256::repeat_byte(u8::MAX); |
246 | 1 | let difficulty = Difficulty::MAX; |
247 | 1 | |
248 | 1 | let result = hash_meets_difficulty(&hash, difficulty); |
249 | 1 | assert!(!result); |
250 | 1 | } |
251 | | |
252 | 1 | #[test] |
253 | 1 | fn hash_meets_difficulty_should_return_true_when_product_doesnt_overflow() { |
254 | 1 | let hash = H256::repeat_byte(1u8); |
255 | 1 | let difficulty = Difficulty::zero(); |
256 | 1 | |
257 | 1 | let result = hash_meets_difficulty(&hash, difficulty); |
258 | 1 | assert!(result); |
259 | 1 | } |
260 | | |
261 | 1 | #[test] |
262 | 1 | fn sha3algorithm_can_clone() { |
263 | 1 | let mock = MockDifficulty::new(1); |
264 | 1 | let algorithm = Sha3Algorithm::new(Arc::new(mock)); |
265 | 1 | |
266 | 1 | let cloning = algorithm.clone(); |
267 | 1 | assert_eq!(cloning.client, algorithm.client); |
268 | 1 | } |
269 | | |
270 | 1 | #[test] |
271 | 1 | fn sha3algorithm_can_return_difficulty() { |
272 | 1 | let mock = MockDifficulty::new(2); |
273 | 1 | let algorithm = Sha3Algorithm::new(Arc::new(mock)); |
274 | 1 | |
275 | 1 | let difficulty = algorithm.difficulty(H256::default()).unwrap(); |
276 | 1 | assert_eq!(difficulty, U256::from(2)); |
277 | 1 | } |
278 | | |
279 | 1 | #[test] |
280 | 1 | fn sha3algorithm_verify_works() { |
281 | 1 | let mock = MockDifficulty::new(1); |
282 | 1 | let algorithm = Sha3Algorithm::new(Arc::new(mock.clone())); |
283 | 1 | |
284 | 1 | let pre_hash = H256::default(); |
285 | 1 | let nonce = H256::default(); |
286 | 1 | |
287 | 1 | let compute = Compute { difficulty: mock.value, pre_hash, nonce }; |
288 | 1 | let seal = compute.compute(); |
289 | 1 | let raw_seal = Seal::encode(&seal); |
290 | 1 | |
291 | 1 | let result = |
292 | 1 | algorithm.verify(&BlockId::Number(1), &pre_hash, Some(&[]), &raw_seal, mock.value); |
293 | 1 | assert_matches!(result, Ok(true)); |
294 | 1 | } |
295 | | |
296 | 1 | #[test] |
297 | 1 | fn compute_should_return_a_seal() { |
298 | 1 | let compute = |
299 | 1 | Compute { difficulty: 1.into(), pre_hash: H256::default(), nonce: H256::default() }; |
300 | 1 | |
301 | 1 | let result = compute.compute(); |
302 | 1 | assert_matches!(result, Seal{ difficulty, work: _, nonce} => { |
303 | 1 | assert_eq!(difficulty, 1.into()); |
304 | 1 | assert_eq!(nonce, H256::default()); |
305 | | }); |
306 | 1 | } |
307 | | |
308 | 1 | #[test] |
309 | 1 | fn verify_should_return_false_when_rawseal_cant_be_decoded() { |
310 | 1 | let result = verify::<TestBlock>( |
311 | 1 | &BlockId::Number(1), |
312 | 1 | &H256::default(), |
313 | 1 | Some(&[]), |
314 | 1 | &vec![], // empty vector should not decode |
315 | 1 | Difficulty::zero(), |
316 | 1 | ); |
317 | | |
318 | 1 | assert_matches!0 (result, Ok(false)); |
319 | 1 | } |
320 | | |
321 | 1 | #[test] |
322 | 1 | fn verify_should_return_false_when_work_doesnt_meet_difficulty() { |
323 | 1 | let seal = Seal { |
324 | 1 | difficulty: 0.into(), |
325 | 1 | work: H256::repeat_byte(u8::MAX), // compared to difficulty |
326 | 1 | nonce: H256::zero(), |
327 | 1 | }; |
328 | 1 | let raw_seal = Seal::encode(&seal); |
329 | 1 | |
330 | 1 | let result = verify::<TestBlock>( |
331 | 1 | &BlockId::Number(1), |
332 | 1 | &H256::default(), |
333 | 1 | Some(&[]), |
334 | 1 | &raw_seal, |
335 | 1 | Difficulty::MAX, // compared to seal.work |
336 | 1 | ); |
337 | | |
338 | 1 | assert_matches!0 (result, Ok(false)); |
339 | 1 | } |
340 | | |
341 | 1 | #[test] |
342 | 1 | fn verify_should_return_false_when_computed_seal_doesnt_match_arguments() { |
343 | 1 | let seal = Seal { |
344 | 1 | difficulty: 0.into(), |
345 | 1 | work: H256::repeat_byte(1u8), // will not overflow * difficulty |
346 | 1 | nonce: H256::zero(), |
347 | 1 | }; |
348 | 1 | let raw_seal = Seal::encode(&seal); |
349 | 1 | |
350 | 1 | let result = verify::<TestBlock>( |
351 | 1 | &BlockId::Number(1), |
352 | 1 | &H256::default(), // will cause different computed seal |
353 | 1 | Some(&[]), |
354 | 1 | &raw_seal, |
355 | 1 | Difficulty::zero(), // will not overflow * seal.work |
356 | 1 | ); |
357 | | |
358 | 1 | assert_matches!0 (result, Ok(false)); |
359 | 1 | } |
360 | | |
361 | 1 | #[test] |
362 | 1 | fn verify_should_return_true_when_computed_seal_matches_arguments() { |
363 | 1 | let difficulty: Difficulty = 1.into(); |
364 | 1 | let pre_hash = H256::default(); |
365 | 1 | let nonce = H256::default(); |
366 | 1 | |
367 | 1 | let compute = Compute { difficulty, pre_hash, nonce }; |
368 | 1 | let seal = compute.compute(); |
369 | 1 | let raw_seal = Seal::encode(&seal); |
370 | 1 | |
371 | 1 | let result = |
372 | 1 | verify::<TestBlock>(&BlockId::Number(1), &pre_hash, Some(&[]), &raw_seal, difficulty); |
373 | | |
374 | 1 | assert_matches!(result, Ok(true)); |
375 | 1 | } |
376 | | } |