Skip to content

Commit

Permalink
update docs and change properties for more clarity
Browse files Browse the repository at this point in the history
  • Loading branch information
EasterTheBunny committed Nov 7, 2023
1 parent 40c1838 commit 39eaeba
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 71 deletions.
258 changes: 206 additions & 52 deletions SIMULATOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,120 +55,274 @@ $ ./bin/simulator --simulate -f ./tools/simulator/plans/simplan_fast_check.json
- `--pprof [bool]`: default false, run pprof server on simulation startup
- `--pprof-port [int]`: default 6060, port to serve pprof profiler on

### Simulation Plan Options
## Simulation Plan

A simulation plan is a set of configurations for the simulator defined in a JSON
file. Each property is described below.
A simulation plan is a set of configurations for the simulator defined in a JSON file and is designed to produce a
consistent simulation between runs. Be aware that there is some variance in simulation outcomes from the same simulation
plan due to randomness in the `rpc`, `blocks`, or `p2pNetwork` configurations. Events are precise relative to block
production.

### Node

The instantiation of a node involves creating simulated dependencies and providing them to the delegate constructor for
the plugin.

*node*
[object]

This object is a container for node related configuration values.
Configuration values that apply to setting up nodes in the network.

*node.totalNodeCount*
[int]

Total number of nodes to run in the simulation. Each node is connected via a
simulated p2p network and provided an isolated contract/RPC simulation.
Total number of nodes to run in the simulation. Each node is connected via a simulated p2p network and provided an
isolated contract/RPC simulation.

*node.maxNodeServiceWorkers*
[int]

Service workers are used to parallelize RPC calls to be able to process more in
a short time. This number is to set the upper limit on the number of service
workers per simulated node.
Service workers are used to parallelize RPC calls to be able to process more in a short time. This number is to set the
upper limit on the number of service workers per simulated node.

*node.maxNodeServiceQueueSize*
[int]

Max queue size for sending work to service workers. This should be deprecated
soon.
Max queue size for sending work to service workers. This should be deprecated soon.

### P2PNetwork

*p2pNetwork.avgLatency*
The p2p network simulation does not include any tcp/udp networking layers and only serves the perpose of inter-node
communication. The simulated network can be configured to simulate nodes operating on the same hardware or in close
proximity or configured to simulate nodes spread across a large physical distance. A `maxLatency` of `300ms` might
simulate the physical distance of nodes operating in Paris and Singapore, for example.

*p2pNetwork*
[object]

Configure the simulated p2p network.

*p2pNetwork.maxLatency*
[int]

The total amount of time a message should take to be sent in the simulated p2p
network. This is an average and is calculated by taking a random number between
0 and the defined latency.
The maximum amount of time a message should take to be sent in the simulated p2p network. This is calculated by taking
a random number between 0 and the provided latency.

### RPC

A simulated RPC is the connection layer between the block producer and the node. In a real-world environment, the block
production can be imagined as a singular source and RPCs independently read the state of block production. In this way,
each RPC can have a different 'view' of the singular block source.

Each node gets an isolated instance of a simulated RPC. The role the RPC plays is to surface changes made to the
singular block source such as new upkeeps being created, ocr configs being committed, or logs being emitted.

*rpc*
[object]

This object is a container for RPC related configurations. There is currently a
limit of a single RPC simulation configuration and applies to all instances.
Configure the behavior of the simulated RPC. There is currently a limit of a single RPC simulation configuration and
applies to all instances.

*rpcDetail.maxBlockDelay*
*rpc.maxBlockDelay*
[int]

The maximum delay in in milliseconds that an RPC would deliver a new block.

*rpcDetail.averageLatency*
*rpc.averageLatency*
[int]

The average response latency of a simulated RPC call. All latency calculations
have a baseline of 50 milliseconds with an added latency calculated as a
binomial distribution of the configuration where `N = conf * 2` and `P = 0.4`.
The average response latency of a simulated RPC call. All latency calculations have a baseline of 50 milliseconds with
an added latency calculated as a binomial distribution of the configuration where `N = conf * 2` and `P = 0.4`.

*rpcDetail.errorRate*
*rpc.errorRate*
[float]

The probability that an RPC call will return an error. `0.02` is `2%`
The probability that an RPC call will return an error. `0.02` is `2%`. RPC providers are essentially cloud services that
have potential failures. Use this configuration to simulate a flaky RPC provider.

*rpcDetail.rateLimitThreshold*
*rpc.rateLimitThreshold*
[int]

Total number of calls per second before returning a rate limit response from the
simulated RPC provider.
Total number of calls per second before returning a rate limit response from the simulated RPC provider.

### Blocks

*blockDetail*
Simulated blocks in the context of the simulator are only containers of events that apply to the network of nodes and
that are provided to the network on a defined cadence. The concept of signatures, and hashes doesn't apply. Where the
term `hash` is used, the value is likely either a randomly generated value or a value derived from some block data.

*blocks*
[object]

Configuration object for simulated chain. The chain is a coordinated block
producer that feeds each simulated RPC by a dedicated channel.
Configuration object for simulated chain. The chain is a coordinated block producer that feeds each simulated RPC by a
dedicated channel. Each simulated RPC can receive blocks at different times.

*blockDetail.genesisBlock*
*blocks.genesisBlock*
[int]

The block number for the first simulated block. Formatted as time.
The block number for the first simulated block.

*blockDetail.blockCadence*
*blocks.blockCadence*
[string]

The rate at which new blocks are created. Formatted as time.

*blockDetail.blockCadenceJitter*
*blocks.blockCadenceJitter*
[string]

Some chains produce blocks on a well defined cadence. Most do not. This
parameter allows some jitter to be applied to the block cadence.
Block cadenece jitter is applied to block production such that each block is not produced exactly on the cadence.

*blockDetail.durationInBlocks*
*blocks.durationInBlocks*
[int]

A simulation only runs for this defined number of blocks. The configured upkeeps
are applied within this range.
A simulation only runs for this defined number of blocks. The configured upkeeps are applied within this range.

*blockDetail.endPadding*
*blocks.endPadding*
[int]

The simulated chain continues to broadcast blocks for the end padding duration
to allow all performs to have time to be completed. The configured upkeeps do
not apply to this block set.
The simulated chain continues to broadcast blocks for the end padding duration to allow all performs to have time to be
completed. The configured upkeeps do not apply to this block set.

### Events

*configEvents*
All events are applied to blocks as they are produced. Each event contains at least a `type` that describes how to
process the event and a `eventBlockNumber` which defines the block in which to apply the event. Many events are
singular. Some events are generative in that multiple events are generated from a single configuration.

Generative events, such as `generateUpkeeps`, allows a more collapsed JSON config. The specific case of generating
upkeeps will create `count` upkeep create events for the same `eventBlockNumber`.

*events*
[array[object]]

Config events change the state of the network and at least 1 is required to
start the network configuration. Each event is broadcast by the simulated chain
at the block defined.
Config events change the state of the network and at least 1 is required to start the network configuration. Each event
is broadcast by the simulated chain at the block defined.

Every event has some common properties:

*type*
[string:required]

Determines the event type. Options include `ocr3config`, `generateUpkeeps`, `logTrigger`

*eventBlockNumber*
[int:required]

Block number to commit this event to block history.

*comment*
[string:optional]

Optional reference value. Not output on logs.


#### OCR 3 Config

- type: `ocr3config`

An event with the type `ocr3config` indicates that a new network configuration was committed to the block history. In a
real-world scenario, this would be an on-chain transaction that emits a log. In the simulation, the event is a specific
type and is recognized by the simulated RPC.

TODO: describe OCR config values and how they are provided

*encodedOffchainConfig*
[string]

The encoded config does not currently enforce an encoding type. An OCR off-chain config is an array of bytes allowing
the encoding to be anything. This configuration property should be the value already encoded as the plugin expects. In
the case of JSON, include character escaping such as `{\"version\":\"v3\"}`.

#### Generate Upkeeps

- type: `generateUpkeeps`

Multiple upkeep events can be generated by using this event type. An upkeep event simulates an upkeep being added to a
registry and made active. The only states relevant to the simulator regarding registered upkeeps are: is it active, and
is it eligible?

*configEvents.triggerBlockNumber*
An upkeep becomes active on the `eventBlockNumber` where it is committed to the block history. From that point,
eligibility begins to apply, which is defined by the `eligibilityFunc`. No other events will apply to an upkeep until
after the upkeep becomes active in the block history, which includes `logTrigger` type events.

The `eligibilityFunc` allows a basic linear function to be supplied indicating when, relative to the trigger block, an
upkeep should be eligible.

Example:

func: `2x + 1`
active at block: `100`
final block: `500`

```
let start = 100;
let end = 500;
let i = 0;
let next = 0;
let eligible = [];
while next < end {
if next > start {
eligible.push(next);
}
let y = (2 * i) + 1;
next = start + Math.round(y);
i++;
}
// the eligibility function makes the upkeep eligible every 2 blocks with a relative
// offset of 1 to the start block
// eligible: [101, 103, 105, 107, ...]
```

The `offsetFunc` advances the `start` point for each generated upkeep to ensure eligibility doesn't overlap for each
generated upkeep.

Special options such as `always` and `never` are also available.

*count*
[int]

The block to broadcast the event on. This block should be after the genesis
block and before the final simulation block.
Total number of upkeeps to generate for the event configuration.

*startID*
[int]

ID to reference the upkeep in the config. The UpkeepID output will be different.

*eligibilityFunc*
[string]

Simple linear equation for generating eligibility. Also allowed are `always` and `never`.

*offsetFunc*
[string]

Simple linear equation for eligibility start offset. Allowed to be empty when `eligibilityFunc` is `always` or `never`.

*upkeepType*
[string]

Options are `conditional` and `logTrigger`.

*logTriggeredBy*
[string]

This value applies to a log trigger type upkeep and is the reference point for a `logTrigger` event. If this value
matches the `triggerValue` of a `logTrigger` event, this upkeep is 'triggered' by the log trigger event.

#### Log Events

- type: `logTrigger`

A simulated log event does only simulates the existence of a real-world log and the value for matching to an upkeep
trigger. Once a log event is active in the block history, it can 'trigger' any active and eligible `logTrigger` type
upkeeps with a matching `triggerValue`.

*configEvents.offchainConfigJSON*
*triggerValue*
[string]

Stringified JSON for off-chain configuration.
Value used to 'trigger' upkeeps.
2 changes: 1 addition & 1 deletion tools/simulator/config/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const (

type Event struct {
Type EventType `json:"type"`
TriggerBlock *big.Int `json:"triggerBlockNumber"`
TriggerBlock *big.Int `json:"eventBlockNumber"`
Comment string `json:"comment,omitempty"`
}

Expand Down
4 changes: 2 additions & 2 deletions tools/simulator/config/simulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ type Node struct {
// Network is a configuration for the simulated p2p network between simulated
// nodes.
type Network struct {
// AvgLatency applies to the amout of time a message takes to be sent
// MaxLatency applies to the amout of time a message takes to be sent
// between peers. This is intended to simulate delay due to physical
// distance between nodes or other network delays.
AvgLatency Duration `json:"avgLatency"`
MaxLatency Duration `json:"maxLatency"`
}

// RPC is a configuration for a simulated RPC client. Each node recieves their
Expand Down
2 changes: 1 addition & 1 deletion tools/simulator/config/simulation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestSimulationPlan_EncodeDecode(t *testing.T) {
MaxQueueSize: 1000,
},
Network: Network{
AvgLatency: Duration(300 * time.Millisecond),
MaxLatency: Duration(300 * time.Millisecond),
},
RPC: RPC{},
Blocks: Blocks{
Expand Down
2 changes: 1 addition & 1 deletion tools/simulator/node/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func NewGroup(conf GroupConfig, progress *telemetry.ProgressTelemetry) (*Group,
return &Group{
conf: conf.SimulationPlan,
nodes: make(map[string]*Simulator),
network: net.NewSimulatedNetwork(conf.SimulationPlan.Network.AvgLatency.Value()),
network: net.NewSimulatedNetwork(conf.SimulationPlan.Network.MaxLatency.Value()),
digester: conf.Digester,
blockSrc: chain.NewBlockBroadcaster(conf.SimulationPlan.Blocks, conf.SimulationPlan.RPC.MaxBlockDelay, conf.Logger, progress, loaders...),
transmitter: lTransmit,
Expand Down
Loading

0 comments on commit 39eaeba

Please sign in to comment.