-
-
Save RandyMcMillan/a1fd7151d4089ba6f2aaa678c9954a97 to your computer and use it in GitHub Desktop.
bitcoin_attack.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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); | |
| } | |
| } |
Author
RandyMcMillan
commented
Oct 13, 2025

Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment