Paul Clark
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee
  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Canonical ledger state Paul Clark, April 2025 ### Introduction Mithril is used to distribute data snapshots signed by nodes representing sufficient stake to make them trustworthy. The primary use of this is currently fast bootstrap of nodes to save having to download all blocks using the Ouroborous mini-protocols. Because Mithril collects multiple signatures for data produced by different nodes, all the signers must see exactly the same data, down to the individual byte representation level. Currently, Mithril can only rely on the exact representation of blocks and transactions, which are stored in exactly the same format as they appear on chain, and hence every node will produce the same data snapshot (the "immutable-db" files). These can be used to download the entire history of the chain without having to use the chain-sync and block-fetch protocols, but the receiver still has to replay all the transactions from the beginning to derive the current ledger state at the time of the snapshot, which allows it to continue to sync from there on. This can take between an hour and several days depending on the receiving client. ### Needed improvement It would be better if a bootstrapping node or other client could fetch a reliable snapshot of the current ledger state (the entire dynamic state of the chain) which would save it having to replay all the transactions. Such a snapshot should also be considerably smaller (the current immutable-db snapshot is around 55GB compressed). Mithril does also offer a ledger state snapshot in the downloaded package, but this cannot be signed by the decentralised set of signers because there is no standard ("canonical") format for how the state is represented which would guarantee that two nodes correctly synced to the same block will produce exactly the same output. Also, there isn't currently any agreement of *when* the snapshots will be produced - it is too much to expect a node to produce one for every block - so we can't guarantee a meaningful sample of snapshots at any particular block height. Although the Mithril service produces a snapshot and gets the signers to sign a *digest* of it, this is a centralised mechanism which goes against the philosophy of Cardano and requires the client to trust the service. ### Summary requirement So the requirement is a standardised file format (or formats) which deterministically and implementation-independently defines the byte-level representation of the ledger data snapshot, which contains all the data required to bootstrap a node from an arbitrary position in the chain, and a simple deterministic way to trigger the snapshot which works the same for all nodes, independently of their local clock. The file format should be reasonably compact (although note that Mithril compresses files in any case), defined by a strong schema notation and capable of encoding in a deterministic form which removes any variation in ordering of values, number encoding formats and so on. Ideally it would use standard tools and formats already used in Cardano. We propose deterministically encoded CBOR ([RFC8949 Sec 4.2](https://datatracker.ietf.org/doc/html/rfc8949#section-4.2), with the tighter profile defined in [dCBOR](https://datatracker.ietf.org/doc/draft-mcnally-deterministic-cbor/)) with CDDL schemas as the base format and tooling. ### Additional use cases Such a "canonical ledger format" also delivers more benefits. Firstly, it can be used as a format to represent the start and end states for state transition tests - both internally for development and potentially as part of a conformance test suite. Secondly, to improve confidence and safety where there are multiple node implementations, there is a proposal to include a ledger state hash in the block header (see CIP#xx) so that any divergence in state between implementations can immediately be detected. The canonical ledger state format can serve as the preimage for this hash. Thirdly, there is the potential to include elements of the ledger state in a Merkle tree, allowing compact proof that a particular element had a particular state at the time of the snapshot. Without specifying this mechanism in detail here, it requires that we can identify an element of the ledger state with a path string such as JSON Pointer [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901). In particular, where an element is likely to be referenced by ID (e.g. UTXO Tx:Index pair, or SPO ID), they should be stored as a map (JSON object) rather than an array of tuples. ### Snapshot contents The essential required data in a snapshot includes: * UTXOs * Stake delegation * Reward accounts * Protocol parameters * Accounting pots (reserves, deposits etc.) * SPO state * DRep state * Governance action general state * Governance action voting state * Header state (e.g. nonces) It could also be useful to produce derived data that is commonly useful: * Payment address balances * Stake address balances * Stake pool delegation distribution ### Timing and load How often should the snapshot be produced? At the limit it would clearly be unreasonable to expect a node to produce a full snapshot at every block. At the other end of the scale, it really needs to be done at least once per epoch to generate useful delegation and governance data. The tradeoff is between the cost of producing the snapshot and the time and relay node capacity needed for a client to catch up from an old snapshot. Since Mithril typically produces a snapshot every four hours, this would seem like a reasonable frequency to produce snapshots as well, which would not put too much load on the node - particularly as it can be written asynchronously, so long as there is an atomic snapshot taken in memory. The current node does this at every block. It is also worth noting that it makes no sense to snapshot the results of volatile blocks (that is those within security parameter k=2160 of the tip). If we did, and there was a rollback which unwound the block at which we took the snapshot, the snapshot would be invalid, and we have no way of signalling its invalidity to clients, even if we wanted to push this complexity onto them. We therefore need to snapshot at the earliest when a block enters the immutable chain. Happily, this is when the current node - and probably other implementations too - will store the block into their immutable data store. We therefore need a simple repeatable algorithm which triggers a snapshot at a deterministic cadence, independently of local clock, and which only captures immutable blocks. We propose the following: ```python snapshot_interval = 4 * 60 * 60 / 20 // =720: 4hr on average, 20s per block if (block_number % snapshot_interval) == 0 && tip_number - block_number >= 2160: trigger_snapshot() ``` The snapshot should represent the ledger state *after* processing the given block number. ### Modularity The snapshot decomposes into a number of logical areas, some of which are reasonably large (UTXO, stake and rewards) while others are tiny (protocol parameters, pots). It makes sense to keep each logical area in a separate file, which has a number of benefits: * Special-purpose snapshots can be created with only a subset * Clients that require only a subset need only read some of the files * It is naturally extensible for future additions * It is easier for a modular ledger implementation to produce ### Forwards and backwards compatibility If we want to be able to update and change the ledger state snapshot format in the future, we need to indicate the version somewhere. JSON (via CBOR) is naturally forwards- and backwards-compatible when using object properties with defaults, so an extension to the format need not necessarily require version-specific code. As mentioned above, if a whole new area is created, we can simply add a new file. However, since a change to the specification could result in a different binary encoding of the same data, we need a process to manage version changes to avoid the signers signing different versions. If the change happens as the result of a hard fork (the mostly likely case) then this happens naturally as SPOs transition between protocol numbers. If there was a requirement to change the snapshot format between hard forks, updated nodes would need to produce both the old and the new format for a time, until a stake majority was signing the new format. We therefore propose the files should be placed in a subdirectory tagged with the version number (e.g. `ledger-v1`) so that both can be produced in the same snapshot if required. ### Snapshot block information and metadata For node bootstrap, we need to know the block height, slot number and block hash (together the "point") *after which* the snapshot was taken, to pass to the chain-sync protocol for ongoing synchronisation. This is kept in a block_data file included in the snapshot. It might be tempting to place other data such as the originating node's type and version in here, but this is of course Kryptonite for the requirement for it to be the same across all nodes. We have however added an unsigned, information-only JSON file for this purpose. We use JSON rather than CBOR to make it more human readable and also to flag that it is not to be relied on since it is not signed. ## File definitions Within directory `ledger-v1`: **Basic:** `block-data.cbor` `utxos.cbor` `stake.cbor` `rewards.cbor` `parameters.cbor` `pots.cbor` `spos.cbor` `dreps.cbor` `governance.cbor` `votes.cbor` **Derived:** `spdd.cbor` `payment-addresses.cbor` `stake-addresses.cbor` **Unsigned:** `metadata.json` [Link to CDDLs of each file]

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully