A simulator for the Malachite core libraries.
This application demonstrates the simplest way to instantiate the Malachite core library. It also demonstrates the extreme flexibility of the library.
The simulator is meant to be a playground for learning how to code with the core library.
The simulator is meant to facilitate the experimentation with new abstractions on top of the core library. This is achieved by enabling quick prototyping and ways to fail fast in the writing of new applications, because the simulator ignores orthogonal concerns such as networking, signature verification, or transaction memory pool.
The simulator consists of three main parts:
- The
main.rs
program, - The
Simulator
, and - Multiple instances of an
Application
, each instance representing the program running at a peer.
The sketch below shows a system context diagram of these three parts.
The Simulator orchestrates the execution of applications that are running at different peers, where all applications are aiming to decide on values. Each application represents a specific peer and is a simple data structure running in the same thread as all the other peers. The application is a wrapper over the Malachite core library, which implements the core consensus state machine of the Tendermint algorithm and does the actual heavy lifting behind the curtains.
The system simulates the execution of a complex distributed system in a single, centralized process. This is achieved through an array of assumptions, as follows:
- the applications at all peers execute in the same thread, and they execute sequentially (i.e., not in parallel with one another)
- the simulator orchestrates the execution steps at various application instances, therefore, the simulator is trusted;
- there is no need for actual networking code: applications send messages to one another via a
Sender
on a local channel; - digital signatures: all peers have the same public/private key; they sign each message, but verification is mocked;
- transaction memory pool:
- peers do not decide on (i.e., finalize) blocks of transactions, but on a single value of type
BaseValue
; - there is no actual memory pool that peers gossip to one another, instead there is one single source of
BaseValue
that themain.rs
program creates and sends over a crossbeam channel to all peers
- peers do not decide on (i.e., finalize) blocks of transactions, but on a single value of type
- transaction execution:
- the application at each peer does not execute the values that are decided; instead, each peer simply sends each decision via a
Sender
to themain.rs
program
- the application at each peer does not execute the values that are decided; instead, each peer simply sends each decision via a
- parallelism—there are three threads:
Knowledge of the Tendermint algorithm is not strictly necessary, but will help in the understanding of many details that follow. See this overview for a good refresher.
The central part of the simulator consists of an elementary busy loop. Recall that there is no actual networking code, so whenever we refer to a "network" component, this refers to a local channel.
The loop has two steps:
- Select the next message from the network; each message has a certain destination.
- Give control to the destination application and let it execute that message; the application will create further message(s) that it sends to the network.
Each message contains a special instruction, specifically it is an Input
.
Every input instructs the application to take a certain step towards reaching agreement and deciding on a value,
so that the other applications decide on the same value.
Prior to the busy loop above,
the simulator initializes each application with a specific input called Input::StarHeight
.
The diagram below depicts the busy loop.
The sequence diagram below depicts the beginning of an execution.
Some remarks on this sequence diagram follow.
During this method, the simulator simply iterates over all the existing peers and triggers
a method called init
on each.
In the diagram there are only two peers, namely 0
and 3
for the sake of simplicity, but in a real system
there would be minimum four peers.
While executing the init
method, each peer creates an input called Input::StartHeight
and they send it to
themselves.
This input will be consumed later.
Note
The steps on yellow background from the sequence diagram correspond with the steps in the enumerated list here.
The simulator calls recv
on the channel that represents the network,
and applies the busy loop we described above, specifically:
- the first message is called
Input::StartHeight
and has as destination peer 0; the simulator therefore selects this peer and callsapply_input
on the application logic corresponding to this peer with the given input. - peer 0 calls
process!
on the core malachite library, and then handles the effect which this call produced- specifically, the first effect is
Effect::StartRound
, which is does not lead to any specific action - peer 0, however, also encounters another effect, called
Effect::GetValue
- to handle this effect, peer 0 will need to provide another input to the library, specifically
Input::Propose
- this input is enqueued in the network for later use; the destination is "self," i.e., peer 0
- specifically, the first effect is
- the simulator then selects the next message, which is also
Input::StartHeight
, this time with destination peer 3- the same happens as with peer 0, except that peer 3 does not encounter any
Effect::GetValue
- this is because only one peer per round is a proposer; in this case, peer 0 was selected as proposer
- the same happens as with peer 0, except that peer 3 does not encounter any
- the simulator now handles the input which peer 0 produced at step 2.iv --
Input::Propose
- the simulator applies this input at peer 0
- the side effect of this input is that peer 0 is now expected to perform a
Effect::Publish
, which means that this peer is supposed to broadcast a message to the other peers - in this case, the message has type
Input::Proposal
- peer 0 sends the proposal message, selecting all the other peers in the network as destinations
- peer 0 applies
Input::Proposal
- the side effect is another
Effect::Publish
- this time, the message to be published is a vote, and this takes the form of
Input::Vote
- specifically, the vote is called a Prevote
- the side effect is another
- peer 0 applies
Input::Vote
- the side effect is
Effect::VerifySignature
- the malachite core library uses this effect to instruct the application at peer 0 to verify the signature of the vote that just arrived
- since we ignore signature verification, this operation is a no-op
- the side effect is
- peer 3 applies
Input::Vote
- similarly to peer 0, the side effect at peer 3 is
Effect::VerifySignature
- similarly to peer 0, the side effect at peer 3 is
- malachite v0.0.1