Cardano SL Updater Implementation Overview

Implementation of the update system can be found in the Pos.Update family of modules. The general approach to implementation is the same as in other subsystems of CSL, such as Txp, Ssc and Delegation. The update system has the global state, stored in the database. The global state can be unambiguously derived from the information that is in the blockchain. The local state, sometimes referred to as “mempool”, is stored in the memory. The mempool is used for data transfer and inclusion of transferred data into blocks. The network protocol (built with standard Inv/Req/Data pattern) is described in Application-level document with the binary protocol described in Binary protocols document.

Currently, everything is ready to add hard fork functionality via software update and then perform a hard fork as described in research section; soft forks (or software updates) are fully implemented.

Fields Updatable with a Soft Fork

An UpdateProposal contains fields for changing some parameters used by Cardano SL (for instance, slot duration). Specifically, upBlockVersion is used to signify that a proposal performs such changes; if upBlockVersion is greater than the last used block version, the changes from upBlockVersionData will be applied.

upBlockVersionData has the type BlockVersionData.

Its fields are described below:

  • bvdScriptVersion – a script language version used to validate script transactions. If the proposal increases upBlockVersion, it must also increase bvdScriptVersion by 1 (and can’t leave it unchanged).
  • bvdSlotDuration – slot duration (in milliseconds).
  • bvdMaxBlockSize – block size limit (in bytes). A proposal can’t increase the block size limit more than twofold compared to the previous limit.
  • bvdMaxHeaderSize - block header size limit (in bytes).
  • bvdMaxTxSize – transaction size limit (in bytes, currently 4096), limits size of TxAux

The checks described above are made in verifyNextBVData.

In addition, there are some fields that are unused right now, but will be used in the future. Their meaning is briefly described below:

  • bvdMpcThd – eligibility threshold for MPC.
  • bvdHeavyDelThd – threshold for heavyweight delegation.
  • bvdUpdateVoteThd – portion of total stake necessary to vote for or against an update.
  • bvdUpdateProposalThd – portion of total stake such that block containing UpdateProposal must contain positive votes for this proposal from stakeholders owning at least this amount of stake.
  • bvdUpdateImplicit – number of slots after which an update is implicitly approved (unless it has more negative votes than positive).
  • bvdUpdateSoftforkThd – portion of total stake such that if total stake of issuers of blocks with some block version is bigger than this portion, this block version is adopted.

Mempool Structure

MemPool consists of votes and proposals. Apart from that MemState contains tip, slot and PollModifier corresponding to MemPool (and to current GState, i. e. to application of MemPool to GState). No matter whether a change in proposal state comes from the network/mempool, or from loading the blockchain, PollModifier represents modification of global state which will be made if one applies mempool.

Updating the Mempool

As nodes deserialize payloads of update system messages, they modify mempool as implemented here.

MemPool is updated in three cases:

  1. When a new proposal/vote is received. In this case, one of the process functions is called, which in turn calls verifyAndApplyUSPayload and then updates current PollModifier and MemPool.
  2. When a new slot starts. In this case some data in MemPool may become invalid. In fact, it happens only when epoch changes. That can happen because stable stake distribution changes and some votes may have not enough stake for inclusion. It’s done in the processNewSlot function.
  3. When GState is updated. It is called usNormalize. Some data may become invalid as a result of block(s) application or rollback. For instance, we have a proposal in memory, apply block with this proposal and it becomes invalid (because it’s already in block). We should drop such proposals. Or we have a vote for proposal from some block, then rollback of this block happens and vote is no longer valid. It is implemented by applying all local data to empty state, ignoring all data which is no longer valid.

Proposal and Votes Accumulation

To vote for a proposal, nodes should send their votes. Proposals and votes are stored in mempool (even if proposals don’t have enough votes for inclusion into blocks, this way votes can be collected automatically) or gathered from the blockchain in order to figure out which proposal is adopted.

Interaction With the Database

In order to verify update system data, we have to get this data from the global state (database). To provide such interface, a well-documented type class MonadPollRead is presented. This type class is used not only for DB interaction, but also to take mempool into account when the data received from the network are processed. It is important that its implementation relies on functions found in Pos.Update.DB module.

Core Types

Core types are mentioned in the Binary Protocols document. Those types reflect the concepts from the research section in a straightforward way. Please refer to the core types module for more information.

Update Proposal Approval

A very important part of implementation of the update mechanism is the part that works with genesis blocks for epochs. This logic resides in processGenesisBlock function. The terminology related to this process is explained below.


Suppose there is a block version X. And there are blocks with version X created in slots S (where S is a set of slots). If total relative stake of leaders of all slots in S is ≥ softforkResolutionThreshold (referred to as «threshold» in the code), then X becomes adopted. See the more detailed description in research overview.

Proposal states

Update proposal can be in one of the states described below.


It means that update proposal is contained in one of the blocks, but it doesn’t have 50% votes for/against it (here 50% means total stake of voters who are for/against proposal relative to total stake of all stakeholders in system) and implicit agreement rule hasn’t been triggered yet.


It means that proposal has more than 50% votes for it or it was added to block long ago (according to implicit agreement rule) and it has more positive votes than negative (comparison by stake of course).


A proposal is called rejected if that proposal has more than 50% votes against it or it was added to block long ago (according to implicit agreement rule) and it has more negative votes than positive (again, comparison by stake).


An approved proposal is called confirmed if at least k blocks ago proposal became Approved. At this point we can be sure that proposal won’t become rejected, because rollbacks with depth more than k aren’t possible.


A rejected proposal is called discarded, if at least k blocks ago that proposal became rejected. At this point we can be sure that proposal won’t be approved, because rollbacks with depth more than k aren’t possible.

Download New Version

In the Pos.Update.Download module, the following algorithms are implemented. Downloaded updates are applied using a tool called cardano-launcher.

Download Confirmed Update

To download a confirmed update, we extract the update hash from ConfirmedProposalState. It is extracted depending on whether or not we’re using an installer on given platform. If the update hash is extracted successfully, the “Download Update by Hash” algorithm is invoked to download and save the confirmed update.

Download Update by Hash

To download an update by hash, we loop through known update servers trying to download the update with given hash using httpLBS from HTTP. It’s simple: in the end, we will either have the update completely downloaded or server list exhausted and an error reported. URIs of the known update servers are defined using --update-server argument of the cardano-node executable.