1use serde::{Deserialize, Serialize};
2use sha2::{digest::FixedOutput, Digest, Sha256};
3use std::marker::PhantomData;
4
5const SALT: &str = "35af8f4890981391c191e6df45b5f780812ddf0213f29299576ac1c98e18173e";
6
7#[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)]
9pub struct Pow<T> {
10 proof: u128,
11 #[serde(skip)]
12 _spook: PhantomData<T>,
13}
14
15impl<T: Serialize> Pow<T> {
20 pub fn prove_work(t: &T, difficulty: u128) -> bincode::Result<Pow<T>> {
27 bincode_cfg()
28 .serialize(t)
29 .map(|v| Self::prove_work_serialized(&v, difficulty))
30 }
31
32 pub fn prove_work_serialized(prefix: &[u8], difficulty: u128) -> Pow<T> {
38 let prefix_sha = Sha256::new().chain(SALT).chain(prefix);
39 let mut n = 0;
40 while score(prefix_sha.clone(), n) < difficulty {
41 n += 1;
42 }
43 Pow {
44 proof: n,
45 _spook: PhantomData,
46 }
47 }
48
49 pub fn score(&self, t: &T) -> bincode::Result<u128> {
51 bincode_cfg()
52 .serialize(t)
53 .map(|v| self.score_serialized(&v))
54 }
55
56 pub fn score_serialized(&self, target: &[u8]) -> u128 {
59 score(Sha256::new().chain(SALT).chain(target), self.proof)
60 }
61}
62
63fn score(prefix_sha: Sha256, proof: u128) -> u128 {
64 first_bytes_as_u128(
65 prefix_sha
66 .chain(&proof.to_be_bytes()) .fixed_result()
68 .as_slice(),
69 )
70}
71
72fn first_bytes_as_u128(inp: &[u8]) -> u128 {
76 bincode_cfg().deserialize(&inp).unwrap()
77}
78
79fn bincode_cfg() -> bincode::Config {
80 let mut cfg = bincode::config();
81 cfg.big_endian();
82 cfg
83}
84
85#[cfg(test)]
86mod test {
87 use super::*;
88
89 const DIFFICULTY: u128 = 0xff000000000000000000000000000000;
90
91 #[test]
92 fn base_functionality() {
93 let phrase = b"Corver bandar palladianism retroform.".to_vec();
95 let pw = Pow::prove_work(&phrase, DIFFICULTY).unwrap();
96 assert!(pw.score(&phrase).unwrap() >= DIFFICULTY);
97 }
98
99 #[test]
100 fn double_pow() {
101 let phrase = "Corver bandar palladianism retroform.".to_owned();
102 let pow = Pow::prove_work(&phrase, DIFFICULTY).unwrap();
103 let powpow: Pow<Pow<String>> = Pow::prove_work(&pow, DIFFICULTY).unwrap();
104 assert!(pow.score(&phrase).unwrap() >= DIFFICULTY);
105 assert!(powpow.score(&pow).unwrap() >= DIFFICULTY);
106 }
107
108 #[test]
109 fn ser_de() {
110 let target: u8 = 1;
111 let pw = Pow::prove_work(&target, DIFFICULTY).unwrap();
112 let message: (u8, Pow<u8>) = (target, pw);
113 let message_ser = bincode_cfg().serialize(&message).unwrap();
114 let recieved_message: (u8, Pow<u8>) = bincode_cfg().deserialize(&message_ser).unwrap();
115 assert_eq!(recieved_message, message);
116 assert!(message.1.score(&message.0).unwrap() >= DIFFICULTY);
117 }
118
119 #[test]
120 fn ser_de_no_spook() {
122 let pw: Pow<u8> = Pow {
123 proof: 0,
124 _spook: PhantomData,
125 };
126 let ser = serde_json::to_string(&pw).unwrap();
127 assert_ne!(&ser, "{\"proof\":0,\"_spook\":null}");
128 assert_eq!(&ser, "{\"proof\":0}");
129 }
130}