General simulation flow
- Specify the simulation parameters
- Name node participants and groups
- Parameters
- Specify the graph
- Trust Edges
- Network/Messaging edges
- Specify events/dynamic actions
- How/when transactions are submitted
- How/when network layers or trust relationships change
- Specify collectors/listeners
- Could be "pure" that are output only
- Closed-loop that listen and interact with network
- Run simulation
General Thoughts
- Consistent names (nodes vs. peers)
- Consistent types (NodeID vs uint32)
- How much overlay algorithm needs to allow specialization?
- Create generic
Digraph<Node, Edge>
that stores as adjacency list? Just wrap boost::graph? - Use this for TrustGraph and NetworkGraph/BasicNetwork
- Create NodeGroup and allow named aliases for sets of nodes
- Add graph builders (scalefree, ring, two-clique?)
- Decide on interface .. use
sim.trust
orgroup.trust
- Decide on interface .. use
- Overlay wrapper for BasicNetwork?
- UNL from trustgraph? Just provide reference to outEdges?
- JSON/.dot output and input?
Simulate the current production setup with 5 validators in a core group and a set of client handlers that receive the transactions.
Simulation s;
// NodeGroup is a light-weight reference to a set of nodes in the simulation
NodeGroup validators = s.createNodeGroup("Validators", 5);
NodeGroup handlers = s.createNodeGroup("ClientHandlers", 3);
NodeGroup network = validators + handlers;
for(Peer & handler : handlers)
{
// Peer is the full peer class instance
// peer.id() is the global unique ID for a peer
handler.isValidator = false;
}
{
// Version 1, use groups/nodes directly
// - UNL and network links are owned by each Peer?
// - Or still owend by Simulation
validators.trust(validators);
handlers.trust(validators);
// all nodes connected to all other nodes
network.link(network, 100ms);
}
{
// Version 2, go through simulator
// explicit for user
s.trust(validators, validators);
s.trust(handlers,validators);
s.link(network, network, 100ms);
}
TxSubmitter txs{start,stop,rate/model};
txs.target(handlers);
Collector standardCollector;
s.addCollect(standardCollector);
// Run for 10 rounds
s.run(10);
// Report result
standardCollector.report();
Simulation s;
NodeGroup northAmerica = s.createNodeGroup("NorthAmerica", 4);
NodeGroup southAmerica = s.createNodeGroup("SouthAmerica", 4);
NodeGroup europe = s.createNodeGroup("Europe", 4);
NodeGroup asia = s.createNodeGroup("Asia", 4);
// BEAST_EXPECT(s.nodeGroup("Europe") == europe)
// BEAST_EXPECT(europe(0) == s.node(europe(0).id()))
NodeGroup network = northAmerica + southAmerica + europe + asia;
// All nodes trust all other nodes
network.trust(network);
// Local network delays are small
for(auto & group : {northAmerica, southAmerica, europe, asia})
northAmerica.connect(northAmerica, 10ms);
// NA to SA random 500 ms +/- 10 ms
s.network_delay(northAmerica, southAmerica, normal(500ms, 10ms));
...
// All txs submitted to Asia
TxSubmitter txs{args,...};
txs.target(asia);
LatencyCollector lc;
s.addCollector(lc);
// run for 1000 seconds of simulation time
s.run(1000s);
lc.printReport();
Simulation s;
// Unnamed
NodeGroup network = s.createNodeGroup(100);
// Various trust primitives
// network.trust(T)
// where T::operator(NodeGroup) sets trust relations
network.trust(network); // clique
network.trust(scaleFree{parms});
network.trust(ring{});
network.trust(tree{network[0], 4 /* children */});
// Network primitives
// network.link(T)
// where T::operator(NodeGroup) sets connection/delay
network.link(network, DelayModel);
network.link(fromTrust{}, DelayModel);
network.link(scaleFree{parms}, DelayModel);
network.link(ring{}, DelayModel);
// Building things out
NodeGroup a = s.createNodeGroup("a",10);
NodeGroup b = s.createNodeGroup("b",10);
a.trust(a);
b.trust(b);
// Links one node of a to all of b
a[3].trust(b);
b.trust(a[3]);
Simulation s;
NodeGroup a = s.createNodeGroup("a", 20);
NodeGroup b = s.createNodeGroup("b", 20);
NodeGroup c = s.createNodeGroup("b", 30);
NodeGraph network = a + b + c;
network.trust(network);
// Should links really be directed?
a.link(b, DelayModel);
b.link(a, DelayModel);
b.link(c, DelayModel);
c.link(b, DelayModel);
// Register net split in the future
s.at(1000s, [&]()
{
a.unlink(b);
b.unlink(a);
c.unlink(b);
b.unlink(c);
});
s.addCollector(foo);
s.run(10000s);
Simulation s = Simulation::fromSpec("config.json/.dot");
// Prefer JSON so it can define node group/names