Coverage Report

Created: 2022-11-10 19:56

/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
artialE
q3
, 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
artialE
q0
, Eq, Encode,
D0
ecod
e0
,
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
}