The simulator is designed to emulate various network conditions and track the state of network actors at specific times. In simulations, it uses the Carnot consensus engine Rust implementation as its actors, and all messages pass through the simulated network.

The simulator operates in three main stages in each simulation epoch:

  1. Network Message Dispersal: This stage involves distributing messages across actors in the network. The network layer of the simulator handles this, taking into account configurable latencies and bandwidth constraints.
  2. Actor Execution: Actors process the network messages they receive.
  3. Message Collection from Actors: After processing the messages, actors generate new network messages. These are collected by the simulator for dispersal in the next epoch.

An "epoch" in the simulator refers to a specific time point where the state of all actors can be analyzed or recorded. Each epoch represents a fixed interval of time, occurring one after another. The length of an epoch is adjustable and is considered by the network layer when dispatching and collecting messages.

The network layer is responsible for managing how messages are sent and received among actors. The simulator allows the placement of actors in seven different regions, potentially having different simulated latencies between each other. The network layer ensures that if the latency between regions is shorter than the epoch's time, the message is delivered. Messages have simulated sizes in bytes, and the network layer checks these against the bandwidth capacity of the receiving actor. If the receiver is not operating at full capacity, depending on the available bandwidth, either the entire message or a portion of it is transmitted. The receiver gets the complete message once the regional latencies and bandwidth limits allow it.

The simulator is configurable via the command line or a JSON file and allows these parameters to be modified: Network Settings, which customize latency between global regions and node distribution; Overlay Settings to determine the network structure, either flat or hierarchical; Node Settings for defining network capacity and timeouts per node; Step Time to set the simulation epoch interval; Runner Settings, offering various execution modes like Sync, Async, Glauber, or Layered; Stream Settings to specify the input data stream path; Node Count to set the total number of nodes; Record Settings to enable or disable the recording of various simulation metrics and states.

The simulator code was written in Rust (1.75.0). “Rayon” library was used to enable parallel execution of actors and network layer. The simulator was specifically design to be able to simulate tens of thousands individual Carnot engine instances. The separation of network layer and actors allows to use the same simulator with different actors e.g. nodes using different consensus engine.

The source code for the simulator can be found at https://github.com/logos-co/nomos-node/tree/master/simulations, the configuration used to generate and analyze the data in this paper can be found at https://github.com/logos-co/nomos-simulations