s-tikhomirov / ln-jamming-simulator

Simulating countermeasures against jamming attacks in the Lightning Network
MIT License
3 stars 0 forks source link

Lightning Network Jamming Simulator

A simulator for Lightning Network payments with unconditional fees, aimed at preventing jamming attacks.

See paper: Unjamming Lightning: A Systematic Approach.

Quick start

Requires NetworkX and NumPy. Tests require pytest.

How to run it:

$ python run.py --num_runs_per_simulation=10 --duration=300 --scenario=virtual --log_level=info --num_jamming_batches=5

See run.py -help for details.

Architecture

The general architecture is as follows.

The Simulator executes a simulation Scenario. A Scenario describes the network topology along with the properties of honest and malicious payments. The goal of a simulation is to estimate the revenue of certain nodes with and without an attack. The results are written as JSON and CSV files into /results (filenames include the current timestamp).

First, we prepare a Scenario. This involves the following:

Running the Scenario involves running two simulations: with and without jamming. A simulation implies, first, creating a Schedule, and then executing it. A Schedule consists of timestamped Events, where each event represents an honest payment or a jam. During execution, the simulator pops the next Event from the Schedule and executes it, until the schedule is empty.

To execute an Event, the simulator does the following:

Sending a Payment along the route involves these stages, until the payment fails or reaches the receiver:

For more implementation details, see classes.md and simulation.md.

An example of JSON output:

{
    "params": {
        "scenario": "wheel",
        "num_target_node_pairs": 8,
        "duration": 30,
        "num_runs_per_simulation": 10,
        "success_base_fee": 1,
        "success_fee_rate": 5e-06,
        "no_balance_failures": false,
        "default_num_slots_per_channel_in_direction": 483,
        "max_num_attempts_per_route_honest": 10,
        "max_num_attempts_per_route_jamming": 493,
        "dust_limit": 354,
        "honest_payments_per_second": 10,
        "min_processing_delay": 1,
        "expected_extra_processing_delay": 3,
        "jam_delay": 7
    },
    "simulations": {
        "honest": [
            {
                "upfront_base_coeff": 0,
                "upfront_rate_coeff": 0,
                "stats": {
                    "num_sent": 3.2,
                    "num_failed": 0.4,
                    "num_reached_receiver": 2.8
                },
                "revenues": {
                    "Alice": -1.3611165,
                    "Hub": 3.0027425,
                    "Bob": -0.5166645,
                    "Charlie": -0.3822195,
                    "Dave": -0.742742,
                    "JammerSender": 0,
                    "JammerReceiver": 0
                }
            }
        ],
        "jamming": [
            {
                "upfront_base_coeff": 0,
                "upfront_rate_coeff": 0,
                "stats": {
                    "num_sent": 2471.9,
                    "num_failed": 2471.9,
                    "num_reached_receiver": 2425
                },
                "revenues": {
                    "Alice": 0.0,
                    "Hub": 0.0,
                    "Bob": 0.0,
                    "Charlie": 0.0,
                    "Dave": 0.0,
                    "JammerSender": 0.0,
                    "JammerReceiver": 0.0
                }
            }
        ]
    }
}

The numbers in stats are not necessarily integers, as they are averaged across multiple simulation runs. The revenues for the jamming case in the example above are all zero, because jams fail and don't pay success-case fees. For upfront_base_coeff > 0 or upfront_rate_coeff > 0, that would not be the case.

The coefficients upfront_base_coeff and upfront_rate_coeff indicate what the unconditional fee parameters are in proportion to the default success-case parameters. If the success-case fee is 1 satoshi plus 5 parts per million, upfront_base_coeff is 2, and upfront_rate_coeff is 3, then the unconditional fee would be 2 satoshi plus 15 parts per million. (The numbers are picked just for the sake of an example; in real simulations, upfront fees are much lower.)