Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_shield/src/srv/pow.rs

6.5 KiB, 45 runs

created by r1870400018:886, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use oxedyne_fe2o3_core::{
2 prelude::*,
3};
4use oxedyne_fe2o3_hash::pow::{
5 Pristine,
6 ZeroBits
7};
8
9use std::{
10 net::{
11 IpAddr,
12 Ipv4Addr,
13 },
14 time::{
15 Duration,
16 SystemTime,
17 },
18};
19
20#[repr(u8)]
21#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
22pub enum DifficultyProfile {
23 Linear = 0,
24}
25
26impl TryFrom<u8> for DifficultyProfile {
27 type Error = Error<ErrTag>;
28
29 fn try_from(n: u8) -> std::result::Result<Self, Self::Error> {
30 match n {
31 0 => Ok(Self::Linear),
32 _ => Err(err!(
33 "'{}' not recognised as a valid server_rps_zbits_profile configuration value, \
34 use a value in the range 0..0.", n;
35 Invalid, Input)),
36 }
37 }
38}
39
40/// Vary the required zero bits in proof of work hashes as a function of the requests-per-second
41/// using the given profile and min/max limits.
42#[derive(Clone, Debug)]
43pub struct DifficultyParams {
44 pub profile: DifficultyProfile,
45 pub max: u16,
46 pub min: u16,
47 pub rps_max: u64,//u16
48}
49
50impl DifficultyParams {
51 #[inline(always)]
52 pub fn required_global_zbits(&self, rps: u64) -> Outcome<ZeroBits> {
53 if self.max < self.min {
54 return Err(err!(
55 "The maximum proof of work difficulty, {} zero bits, is below the \
56 minimum of {}.", self.max, self.min;
57 Invalid, Configuration));
58 }
59 match self.profile {
60 DifficultyProfile::Linear => {
61 if self.max == 0 {
62 return Ok(0);
63 }
64 let span = (self.max - self.min) as u64;
65 let scaled = span.saturating_mul(rps) / (self.max as u64);
66 let zbits = (self.min as u64).saturating_add(scaled);
67 Ok(zbits.min(self.max as u64) as ZeroBits)
68 },
69 }
70 }
71}
72
73#[derive(Clone, Debug)]
74pub struct PowPristine<
75 const C: usize,
76 const P0: usize,
77 const P1: usize,
78> {
79 pub code: [u8; C],
80 pub src_addr: IpAddr,
81 pub trg_addr: IpAddr,
82 pub timestamp: Duration,
83 pub time_horiz: u64,
84}
85
86impl<
87 const C: usize,
88 const P0: usize,
89 const P1: usize,
90>
91 Default for PowPristine<C, P0, P1>
92{
93 fn default() -> Self {
94 Self {
95 code: [0; C],
96 src_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
97 trg_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
98 timestamp: Duration::ZERO,
99 time_horiz: 600,
100 }
101 }
102}
103
104impl<
105 const C: usize,
106 const P0: usize,
107 const P1: usize,
108>
109 Pristine<P0, P1> for PowPristine<C, P0, P1>
110{
111 fn to_bytes(&self) -> Outcome<[u8; P1]> {
112 let mut byts = [0u8; P1];
113 let mut i = res!(self.prefix(&mut byts));
114 let t = self.timestamp.as_secs().to_be_bytes();
115 for b in t {
116 byts[i] = b;
117 i += 1;
118 }
119 Ok(byts)
120 }
121
122 /// Pad IPv4 addresses out to the length of an IPv6 address by repetition. Append the code.
123 fn prefix(&self, byts: &mut [u8]) -> Outcome<usize> {
124 if byts.len() < Self::PREFIX_BYTE_LEN {
125 return Err(err!(
126 "Cannot fit {} address bytes into given slice of length {}.",
127 Self::PREFIX_BYTE_LEN, byts.len();
128 Bug, Input, TooSmall));
129 }
130 let mut i: usize = 0;
131 for addr in [self.src_addr, self.trg_addr] {
132 match addr {
133 IpAddr::V4(addr) => {
134 for _ in 0..4 { // Padded via 4 x copies.
135 for b in addr.octets() {
136 byts[i] = b;
137 i += 1;
138 }
139 }
140 },
141 IpAddr::V6(addr) => {
142 for b in addr.octets() {
143 byts[i] = b;
144 i += 1;
145 }
146 },
147 }
148 }
149 for b in self.code {
150 byts[i] = b;
151 i += 1;
152 }
153 Ok(Self::PREFIX_BYTE_LEN)
154 }
155
156 /// Check that the timestamp in the artefact is less than the given time horizon in seconds.
157 fn timestamp_valid(&self, artefact: &[u8]) -> Outcome<bool> {
158 if artefact.len() < Self::TIMESTAMP_LEN {
159 return Err(err!(
160 "Artefact slice length {} must be at least a timestamp length, {}.",
161 artefact.len(), Self::TIMESTAMP_LEN;
162 Input, TooSmall));
163 }
164 let t0 = u64::from_be_bytes(
165 res!(<[u8; 8]>::try_from(&artefact[..8]), Decode, Bytes)
166 );
167 let t1 = res!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)).as_secs();
168 if t0 > t1 {
169 Ok(false)
170 } else {
171 Ok((t1 - t0) < self.time_horiz)
172 }
173 }
174}
175
176impl<
177 const C: usize,
178 const P0: usize,
179 const P1: usize,
180>
181 PowPristine<C, P0, P1>
182{
183 pub const TIMESTAMP_LEN: usize = 8;
184 pub const ADDR_LEN: usize = 16;
185
186 /// Create a new `PowPristine` for the purpose of validating an incoming packet.
187 pub fn new_rx(
188 code: [u8; C],
189 src_addr: IpAddr,
190 trg_addr: IpAddr,
191 time_horiz: u64,
192 )
193 -> Outcome<Self>
194 {
195 if P0 >= P1 {
196 return Err(err!(
197 "The pristine prefix length {} must be less than the pristine length, {}.",
198 P0, P1;
199 Input, Mismatch));
200 }
201 // TODO Other checks?
202 Ok(Self {
203 code,
204 src_addr,
205 trg_addr,
206 timestamp: Duration::ZERO,
207 time_horiz,
208 })
209 }
210
211 pub fn trace(&self) -> Outcome<()> {
212 let byts = res!(self.to_bytes());
213
214 // P0 = 2*ADDR_LEN + C
215 // So if we work backwards from P0:
216 // code_start = P0 - C
217 // trg_start = P0 - C - ADDR_LEN
218 // src_start = P0 - C - 2*ADDR_LEN (which is 0)
219
220 let code_start = P0 - C;
221 let trg_start = P0 - C - Self::ADDR_LEN;
222
223 trace!(async_log::stream(), "\nPrefix [{:>4}]: {:02x?}\
224 \n SrcAddr [{:>4}]: {:02x?}\
225 \n TrgAddr [{:>4}]: {:02x?}\
226 \n Code [{:>4}]: {:02x?}\
227 \nArtefact [{:>4}]: {:02x?}\
228 \n Time [{:>4}]: {:02x?}",
229 P0, &byts[..P0],
230 Self::ADDR_LEN, &byts[..Self::ADDR_LEN],
231 Self::ADDR_LEN, &byts[trg_start..code_start],
232 C, &byts[code_start..P0],
233 P1-P0, &byts[P0..],
234 P1-P0, &byts[P0..],
235 );
236 Ok(())
237 }
238}