diff --git a/.github/workflows/test_workspace.yml b/.github/workflows/test_workspace.yml index 57a4a28f..c7a2a214 100644 --- a/.github/workflows/test_workspace.yml +++ b/.github/workflows/test_workspace.yml @@ -26,7 +26,8 @@ jobs: with: targets: x86_64-unknown-linux-gnu - name: Cargo Test - run: cargo test + run: > + cargo test -p tig-utils --features web3 - name: Update Commit Status (Success) if: success() uses: myrotvorets/set-commit-status-action@master diff --git a/tig-protocol/src/add_block.rs b/tig-protocol/src/add_block.rs index 8ee618fd..610d085d 100644 --- a/tig-protocol/src/add_block.rs +++ b/tig-protocol/src/add_block.rs @@ -646,7 +646,9 @@ async fn update_frontiers(ctx: &mut T, block: &Block) { let multiplier = (*block_data.num_qualifiers() as f64 / config.qualifiers.total_qualifiers_threshold as f64) .clamp(0.0, config.difficulty_bounds.max_multiplier); - let scaled_frontier = base_frontier.scale(&min_difficulty, &max_difficulty, multiplier); + let scaled_frontier = base_frontier + .scale(&min_difficulty, &max_difficulty, multiplier) + .extend(&min_difficulty, &max_difficulty); block_data.base_frontier = Some(base_frontier); block_data.scaled_frontier = Some(scaled_frontier); @@ -729,7 +731,7 @@ async fn update_influence(ctx: &mut T, block: &Block) { let imbalance = cv_sqr / (num_challenges - one); let imbalance_penalty = - one - PreciseNumber::approx_inverse_exp(imbalance_multiplier * imbalance); + one - PreciseNumber::approx_inv_exp(imbalance_multiplier * imbalance); weights.push(mean * (one - imbalance_penalty)); diff --git a/tig-protocol/src/submit_benchmark.rs b/tig-protocol/src/submit_benchmark.rs index 54f0fed8..8fb26b64 100644 --- a/tig-protocol/src/submit_benchmark.rs +++ b/tig-protocol/src/submit_benchmark.rs @@ -227,12 +227,12 @@ fn verify_benchmark_difficulty(difficulty: &Vec, challenge: &Challenge) -> ) }; match difficulty.within(lower_frontier, upper_frontier) { - PointCompareFrontiers::Above(_) => { + PointCompareFrontiers::Above => { return Err(ProtocolError::DifficultyAboveHardestFrontier { difficulty: difficulty.clone(), }); } - PointCompareFrontiers::Below(_) => { + PointCompareFrontiers::Below => { return Err(ProtocolError::DifficultyBelowEasiestFrontier { difficulty: difficulty.clone(), }); diff --git a/tig-utils/Cargo.toml b/tig-utils/Cargo.toml index 1fa46c83..c26abd83 100644 --- a/tig-utils/Cargo.toml +++ b/tig-utils/Cargo.toml @@ -30,6 +30,9 @@ web-sys = { version = "0.3.68", optional = true, features = [ 'Window', ] } +[dev-dependencies] +tokio = { version = "1.37.0", features = ["full"] } + [lib] crate-type = ["cdylib", "rlib"] diff --git a/tig-utils/src/eth.rs b/tig-utils/src/eth.rs index b835c4e9..507d3520 100644 --- a/tig-utils/src/eth.rs +++ b/tig-utils/src/eth.rs @@ -1,7 +1,7 @@ use crate::number::PreciseNumber; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Transaction { pub sender: String, pub receiver: String, diff --git a/tig-utils/src/frontiers.rs b/tig-utils/src/frontiers.rs index 9215d628..73526bbc 100644 --- a/tig-utils/src/frontiers.rs +++ b/tig-utils/src/frontiers.rs @@ -6,10 +6,10 @@ pub type Point = Vec; pub type Frontier

= HashSet

; #[derive(Debug, Clone, PartialEq)] -pub enum PointCompareFrontiers

{ - Below(P), +pub enum PointCompareFrontiers { + Below, Within, - Above(P), + Above, } #[derive(Debug, Clone, PartialEq)] @@ -28,7 +28,7 @@ pub trait PointOps { &self, lower_frontier: &Frontier, upper_frontier: &Frontier, - ) -> PointCompareFrontiers; + ) -> PointCompareFrontiers; } pub trait FrontierOps { type Point; @@ -85,21 +85,21 @@ impl PointOps for Point { &self, lower_frontier: &Frontier, upper_frontier: &Frontier, - ) -> PointCompareFrontiers { + ) -> PointCompareFrontiers { // Check if the point is not dominated by any point in the lower frontier - if let Some(point) = lower_frontier + if lower_frontier .iter() - .find(|lower_point| self.pareto_compare(lower_point) == ParetoCompare::BDominatesA) + .any(|lower_point| self.pareto_compare(lower_point) == ParetoCompare::BDominatesA) { - return PointCompareFrontiers::Below(point.clone()); + return PointCompareFrontiers::Below; } // Check if the point does not dominate any point in the upper frontier - if let Some(point) = upper_frontier + if upper_frontier .iter() - .find(|upper_point| self.pareto_compare(upper_point) == ParetoCompare::ADominatesB) + .any(|upper_point| self.pareto_compare(upper_point) == ParetoCompare::ADominatesB) { - return PointCompareFrontiers::Above(point.clone()); + return PointCompareFrontiers::Above; } PointCompareFrontiers::Within @@ -155,9 +155,22 @@ impl FrontierOps for Frontier { max_point: &Self::Point, multiplier: f64, ) -> Frontier { - self.iter() + let frontier: Frontier = self + .iter() .map(|point| point.scale(min_point, max_point, multiplier)) - .collect() + .collect(); + if multiplier > 1.0 { + frontier.pareto_frontier() + } else { + frontier + .into_iter() + .map(|d| d.iter().map(|x| -x).collect()) // mirror the points so easiest difficulties are first + .collect::() + .pareto_frontier() + .iter() + .map(|d| d.iter().map(|x| -x).collect()) + .collect() + } } fn sample(&self, rng: &mut R) -> Self::Point { // FIXME only works for 2 dimensional points diff --git a/tig-utils/src/number.rs b/tig-utils/src/number.rs index 32b3ecd9..0520422b 100644 --- a/tig-utils/src/number.rs +++ b/tig-utils/src/number.rs @@ -43,7 +43,7 @@ impl PreciseNumber { Ok(Self(U256::from_dec_str(value)?)) } - pub fn approx_inverse_exp(x: PreciseNumber) -> PreciseNumber { + pub fn approx_inv_exp(x: PreciseNumber) -> PreciseNumber { // taylor series approximation of e^-x let one = PreciseNumber::from(1); let mut positive_terms = one.clone(); diff --git a/tig-utils/tests/eth.rs b/tig-utils/tests/eth.rs new file mode 100644 index 00000000..94947141 --- /dev/null +++ b/tig-utils/tests/eth.rs @@ -0,0 +1,110 @@ +#[cfg(all(feature = "web3", test))] +mod tests { + #[test] + fn test_recover_address_from_msg_and_sig() { + assert_eq!( + tig_utils::recover_address_from_msg_and_sig( + "test message", + "0x3556abec5b4b955f02a3643a5a3a29672caee32bbae180506452c84ab51c83f608dce8828923eccd1f3b81d8aca540864b917f1221b3a4d7833a4e3ec183369d1c" + ) + .unwrap(), + "0x7e6f4be25823b70c97009670da2eea36ce9096dc" + ); + } + + #[tokio::test] + async fn test_get_gnosis_safe_address() { + assert_eq!( + tig_utils::get_gnosis_safe_address( + "https://mainnet.base.org", + "0x0111ed72b3cd75e786083cf5d5db3a5ef0317891712315547fe49b3a16eebb16" + ) + .await + .unwrap(), + "0x6ab6dffdee6efc0e60b34ef4685b40784c497af8".to_string() + ); + assert_eq!( + tig_utils::get_gnosis_safe_address( + "https://mainnet.base.org", + "0xbe95f21a4172d16cbf243ed6082e6c1cc132ec5ac3011a8b5289283f9059b8ce", + ) + .await + .unwrap(), + "0xb787d8059689402dabc0a05832be526f79aa6b57".to_string() + ); + assert_eq!( + tig_utils::get_gnosis_safe_address( + "https://sepolia.base.org", + "0x90453a4f4ffffedeb70f144ce25d7ca74214f159ce394f0d929fda1d004d8ed0", + ) + .await + .unwrap(), + "0x0112fb82f8041071d7000fb9e4782668e8cbd05f".to_string() + ); + } + + #[tokio::test] + async fn test_get_gnosis_safe_owners() { + assert_eq!( + tig_utils::get_gnosis_safe_owners( + "https://mainnet.base.org", + "0x6ab6dffdee6efc0e60b34ef4685b40784c497af8" + ) + .await + .unwrap(), + vec!["0x7adc19694782c61132bcc38accaf1156d13c80d1".to_string()] + ); + assert_eq!( + tig_utils::get_gnosis_safe_owners( + "https://mainnet.base.org", + "0xb787d8059689402dabc0a05832be526f79aa6b57", + ) + .await + .unwrap(), + vec![ + "0x9e10de6645d81823561aa4c91bef26c00b6c4d81".to_string(), + "0xa327948a93000c6a37cac11b4239d7422a47c882".to_string(), + "0x7a73bec3a56f935687dd30d0ff7c4bc632558c8b".to_string() + ] + ); + assert_eq!( + tig_utils::get_gnosis_safe_owners( + "https://sepolia.base.org", + "0x0112fb82f8041071d7000fb9e4782668e8cbd05f", + ) + .await + .unwrap(), + vec!["0x38d57e70513503c851f0a997fc1c8ab41cd2fca2".to_string()] + ); + } + + #[tokio::test] + async fn test_get_transaction() { + assert_eq!( + tig_utils::get_transaction( + "https://mainnet.base.org", + "0x2c84729e0c24ca982f598215a82888bf8620ebd1e42561b932ddf925b556ba51" + ) + .await + .unwrap(), + tig_utils::Transaction { + sender: "0x097d62f58e2986f2e1d85f259454af15f02f601e".to_string(), + receiver: "0xe37bf84f75a8c3225d80aea2a95a24fd5a736895".to_string(), + amount: tig_utils::PreciseNumber::from_dec_str("362450000000000000").unwrap() + } + ); + assert_eq!( + tig_utils::get_transaction( + "https://sepolia.base.org", + "0x72589afe1f24794328f6ab0b43933ba0d2f8d7321d7bb6febe1513961dd3271e", + ) + .await + .unwrap(), + tig_utils::Transaction { + sender: "0xf0e1c3b2bf8e5ec5420b82d6361d074bc4d8b7f4".to_string(), + receiver: "0xb88a1d716fa26dea1b20b2263c10ca2d7613e22f".to_string(), + amount: tig_utils::PreciseNumber::from_dec_str("499990756092107808").unwrap() + } + ); + } +} diff --git a/tig-utils/tests/frontiers.rs b/tig-utils/tests/frontiers.rs new file mode 100644 index 00000000..ddaa8a38 --- /dev/null +++ b/tig-utils/tests/frontiers.rs @@ -0,0 +1,108 @@ +use tig_utils::{Frontier, FrontierOps, ParetoCompare, PointCompareFrontiers, PointOps}; + +#[test] +fn test_pareto_compare() { + assert_eq!(vec![1, 0].pareto_compare(&vec![1, 0]), ParetoCompare::Equal); + assert_eq!(vec![1, 0].pareto_compare(&vec![0, 1]), ParetoCompare::Equal); + assert_eq!( + vec![1, 1].pareto_compare(&vec![0, 1]), + ParetoCompare::ADominatesB + ); + assert_eq!( + vec![1, 0].pareto_compare(&vec![1, 1]), + ParetoCompare::BDominatesA + ); +} + +#[test] +fn test_pareto_frontier() { + let points: Frontier = vec![ + vec![3, 1], + vec![1, 0], + vec![0, 1], + vec![1, 1], + vec![0, 0], + vec![2, 2], + vec![2, 1], + vec![1, 3], + ] + .into_iter() + .collect(); + assert_eq!( + points.pareto_frontier(), + vec![vec![2, 2], vec![3, 1], vec![1, 3]] + .into_iter() + .collect::() + ); +} + +#[test] +fn test_scale_point() { + // ceil((x - min + 1) * multiplier) + assert_eq!( + vec![3, 1].scale(&vec![0, 0], &vec![10, 10], 1.2), + vec![4, 2] + ); + assert_eq!( + vec![6, 2].scale(&vec![0, 0], &vec![10, 10], 0.7), + vec![4, 2] + ); +} + +#[test] +fn test_scale_frontier() { + let frontier: Frontier = vec![vec![3, 1], vec![2, 2], vec![0, 4]] + .into_iter() + .collect(); + assert_eq!( + frontier.scale(&vec![0, 0], &vec![10, 10], 1.2), + vec![vec![4, 2], vec![3, 3], vec![1, 5]] + .into_iter() + .collect::() + ); + assert_eq!( + frontier.scale(&vec![0, 0], &vec![10, 10], 0.6), + vec![vec![1, 1], vec![0, 2]] + .into_iter() + .collect::() + ); +} + +#[test] +fn test_extend() { + let frontier: Frontier = vec![vec![3, 1], vec![2, 2], vec![0, 4]] + .into_iter() + .collect(); + assert_eq!( + frontier.extend(&vec![0, 0], &vec![10, 10]), + vec![vec![4, 0], vec![3, 1], vec![2, 2], vec![0, 4]] + .into_iter() + .collect::() + ); +} + +#[test] +fn test_within() { + let frontier1: Frontier = vec![vec![3, 1], vec![2, 2], vec![0, 4]] + .into_iter() + .collect(); + let frontier2: Frontier = vec![vec![6, 0], vec![5, 3], vec![0, 7]] + .into_iter() + .collect(); + assert_eq!( + vec![4, 4].within(&frontier1, &frontier2), + PointCompareFrontiers::Within + ); + assert_eq!( + vec![4, 0].within(&frontier1, &frontier2), + PointCompareFrontiers::Within + ); + assert_eq!( + vec![5, 4].within(&frontier1, &frontier2), + PointCompareFrontiers::Above + ); + assert_eq!( + vec![1, 2].within(&frontier1, &frontier2), + PointCompareFrontiers::Below + ); +} diff --git a/tig-utils/tests/number.rs b/tig-utils/tests/number.rs new file mode 100644 index 00000000..878d2e3a --- /dev/null +++ b/tig-utils/tests/number.rs @@ -0,0 +1,49 @@ +use tig_utils::*; + +#[test] +#[should_panic] +fn test_add_overflow() { + let _ = PreciseNumber::from_dec_str( + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + ) + .unwrap() + + PreciseNumber::from(1); +} + +#[test] +#[should_panic] +fn test_sub_underflow() { + let _ = PreciseNumber::from(0) - PreciseNumber::from(1); +} + +#[test] +#[should_panic] +fn test_mul_overflow() { + let _ = PreciseNumber::from_dec_str( + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + ) + .unwrap() + * PreciseNumber::from(2); +} + +#[test] +#[should_panic] +fn test_div_zero() { + let _ = PreciseNumber::from(1) / PreciseNumber::from(0); +} + +#[test] +fn test_approx_inv_exp() { + assert_eq!( + PreciseNumber::approx_inv_exp(PreciseNumber::from(0)), + PreciseNumber::from(1) + ); + assert_eq!( + PreciseNumber::approx_inv_exp(PreciseNumber::from(1) / PreciseNumber::from(2)), + PreciseNumber::from_dec_str("606530659712633423").unwrap() + ); + assert_eq!( + PreciseNumber::approx_inv_exp(PreciseNumber::from(1)), + PreciseNumber::from_dec_str("367879441171442320").unwrap() + ); +}