Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created October 13, 2025 14:46
Show Gist options
  • Select an option

  • Save rust-play/f4e51da6c435de4ddd35bd175e1ab479 to your computer and use it in GitHub Desktop.

Select an option

Save rust-play/f4e51da6c435de4ddd35bd175e1ab479 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
//use std::f64;
/// Calculates the factorial of a non-negative integer 'n'.
/// Returns the result as a floating-point number (f64).
fn factorial(n: u32) -> f64 {
if n == 0 {
1.0
} else {
// Calculates the product of integers from 1 up to n.
(1..=n).map(|i| i as f64).product()
}
}
/// Calculates the probability that the honest network successfully secures a transaction
/// (e.g., against a double-spend attack) given an attacker's hash power and a required
/// number of confirmations. This is often used in Bitcoin security analysis.
///
/// The formula is:
/// $$1 - \sum_{k=0}^{z} \frac{\lambda^k e^{-\lambda}}{k!} \left(1 - \left(\frac{q}{p}\right)^{(z-k)}\right)$$
///
/// Parameters:
/// - lambda: Mean number of blocks the attacker is expected to control (\lambda).
/// - p: Honest network's fractional hash power ($p$).
/// - q: Attacker's fractional hash power ($q$).
/// - z: Number of confirmations required for the transaction ($z$).
///
/// Returns:
/// The probability that the honest chain secures the transaction, as an f64.
fn calculate_attack_security_probability(lambda: f64, p: f64, q: f64, z: u32) -> f64 {
if p == 0.0 {
// Panics if the denominator is zero, as division would be infinite or NaN.
panic!("Parameter 'p' (Honest Hash Power) cannot be zero.");
}
let ratio_qp = q / p;
let exp_neg_lambda = (-lambda).exp(); // e^{-\lambda}
let mut sum: f64 = 0.0;
for k in 0..=z {
// 1. Poisson Term: P(k; \lambda) - Probability attacker mines exactly k blocks (P_A).
let lambda_pow_k = lambda.powi(k as i32);
let k_factorial = factorial(k);
let poisson_term = (lambda_pow_k * exp_neg_lambda) / k_factorial;
// 2. Conditional Term: 1 - (q/p)^(z-k) - Probability honest network catches up.
let exponent = (z - k) as i32;
let power_term = 1.0 - ratio_qp.powi(exponent);
// Summation: Sum of P_A * (Probability honest chain wins given P_A)
sum += poisson_term * power_term;
}
// Final result: 1 - (Probability attacker wins)
1.0 - sum
}
// -------------------------------------------------------------
fn main() {
// Bitcoin Attack Analysis Example:
// Parameters for a small-scale attacker aiming for 3 confirmations.
let lambda = 2.5; // Attacker expects to control 2.5 blocks on average
let p = 0.6; // Honest network has 60% hash power
let q = 0.4; // Attacker has 40% hash power
let z = 3; // Transaction requires 3 confirmations
let result = calculate_attack_security_probability(lambda, p, q, z);
println!("--- Bitcoin Attack Security Model ---");
println!("λ (Attacker Expected Blocks): {}", lambda);
println!("p (Honest Hash Power): {}", p);
println!("q (Attacker Hash Power): {}", q);
println!("z (Confirmations Required): {}", z);
println!("\nCalculated P(Honest Chain Secures Transaction): {:.8}", result);
// The probability of a successful attack is 1 - result.
println!("Calculated P(Successful Attack): {:.8}", 1.0 - result);
}
// -------------------------------------------------------------
// Test Module
// -------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
// Tolerance for floating-point comparisons (0.00002)
const EPSILON: f64 = 2e-5;
// Helper to check for approximate equality
fn assert_approx_eq(a: f64, b: f64) {
assert!((a - b).abs() < EPSILON, "Assertion failed: {} is not close to {}", a, b);
}
// --- Factorial Tests (Quick check) ---
#[test]
fn test_factorial_small_values() {
assert_approx_eq(factorial(0), 1.0);
assert_approx_eq(factorial(5), 120.0);
}
// --- Security Model Tests ---
#[test]
// Tests the primary example case (lambda=2.5, p=0.6, q=0.4, z=3)
fn test_verified_case_1() {
let lambda = 2.5;
let p = 0.6;
let q = 0.4;
let z = 3;
// Expected value (Probability Honest Chain Wins)
let expected = 0.742742;
let result = calculate_attack_security_probability(lambda, p, q, z);
assert_approx_eq(result, expected);
}
#[test]
// Tests a case where q/p = 1.0 (equal hash power). Probability should be 1.0.
fn test_equal_hash_power() {
let lambda = 1.0;
let p = 0.5;
let q = 0.5;
let z = 5;
let expected = 1.0;
let result = calculate_attack_security_probability(lambda, p, q, z);
assert_approx_eq(result, expected);
}
#[test]
// Tests a case with a larger z and small attacker power (q/p = 0.111...).
// Attacker's probability of success should be very low (Honest win high).
fn test_small_attacker_power() {
let lambda = 0.5;
let p = 0.9;
let q = 0.1;
let z = 5;
// Corrected expected value (Probability Honest Chain Wins)
let expected = 0.00065204;
let result = calculate_attack_security_probability(lambda, p, q, z);
assert_approx_eq(result, expected);
}
#[test]
#[should_panic]
// Tests the necessary panic when the honest network hash power is zero.
fn test_panic_on_p_zero() {
calculate_attack_security_probability(1.0, 0.0, 1.0, 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment