-
Notifications
You must be signed in to change notification settings - Fork 11
Update mechanism
Currently, the Daedalus and the Node (this is what I’m calling the node, thus the uppercase) communicate via JSON API once they have settled in on a port via which to communicate (see here).
First of all, we need to understand that the blocks in the blockchain contain the version of Daedalus (the frontend). We can say that Daedalus, also known as frontend is the Server, and that the Node, also known as backend is the Client, which are the same things under different names. We can imagine that each block can contain a version of the frontend, which is essentially a hash signature from the installer. That is something that can change in the future, but we can simplify our life by imagining that what the blockchain contains is the link for the installer (which, when simplified, it does).
Let’s start simple. Let’s take the blockchain and the version into consideration. First of all, we can consider what we have in production, since that is something we can base our assumptions on:
- there are 101(which is the number of epochs at the time of writing this) epochs in the blockchain
- there is 21600 slots in an epoch
- each slot may contains a block
- there could be 21600 blocks in an epoch, if all slots have a block
- each block may contain a frontend version
- when a hard fork occurs, the update system stops working and the client needs to download the new frontend manually, in our current versions we have that covered since the protocol version 1 and 2 will contain the information about the update
We can remove other details for now and simply focus on this simple scenario. The very simple representation can be seen on Figure 5.
We then consider how to describe such a system. Since we can observe computation/digital systems as state machines, we proceed to do so. We can imagine a simple system that only deals with the blocks and slots in a very simple manner.
We have the blockchain which is the collection of blocks (which then contain transactions, but we omit that since we are not really interested in them right now). The node which we run is a simple machine which reads those blocks into (local) memory so it can know at what state the whole blockchain is. Given that, we can simply imagine the node syncing blocks each ”tick” (a unit of time which we are not interested in, but serves as a snapshot of state of our system in some interesting moments).
We can observe such synchronization as seen here:
node state = 0
blockchain state = 233
⇒
node state = 6
blockchain state = 234
⇒
node state = 23
blockchain state = 234
In this case, we have a situation where the node synced 6 blocks after the first transition, and it synced further 17 blocks on the second transition. The blockchain state remains constants for the purpose of simplification, it would ordinarily increase.
The somewhat enriched structure requires of us to summon additional states into the process, since we do need some way of describing what happens to the machine itself with these new changes. Or, to put it simply, we need to add information about installer versions on blocks in order to check how it all fits together. We can simplify the idea by first imagining that we only have one update in the whole blockchain, and add additional installers as we further refine the idea. We can imagine that we have the installer on block 22, and that the installer is updated once we sync up to that point, as seen here:
node state = 0
blockchain state = 234
installer version block = 22
latest installer version = 0
⇒
node state = 6
blockchain state = 234
installer version block = 22
latest installer version = 0
⇒
node state = 23
blockchain state = 234
installer version block = 22
latest installer version = 22
As you can imagine, this fits very nicely into testing, for example state-machinequickcheck (or similar), using Haskell. We can advance such idea by increasing the number of updates in the blockchain and by asssigning different versions of the installers to each update.
A simple communication between the frontend and the blockchain (backend) can be described as seen on Figure 7. The specifics of how this works are a bit tricky. We use the cardano-launcher also known simply as Launcher is something we require so we can have control over the (Electron) Daedalus process and to be sure it shuts down correctly.
The installers are different on different platforms:
- on Windows we download and use the installer directly
- on Mac we use the pkg file, which we open using an external program
- on Linux we use a custom script called the update-runner, which we build using Nix
For now, we can abstract over that and say that each platform has it’s own specifics. Let’s take a look at some of the key functions we will use:
uniqueKeys ∈ ∀k(k ∈ Ordered).(k → v) → P k
blockchainContents ∈ Blockchain → (Epoch → [Slot])
blockchainEpochs ∈ Blockchain → P Epoch
blockchainEpochs = uniqueKeys ◦ blockchainContents
fetchUniqueUpdatesFromBlockchain ∈ Blockchain → P InstallerVersion
If we take a look at the typical state transition of such a system, we can easily imagine something like this.
- cardano-ledger
- cardano-node
- cardano-wallet wallet BE
- Daedalus wallet FE
- iohk-monitoring-framework
- ouroboros-network