Skip to content

Commit 49c39b5

Browse files
apollo_consensus_orchestrator: add SNIP-35 proposer-validator symmetry tests
1 parent 601eeac commit 49c39b5

3 files changed

Lines changed: 89 additions & 35 deletions

File tree

Cargo.lock

Lines changed: 23 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_consensus_orchestrator/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ metrics-exporter-prometheus.workspace = true
7777
mockall.workspace = true
7878
mockito.workspace = true
7979
num-bigint.workspace = true
80+
rand.workspace = true
81+
rand_chacha.workspace = true
8082
rstest.workspace = true
8183
serde_json.workspace = true
8284

crates/apollo_consensus_orchestrator/src/snip35/test.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1+
use rand::{Rng, SeedableRng};
2+
use rand_chacha::ChaCha8Rng;
13
use rstest::rstest;
24
use starknet_api::block::GasPrice;
35

4-
use crate::snip35::{compute_fee_actual, compute_fee_proposal, compute_fee_target};
6+
use crate::snip35::{
7+
compute_fee_actual,
8+
compute_fee_proposal,
9+
compute_fee_target,
10+
FEE_PROPOSAL_MARGIN_PPT,
11+
ORACLE_L2_GAS_FLOOR_MAX_FRI,
12+
ORACLE_L2_GAS_FLOOR_MIN_FRI,
13+
PPT_DENOMINATOR,
14+
TARGET_ATTO_USD_PER_L2_GAS,
15+
};
516

617
const FRI_DECIMALS_SCALE: u128 = 10u128.pow(18);
718
const FLOOR_MIN_FRI: u128 = 100;
@@ -134,3 +145,55 @@ fn test_compute_fee_actual_lone_adversary_cannot_skew_median() {
134145
window.push(GasPrice(u128::MAX / 2));
135146
assert_eq!(compute_fee_actual(&window, 10), Some(GasPrice(1_000_000)));
136147
}
148+
149+
/// The validator's accept predicate. Must stay in sync with
150+
/// `validate_proposal::is_proposal_init_valid` SNIP-35 bounds check.
151+
fn validator_accepts(fee_actual: GasPrice, fee_proposal: GasPrice, margin_ppt: u128) -> bool {
152+
let lower = fee_actual.0.saturating_mul(PPT_DENOMINATOR) / (PPT_DENOMINATOR + margin_ppt);
153+
let upper = fee_actual.0.saturating_mul(PPT_DENOMINATOR + margin_ppt) / PPT_DENOMINATOR;
154+
fee_proposal.0 >= lower && fee_proposal.0 <= upper
155+
}
156+
157+
#[test]
158+
fn test_malicious_high_fee_proposal_rejected() {
159+
// Upper bound for fee_actual=1_000_000 with margin=2ppt is 1_002_000.
160+
let fee_actual = GasPrice(1_000_000);
161+
assert!(validator_accepts(fee_actual, GasPrice(1_002_000), 2));
162+
for proposal in [1_002_001u128, 1_003_000, 2_000_000, u128::MAX] {
163+
assert!(!validator_accepts(fee_actual, GasPrice(proposal), 2), "accepted {proposal}");
164+
}
165+
}
166+
167+
#[test]
168+
fn test_malicious_low_fee_proposal_rejected() {
169+
// Lower bound for fee_actual=1_000_000 with margin=2ppt is 998_003.
170+
let fee_actual = GasPrice(1_000_000);
171+
assert!(validator_accepts(fee_actual, GasPrice(998_003), 2));
172+
for proposal in [998_002u128, 500_000, 1, 0] {
173+
assert!(!validator_accepts(fee_actual, GasPrice(proposal), 2), "accepted {proposal}");
174+
}
175+
}
176+
177+
#[test]
178+
fn test_honest_proposer_always_passes_validation_fuzzed() {
179+
// Consensus safety: whatever compute_fee_proposal produces, the validator accepts.
180+
let mut rng = ChaCha8Rng::seed_from_u64(0xDEADBEEF);
181+
for _ in 0..10_000 {
182+
let fee_actual_value = rng.gen_range(1u128..1_000_000_000_000_000_000);
183+
let strk_usd_rate = rng.gen_range(1u128..2 * 10u128.pow(18));
184+
let fee_actual = GasPrice(fee_actual_value);
185+
let target = compute_fee_target(
186+
TARGET_ATTO_USD_PER_L2_GAS,
187+
strk_usd_rate,
188+
ORACLE_L2_GAS_FLOOR_MIN_FRI,
189+
ORACLE_L2_GAS_FLOOR_MAX_FRI,
190+
);
191+
let oracle_result = if rng.gen_bool(0.1) { None } else { Some(target) };
192+
let proposal = compute_fee_proposal(oracle_result, fee_actual, FEE_PROPOSAL_MARGIN_PPT);
193+
assert!(
194+
validator_accepts(fee_actual, proposal, FEE_PROPOSAL_MARGIN_PPT),
195+
"fee_actual={fee_actual_value} rate={strk_usd_rate} proposal={}",
196+
proposal.0
197+
);
198+
}
199+
}

0 commit comments

Comments
 (0)