Skip to content

Commit 7895fa9

Browse files
apollo_consensus_orchestrator: add SNIP-35 proposer-validator symmetry tests
1 parent 8bac2e4 commit 7895fa9

3 files changed

Lines changed: 88 additions & 33 deletions

File tree

Cargo.lock

Lines changed: 21 additions & 32 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: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
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, Snip35Info};
6+
use crate::snip35::{
7+
compute_fee_actual,
8+
compute_fee_proposal,
9+
compute_fee_target,
10+
Snip35Info,
11+
FEE_PROPOSAL_MARGIN_PPT,
12+
ORACLE_L2_GAS_FLOOR_MAX_FRI,
13+
ORACLE_L2_GAS_FLOOR_MIN_FRI,
14+
PPT_DENOMINATOR,
15+
TARGET_ATTO_USD_PER_L2_GAS,
16+
};
517

618
#[test]
719
fn snip35_info_serializes_field_by_name() {
@@ -168,3 +180,55 @@ fn test_compute_fee_actual_lone_adversary_cannot_skew_median() {
168180
window.push(GasPrice(u128::MAX / 2));
169181
assert_eq!(compute_fee_actual(&window, 10), Some(GasPrice(1_000_000)));
170182
}
183+
184+
/// The validator's accept predicate. Must stay in sync with
185+
/// `validate_proposal::is_proposal_init_valid` SNIP-35 bounds check.
186+
fn validator_accepts(fee_actual: GasPrice, fee_proposal: GasPrice, margin_ppt: u128) -> bool {
187+
let lower = fee_actual.0.saturating_mul(PPT_DENOMINATOR) / (PPT_DENOMINATOR + margin_ppt);
188+
let upper = fee_actual.0.saturating_mul(PPT_DENOMINATOR + margin_ppt) / PPT_DENOMINATOR;
189+
fee_proposal.0 >= lower && fee_proposal.0 <= upper
190+
}
191+
192+
#[test]
193+
fn test_malicious_high_fee_proposal_rejected() {
194+
// Upper bound for fee_actual=1_000_000 with margin=2ppt is 1_002_000.
195+
let fee_actual = GasPrice(1_000_000);
196+
assert!(validator_accepts(fee_actual, GasPrice(1_002_000), 2));
197+
for proposal in [1_002_001u128, 1_003_000, 2_000_000, u128::MAX] {
198+
assert!(!validator_accepts(fee_actual, GasPrice(proposal), 2), "accepted {proposal}");
199+
}
200+
}
201+
202+
#[test]
203+
fn test_malicious_low_fee_proposal_rejected() {
204+
// Lower bound for fee_actual=1_000_000 with margin=2ppt is 998_003.
205+
let fee_actual = GasPrice(1_000_000);
206+
assert!(validator_accepts(fee_actual, GasPrice(998_003), 2));
207+
for proposal in [998_002u128, 500_000, 1, 0] {
208+
assert!(!validator_accepts(fee_actual, GasPrice(proposal), 2), "accepted {proposal}");
209+
}
210+
}
211+
212+
#[test]
213+
fn test_honest_proposer_always_passes_validation_fuzzed() {
214+
// Consensus safety: whatever compute_fee_proposal produces, the validator accepts.
215+
let mut rng = ChaCha8Rng::seed_from_u64(0xDEADBEEF);
216+
for _ in 0..10_000 {
217+
let fee_actual_value = rng.gen_range(1u128..1_000_000_000_000_000_000);
218+
let strk_usd_rate = rng.gen_range(1u128..2 * 10u128.pow(18));
219+
let fee_actual = GasPrice(fee_actual_value);
220+
let target = compute_fee_target(
221+
TARGET_ATTO_USD_PER_L2_GAS,
222+
strk_usd_rate,
223+
ORACLE_L2_GAS_FLOOR_MIN_FRI,
224+
ORACLE_L2_GAS_FLOOR_MAX_FRI,
225+
);
226+
let oracle_result = if rng.gen_bool(0.1) { None } else { Some(target) };
227+
let proposal = compute_fee_proposal(oracle_result, fee_actual, FEE_PROPOSAL_MARGIN_PPT);
228+
assert!(
229+
validator_accepts(fee_actual, proposal, FEE_PROPOSAL_MARGIN_PPT),
230+
"fee_actual={fee_actual_value} rate={strk_usd_rate} proposal={}",
231+
proposal.0
232+
);
233+
}
234+
}

0 commit comments

Comments
 (0)