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
| 1 | use oxedyne_fe2o3_core::{ |
| 2 | prelude::*, |
| 3 | }; |
| 4 | use oxedyne_fe2o3_hash::pow::{ |
| 5 | Pristine, |
| 6 | ZeroBits |
| 7 | }; |
| 8 | |
| 9 | use 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)] |
| 22 | pub enum DifficultyProfile { |
| 23 | Linear = 0, |
| 24 | } |
| 25 | |
| 26 | impl 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)] |
| 43 | pub struct DifficultyParams { |
| 44 | pub profile: DifficultyProfile, |
| 45 | pub max: u16, |
| 46 | pub min: u16, |
| 47 | pub rps_max: u64,//u16 |
| 48 | } |
| 49 | |
| 50 | impl 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)] |
| 74 | pub 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 | |
| 86 | impl< |
| 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 | |
| 104 | impl< |
| 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 | |
| 176 | impl< |
| 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 | } |