Conversation
elnosh
left a comment
There was a problem hiding this comment.
I'm having issues running the slow-jam attack - I have been running it for hours and still does not finish.
The behavior of the single-threaded paused tokio runtime is still somewhat obscure to me but I think I'm hitting the issue because of this https://github.com/carlaKC/sim-ln/blob/96c8f6dc3e8bf9d54e60f56f344f1e8e2a11fd00/simln-lib/src/clock.rs#L28-L29 and for slow-jam attack we hold the HTLC for 2 weeks while jamming and then we poll in a loop here
jam-ln/ln-simln-jamming/src/attacks/slow_jam.rs
Lines 411 to 420 in e7a9b6b
1ed0029 to
adc931b
Compare
Discussed offline: loop creates futures that can be in Speedup we get here is really nice, down from 20 minutes to a few seconds: One thing that we do lose here (which is a bit of a shame) is that we can't parallelize reading in the large peacetime file. I'm going to work on some changes to how we handle all this unweildy data anyway, so hopefully that's not such an issue long term. |
|
Diff since last review: |
history_from_file split the file into byte-range chunks read on parallel worker threads. We're about to switch to a single thread runtime, so we don't get any benefit from this after that. The chunking also had an off-by-one that silently dropped the final chunk (the whole file when there was only one chunk), which a plain sequential read can't hit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
If we have rust files called reputation or revenue they'd be ignored here.
Pull the body of each binary's main() into an async run() that accepts the clock, leaving main() responsible only for parsing, logging and constructing the clock. This sets up for handing run() a clock from a virtual-time runtime in a later commit. Applies to all three binaries: the attack simulation, the forward builder and the reputation builder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // Read the whole file sequentially. This previously split the file into byte-range chunks read | ||
| // on parallel worker threads, but under the single-threaded virtual-time runtime there is only | ||
| // ever one worker, so the chunking never parallelized - it only added complexity, and an | ||
| // off-by-one in the chunk loop silently dropped the final chunk (or the entire file when there | ||
| // was a single chunk). A plain sequential read cannot have a chunk-boundary bug. | ||
| let file = File::open(file_path)?; | ||
| let file_size = file.metadata()?.len(); | ||
| let filter_cutoff = { | ||
| if let Some(duration) = filter_duration { | ||
| let reader = BufReader::new(file); | ||
| let mut csv_reader = csv::Reader::from_reader(reader); | ||
| let mut first_record = StringRecord::new(); | ||
| csv_reader.read_record(&mut first_record)?; | ||
| let incoming_add_ts: u64 = first_record[4].parse()?; | ||
| Some(incoming_add_ts.add(duration.as_nanos() as u64)) | ||
| } else { | ||
| None | ||
| } | ||
| }; | ||
|
|
||
| let mut tasks: Vec<tokio::task::JoinHandle<Result<Vec<BootstrapForward>, BoxError>>> = | ||
| Vec::with_capacity(num_chunks); | ||
| let mut csv_reader = csv::Reader::from_reader(BufReader::new(file)); |
There was a problem hiding this comment.
yeah I realized after first couple runs that this was slower because it's done single-threaded in the runtime sim-ln creates :(
I don't think there's any reason we can't parallelize the read before actually running the simulation but that will require changes to sim-ln and for how much faster the simulation is already running I'd be fine if left as-is.
There was a problem hiding this comment.
I have aspirations to blitz the peacetime_traffic.csv now that we can run sims faster (we can just run peacetime and attack networks in parallel) so shall address in a followup!
| // Run on sim-ln's paused, single-threaded virtual-time runtime so the reputation replay completes instantly. | ||
| // Anchor virtual time at the real wall clock because the routing graph rejects channel updates whose timestamps | ||
| // are far from real time. | ||
| let start_time = SystemTime::now(); | ||
| block_on_virtual_time(start_time, |clock| run(clock, cli))??; |
There was a problem hiding this comment.
I'm wondering why does the reputation-builder need to be ran with the single-threaded runtime from sim-ln? Since we are not running a simulation but rather just replaying events. Maybe to keep the reputation building consistent but there's not much sleep/waits in those events
There was a problem hiding this comment.
Fair - probably unnecessary. I'm going to take a look at cleaning up all this setup so will get to this in a follow up!
8a3799c to
e2809d1
Compare
Pin simln-lib/sim-cli to the virtual-time PR head (enabling simln-lib's virtual-time feature) and adapt to the reimplemented SimulationClock: - Construct the clock with the new SimulationClock::new(start_time) signature in place of the speedup multiplier, across the binaries, tests and graph setup. - Reimplement the local InstantClock trait on tokio::time::Instant so elapsed time tracks virtual time. - Pass the latency interceptor's new seed parameter (None for now; seeding follows in a later commit). The binaries still build their own clock and run on a standard runtime; moving them onto the paused virtual-time runtime happens next. Tracks bitcoin-dev-project/sim-ln#309. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Make each binary's main() synchronous and drive run() through runtime::block_on_virtual_time, which owns the paused single-threaded runtime and hands run() its clock. A run now advances virtual time straight to the next event, completing as fast as the CPU allows instead of sleeping on real time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The virtual-time SimulationClock no longer takes a speedup multiplier, so the --clock-speedup flag (and its default) is now dead. Drop it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On the virtual-time runtime a run is deterministic for a fixed seed, so thread one shared SIM_SEED through the simulation. Feed it to both the SimulationCfg (payment generation, preimages) and the latency interceptor's seed parameter so that sampled delays are reproducible too, not just payments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Virtual time has no wall-clock limit, so an attack that never triggers shutdown would advance time forever. Cap the attack simulation's duration at one virtual year as a safeguard; attacks are expected to terminate well before this. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR points to a (currently PR'd) version of sim-ln that grants us access to a virtual clock (sped up using a tokio paused runtime).
This updates simulations that take hours to take seconds. I have run a few of our existing attacks on both main and this branch, and the numbers are within 2% of each other. Given that we don't have perfect determinism, this seems reasonable enough to me.