Skip to content

Core

org.blockchainbench.core is the engine. It accepts batches of configurations through a small public interface, runs them against the Palladio simulator on a background worker, and turns each raw simulator output into a typed, persisted result. Everything Palladio-specific — standalone initialisation, EMF model loading, factory selection, serialisation — is contained here so that the rest of the project never has to see it.

The only type front ends depend on is IBlockchainBench:

int submit(List<SimulationConfig> configs, BaseConfig baseConfig, SimulationMode mode);
int submit(List<SimulationConfig> configs, BaseConfig baseConfig, String modelPath, SimulationMode mode);
SimulationInfo getSimulation(int simulationId);
List<SimulationInfo> listSimulations();
void setBaseConfig(BaseConfig baseConfig);
void shutdown();

submit enqueues a group of runs and returns its simulationId without blocking. Callers observe progress and results through getSimulation, and release the worker thread with shutdown when they are done.

SimulationHandler is the sole implementation of IBlockchainBench. It holds:

  • a single-threaded simulation-worker executor (sequential FIFO, EMF-safe),
  • a registry of submitted groups (Map<Integer, SimulationInfo>),
  • a cache of initialised simulators, one per SimulationMode,
  • an atomic id sequence, and an optional fallback BaseConfig.

When a group is submitted, the handler builds one RunInfo per configuration, records a SimulationInfo in state QUEUED, and schedules process(...) on the worker. process sets the group to RUNNING, ensures the simulator for that mode is initialised (once, cached), then iterates the runs. For each run it resolves the model path, calls the simulator, appends the returned SimulationRun to the group’s result list, and marks the run FINISHED or ERROR. A failure in one run is caught and recorded on that run without aborting the rest; a fatal failure (caught as Throwable, so linkage and initialisation errors are included) marks the whole group ERROR.

Each run needs a path to a blockchain-system model. The handler resolves it in a fixed order and stops at the first value that is present:

  1. the path already set on the SimulationConfig,
  2. otherwise the group default from the BaseConfig,
  3. otherwise the modelPath passed to submit,
  4. otherwise a deterministic lookup by config_id via ConfigManager.pickModelPath, which maps to data/testmodels/threesim-<config_id>/Net.blockchainsystem.

The per-run work sits behind IBlockchainBenchSimulator, whose template implementation is AbstractStandaloneSimulator. The abstract class owns the shared skeleton — standalone initialisation, parameter construction, timing and memory measurement, and result persistence — and leaves three decisions to subclasses.

classDiagram
  class IBlockchainBench {
    <<interface>>
    +submit(...) int
    +getSimulation(id) SimulationInfo
    +listSimulations() List
    +setBaseConfig(BaseConfig)
    +shutdown()
  }
  class SimulationHandler {
    -ExecutorService executor
    -Map simulations
    -Map simulators
    -process(info, base, modelPath)
    -resolveModelPath(...) String
    -ensureInitialized(mode) IBlockchainBenchSimulator
  }
  class IBlockchainBenchSimulator {
    <<interface>>
    +initAnalysis() boolean
    +runSimulation(config, base, simId, runId) SimulationRun
  }
  class AbstractStandaloneSimulator {
    <<abstract>>
    +initAnalysis() boolean
    +runSimulation(...) SimulationRun
    #runSimulationFactory(...) String
    #attackerId() int
    #createOutputPath(simId, runId) Path
    #registerHk2OsgiResourceLocator() boolean
  }
  class BlockchainTrilemmaStandalone {
    #attackerId() int
  }
  class BlockchainTrilemmaAttackStandalone {
    #attackerId() int
  }

  IBlockchainBench <|.. SimulationHandler
  IBlockchainBenchSimulator <|.. AbstractStandaloneSimulator
  AbstractStandaloneSimulator <|-- BlockchainTrilemmaStandalone
  AbstractStandaloneSimulator <|-- BlockchainTrilemmaAttackStandalone
  SimulationHandler --> IBlockchainBenchSimulator : caches per mode

The two concrete simulators differ only in a few points:

AspectBlockchainTrilemmaStandalone (NORMAL)BlockchainTrilemmaAttackStandalone (ATTACK)
Factory usedTrilemmaSimulationFactorySelfishMiningSimulationFactory
Attacker id01
Output folderindiv_json/result_selfishmining/
HK2 resource locatorregisterednot registered
Stack trace on init failureprintedsuppressed

AbstractStandaloneSimulator.initAnalysis processes the EMF extension registry and then initialises the standalone environment through StandaloneInitializerBuilder. runSimulation builds the Palladio SimulationParameters (Monte-Carlo or single, chosen from BaseConfig.simulationType), delegates to the subclass factory, measures time and memory with RunMetrics, and hands everything to SimulationResultWriter.

The factories translate a resolved configuration into a concrete Palladio simulation and run it.

  • TrilemmaSimulationFactory builds either a ThreesimMonteCarloSimulation or a ThreesimSingleSimulation, wires in a LogOutputProviderImpl, runs it, and serialises the result with the Threesim JSON serialiser.
  • SelfishMiningSimulationFactory builds a MonteCarloDoubleSpendingAttackSimulation or a SingleDoubleSpendingAttackSimulation, using a LogOutputAttackProviderImpl and a SimulationRoundInterpretationImpl to classify each round’s outcome, and serialises with Gson.
  • BlockchainSystemFactoryResolver is shared by both. Given a SimulationConfig and an attackerPresent flag, it loads the model, inspects its network topology, and returns the matching Threesim factory (ConnectedSubgraphNetworkBlockchainSystemFactory or ExplicitNetworkBlockchainSystemFactory). An unsupported topology fails fast.
flowchart TB
  subgraph NORMAL
    N1["BlockchainTrilemmaStandalone"] --> N2["TrilemmaSimulationFactory"]
    N2 --> N3["Threesim Single / MonteCarlo simulation"]
  end
  subgraph ATTACK
    A1["BlockchainTrilemmaAttackStandalone"] --> A2["SelfishMiningSimulationFactory"]
    A2 --> A3["DoubleSpending Single / MonteCarlo simulation"]
  end
  N2 --> R["BlockchainSystemFactoryResolver"]
  A2 --> R
  R --> ML["BlockchainSystemModelLoader"]
  R --> TF["Threesim BlockchainSystem factory"]

BlockchainSystemModelLoader reads a BlockchainSystem EMF model and all of its co-located resources — network topology, node allocation, component repository, transactions, and so on — from the model’s folder under data/testmodels/. It registers the XMI resource factory and the relevant EMF packages, loads each of the seven resource extensions, and repeatedly calls EcoreUtil.resolveAll until the resource set stops growing, so that all cross-references are resolved before the model is handed to a factory.

RunMetrics brackets a run: at start() it triggers a garbage collection and records heap usage and a nanosecond timestamp; stopAndGetUsedMemoryMb() returns the delta in megabytes and captures the stop timestamp.

SimulationResultWriter produces the per-run artifact in three steps: parse the raw simulator JSON into a typed SimulationResult; assemble a SimulationRun that bundles the run metadata, the full input configuration, the base configuration, the typed result, and the timing and memory numbers; then serialise that back to pretty-printed JSON and write it to the run’s output path. The assembled SimulationRun is returned to the handler even if the disk write fails, so the in-memory result is never lost.

sequenceDiagram
  autonumber
  participant UI as Front end
  participant H as SimulationHandler
  participant W as simulation-worker
  participant S as AbstractStandaloneSimulator
  participant F as Factory
  participant P as 3SIM / BSCM
  participant D as Disk

  UI->>H: submit(configs, baseConfig, mode)
  H->>H: build runs, record SimulationInfo (QUEUED)
  H->>W: enqueue process(info)
  H-->>UI: simulationId
  W->>S: ensureInitialized(mode) then initAnalysis()
  Note over S,P: standalone init runs once per mode
  loop each run
    W->>S: runSimulation(config, base, simId, runId)
    S->>F: runSimulationFactory(params, config, base)
    F->>P: run()
    P-->>F: SimulationResult
    F-->>S: raw JSON
    S->>D: write result_run_simId_runId.json
    S-->>W: SimulationRun
    W->>W: append to info.results, run FINISHED
  end
  W->>H: info.status = FINISHED or ERROR
  UI->>H: getSimulation(id) (polled)
  • The data types exchanged with front ends and written to disk are documented in Utils.
  • The cross-layer flow and the status state machine are in Simulation lifecycle.