├── .clang-format ├── .github ├── release-drafter.yml └── workflows │ ├── gh-pages.yml │ ├── markdown_link_checker.yml │ └── release-drafter.yml ├── .gitignore ├── LICENSE ├── README.md ├── book.toml ├── src ├── README.md ├── SUMMARY.md ├── rationale │ ├── distributing_rewards.md │ ├── fork_choice_das.md │ ├── message_block_layout.md │ ├── rationale.md │ └── rewards.md └── specs │ ├── architecture.md │ ├── block_proposer.md │ ├── consensus.md │ ├── data_structures.md │ ├── figures │ ├── block_data_structures.dot │ ├── block_data_structures.svg │ ├── data_root.svg │ ├── rs2d.svg │ ├── rs2d_extend.svg │ ├── rs2d_extending.svg │ ├── rs2d_originaldata_message.svg │ ├── rs2d_originaldata_reserved.svg │ ├── rs2d_quadrants.svg │ ├── rs2d_row.svg │ ├── share.dot │ └── share.svg │ ├── light_client.md │ ├── networking.md │ ├── node_types.md │ ├── proto │ ├── consensus.proto │ ├── types.proto │ └── wire.proto │ └── specification.md └── theme └── highlight.js /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | ... 4 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: "v$RESOLVED_VERSION" 2 | tag-template: "v$RESOLVED_VERSION" 3 | change-template: "- $TITLE (#$NUMBER) @$AUTHOR" 4 | change-title-escapes: '\<*_&' 5 | version-resolver: 6 | major: 7 | labels: 8 | - "release:major" 9 | minor: 10 | labels: 11 | - "release:minor" 12 | patch: 13 | labels: 14 | - "release:patch" 15 | default: patch 16 | template: | 17 | ## Changes 18 | 19 | $CHANGES 20 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v* 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup mdBook 17 | uses: peaceiris/actions-mdbook@v1 18 | with: 19 | mdbook-version: '0.4.8' 20 | 21 | - run: mdbook build . 22 | 23 | - name: Deploy latest 24 | uses: peaceiris/actions-gh-pages@v3 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | publish_dir: ./book 28 | destination_dir: latest 29 | if: github.ref == 'refs/heads/master' 30 | 31 | - name: Get tag 32 | id: branch_name 33 | run: | 34 | echo ::set-output name=BRANCH_NAME::${GITHUB_REF#refs/tags/} 35 | if: startsWith(github.ref, 'refs/tags') 36 | 37 | - name: Deploy tag 38 | uses: peaceiris/actions-gh-pages@v3 39 | with: 40 | github_token: ${{ secrets.GITHUB_TOKEN }} 41 | publish_dir: ./book 42 | destination_dir: ${{ steps.branch_name.outputs.BRANCH_NAME }} 43 | if: startsWith(github.ref, 'refs/tags') 44 | -------------------------------------------------------------------------------- /.github/workflows/markdown_link_checker.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v* 9 | pull_request: 10 | 11 | jobs: 12 | markdown-link-check: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: gaurav-nelson/github-action-markdown-link-check@1.0.12 17 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Celestia Specifications 2 | 3 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/YsnTPcSfWQ) 4 | 5 | ## Notice 6 | 7 | **THIS REPOSITORY IS NOT CURRENTLY ACTIVELY MAINTAINED, AND DOES NOT REFLECT THE CELESTIA PROTOCOL. THE REFERENCE IMPLEMENTATION OF THE CELESTIA PROTOCOL SHOULD BE CONSULTED AS THE COMPLETE SPECIFICATION.** 8 | 9 | 1. : Tendermint Core full node 10 | 1. : Celestia-specific logic, attached to Celestia Core node 11 | 1. : Celestia state machine (staking and fee payments) logic 12 | 13 | The following core ideas in this repository inform the implementation: 14 | 15 | 1. [Erasure coding](./src/specs/data_structures.md#erasure-coding) 16 | 2. [Namespace IDs](./src/specs/consensus.md#reserved-namespace-ids) 17 | 3. [MsgPayForData transaction](./src/specs/data_structures.md#signedtransactiondatamsgpayfordata) 18 | 4. [State transition](./src/specs/consensus.md#blockavailabledatatransactiondata) 19 | 20 | ## Building From Source 21 | 22 | To build book: 23 | 24 | ```sh 25 | mdbook build 26 | ``` 27 | 28 | To serve locally: 29 | 30 | ```sh 31 | mdbook serve 32 | ``` 33 | 34 | ## Contributing 35 | 36 | Markdown files must conform to [GitHub Flavored Markdown](https://github.github.com/gfm/). Markdown must be formatted with: 37 | 38 | - [markdownlint](https://github.com/DavidAnson/markdownlint) 39 | - [Markdown Table Prettifier](https://github.com/darkriszty/MarkdownTablePrettify-VSCodeExt) 40 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Celestia Labs"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Celestia Specifications" 7 | 8 | [output.html] 9 | mathjax-support = true 10 | git-repository-url = "https://github.com/celestiaorg/celestia-specs" 11 | 12 | [rust] 13 | edition = "2018" 14 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Celestia Specifications 2 | 3 | - [Specification](./specs/specification.md) 4 | - [Architecture](./specs/architecture.md) 5 | - [Data Structures](./specs/data_structures.md) 6 | - [Consensus](./specs/consensus.md) 7 | - [Block Proposer](./specs/block_proposer.md) 8 | - [Networking](./specs/networking.md) 9 | - [Light client](./specs/light_client.md) 10 | - [Node types](./specs/node_types.md) 11 | - [Rationale](./rationale/rationale.md) 12 | - [Block Rewards](./rationale/rewards.md) 13 | - [Distributing Rewards and Penalties](./rationale/distributing_rewards.md) 14 | - [Fork Choice Rule with Data Availability Sampling](./rationale/fork_choice_das.md) 15 | - [Message Layout](./rationale/message_block_layout.md) 16 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Celestia Specifications](./README.md) 4 | 5 | - [Specification](./specs/specification.md) 6 | - [Architecture](./specs/architecture.md) 7 | - [Data Structures](./specs/data_structures.md) 8 | - [Consensus](./specs/consensus.md) 9 | - [Block Proposer](./specs/block_proposer.md) 10 | - [Networking](./specs/networking.md) 11 | - [Light client](./specs/light_client.md) 12 | - [Node types](./specs/node_types.md) 13 | - [Rationale](./rationale/rationale.md) 14 | - [Block Rewards](./rationale/rewards.md) 15 | - [Distributing Rewards and Penalties](./rationale/distributing_rewards.md) 16 | - [Fork Choice Rule with Data Availability Sampling](./rationale/fork_choice_das.md) 17 | - [Message Layout](./rationale/message_block_layout.md) 18 | -------------------------------------------------------------------------------- /src/rationale/distributing_rewards.md: -------------------------------------------------------------------------------- 1 | # Rationale: Distributing Rewards and Penalties 2 | 3 | - [Preamble](#preamble) 4 | - [Distribution Scheme](#distribution-scheme) 5 | - [State-Efficient Implementation](#state-efficient-implementation) 6 | 7 | ## Preamble 8 | 9 | Due to the requirement that all incorrect state transitions on Celestia be provable with a [compact fraud proof](https://arxiv.org/abs/1809.09044) that is cheap enough to verify within a smart contract on a remote chain (e.g. Ethereum), computing how rewards and penalties are distributed must involve no iterations. To understand why, let us consider the following desiderata in a staking system: 10 | 11 | 1. In-protocol stake delegation: this makes it easier for users to participate in the consensus process, and reduces reliance on custodial staking services. 12 | 1. In-protocol enforcement of proper distribution of rewards and penalities to delegators: rewards and penalties collected by validators should be distributed to delegators trustlessly. 13 | 14 | Naively, rewards and penalties (henceforth referred to collectively as "rewards", since penalties are simply negative rewards) can be distributed immediately. For example, when a validator produces a new block and is entitled to collecting transaction fees, these fees can be distributed to every single account delegating stake to this validator. This requires iterating over potentially a huge number of state elements for a single state transition (i.e. transaction), which is computationally expensive. The specific problem is that it would be infeasible to prove that such a state transition was _incorrect_ (i.e. with a fraud proof) within the execution system of a remote blockchain (i.e. with a smart contract). 15 | 16 | This forms the primary motivation of the scheme discussed here: a mechanism for distributing rewards that is state-efficient and requires no iteration over state elements for any state transition. 17 | 18 | ## Distribution Scheme 19 | 20 | The scheme presented here is an incarnation of Cosmos' [F1 fee distribution scheme](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/fee_distribution/f1_fee_distr.pdf). F1 has the nice property of being approximation-free and, with proper implementation details, can be highly efficient with state usage and completely iteration-free in all cases. 21 | 22 | Naively, when considering a single block, the reward that should be given to a delegator with stake \\( x \\), who is delegating to a validator with total voting power \\( n \\), whose reward in that block is \\( T \\), is: 23 | 24 | $$ 25 | \text{naive reward} = x \frac{T}{n} 26 | $$ 27 | 28 | In other words, the voting power contributed by the delegator multiplied by the _reward rate_, i.e. the rewards per unit of voting power. We note that if the total voting power of a validator remains constant forever, then the above equation holds and is approximation-free. However, changes to the total voting power need to be accounted for. 29 | 30 | Blocks between two changes to a validator's voting power (i.e. whenever a user delegates or undelegates stake) are a _period_. Every time a validator's voting power changes (i.e. a new period \\( f \\) begins), an entry \\( Entry_f \\) for this period is saved in state, which records _the reward rate up to the beginning of_ \\( f \\). This is simply the sum of the reward rate up to the beginning of previous period \\( f-1 \\) and the reward rate of the period \\( f \\) itself: 31 | 32 | $$ 33 | Entry_f = \begin{cases} 34 | 0 & f = 0 \\\\ 35 | Entry_{f-1} + \frac{T_f}{n_f} & f > 0 \\\\ 36 | \end{cases} 37 | $$ 38 | 39 | Note that \\( Entry \\) is a monotonically increasing function. 40 | 41 | Finally, the raw reward for a delegation is simply proportional to the difference in entries between the period where undelegation ended (\\( f \\)) and where it began (\\( k \\)). 42 | 43 | $$ 44 | \text{reward} = x (Entry_f - Entry_k) 45 | $$ 46 | 47 | This raw reward can be scaled by additional factors, such as commissions or slashing penalties. 48 | 49 | ### State-Efficient Implementation 50 | 51 | The F1 paper does not specify where entries are stored in state, but the understanding is that they are placed in independent state elements. This has the downside of requiring multiple Merkle branches to prove the inclusion of entries for e.g. fraud proofs. We can improve on this by leveraging a specific property of entries, namely that each entry is only used in exactly two cases: 52 | 53 | 1. To compute the next entry. 54 | 1. To compute the reward of a delegator. 55 | 56 | Intuitively, after having being used twice, an entry can be pruned from the state. We can make use of this by storing only the latest entry with its respective validator object, and a copy of the two entries each delegation needs with the delegation object. By storing entries directly with the objects that require them, state transitions can be statelessly validated without extra inclusion proofs. 57 | -------------------------------------------------------------------------------- /src/rationale/fork_choice_das.md: -------------------------------------------------------------------------------- 1 | # Fork Choice Rule with Data Availability Sampling 2 | 3 | - [Preamble](#preamble) 4 | - [Invalid vs Unavailable](#invalid-vs-unavailable) 5 | - [Scenarios](#scenarios) 6 | 7 | ## Preamble 8 | 9 | Tendermint provides finality under an honest 2/3 of voting power assumption. It is one of several ["BFT" consensus protocols](https://arxiv.org/abs/1807.04938) (also known as "classical" consensus protocols). Under that assumption, new _valid_ blocks are immediately and forever final as soon as 2/3 of voting power commits to the block. Therefore, under that assumption, Tendermint is fork-free. 10 | 11 | Contemporary blockchains support full nodes (which are secure under no assumption on voting power honesty) and light nodes (which are secure under an honest majority of voting power assumption). LazyLedger is unique in [supporting light nodes with stronger security guarantees](../specs/node_types.md#node-type-definitions): 12 | 13 | 1. full nodes are secure under no assumptions on voting power honesty 14 | 1. light nodes (and partial nodes) are secure under [an honest minority of nodes and synchronous communication](https://arxiv.org/abs/1809.09044), and no assumptions on voting power honesty 15 | 1. superlight nodes are secure under an honest majority of voting assumption 16 | 17 | Note that _secure_ in this context is defined as "not accepting invalid blocks," and is orthogonal to _consensus safety_. The introduction of light nodes that do not depend on an honest majority assumption also introduces additional cases that must be analyzed. 18 | 19 | ## Invalid vs Unavailable 20 | 21 | Tendermint (and other consensus protocols) requires blocks to be _valid_, i.e. pass a [validity predicate](https://arxiv.org/abs/1807.04938) before they are accepted by an honest node. Note that both validity and invalidity are deterministic and monotonic, i.e. that once a block is valid or invalid, it will be valid or invalid for all future time. 22 | 23 | With [Data Availability Sampling](https://arxiv.org/abs/1809.09044) (DAS), there is a notion of _available_ and _unavailable_ blocks. Both are probabilistic rather than deterministic. Availability is assumed monotonic (i.e. once a block is available, it will remain available since The Internet Never Forgets), but unavailability is not. A block proposer may hide a block to make currently-online nodes see the block as unavailable, then reveal the entire (valid) block at a later time. 24 | 25 | ## Scenarios 26 | 27 | We consider two scenarios. 28 | 29 | **A dishonest majority hide a committed block, commit to a second block at the same height within the weak subjectivity window to fork the chain, then reveal the first block**. This is trivially equivocation and requires social consensus to resolve which fork to accept. The unavailability of the first block is orthogonal. Nodes that detect equivocation by a majority of voting power within the weak subjectivity window must halt regardless. 30 | 31 | **A dishonest majority hide a committed block, commit additional blocks on top of it, then reveal the first block within the weak subjectivity window**. There is no equivocation. Note that a node cannot distinguish a dishonest majority in this scenario from a transient network failure on their end and an honest majority. 32 | 33 | A requirement is that full nodes and light nodes agree on the same head of the chain automatically in this case, i.e. without human intervention. 34 | 35 | Light nodes follow consensus state (i.e. validator set changes and commits) and perform DAS. If a block is seen as unavailable but has a commit, DAS is performed on the block continuously until either DAS passes, or the weak subjectivity window is exceeded at which point the node halts. 36 | 37 | Full nodes fully download and execute blocks. If a block is seen as unavailable but has a commit, full downloading is re-attempted continuously until either it succeeds, or the weak subjectivity window is exceeded at which point the node halts. 38 | 39 | Under [an honest minority of nodes and synchronous communication](https://arxiv.org/abs/1809.09044) assumptions, passing DAS probabilistically guarantees the block can be fully downloaded. Therefore, the above protocol guarantees light nodes and full nodes will agree on the same head automatically without manual intervention, under a synchrony assumption equal to the unbonding window (formal proof pending). 40 | 41 | > Note: whether re-downloading/re-sampling is attempted at a fixed interval or on receipt of a new block header with a Tendermint commit is an implementation detail. 42 | 43 | > Note: users may wish to set a much shorter timeout than the unbonding window before they are notified of their node being unable to verify the availability of a committed block. This does not affect the properties analyzed above, but may result in more manual attention in the event of intermittent network disruptions. 44 | -------------------------------------------------------------------------------- /src/rationale/message_block_layout.md: -------------------------------------------------------------------------------- 1 | # Message Layout 2 | 3 | - [Preamble](#preamble) 4 | - [Message Layout Rationale](#message-layout-rationale) 5 | - [Non-Interactive Default Rules](#non-interactive-default-rules) 6 | - [Caveats](#caveats) 7 | 8 | ## Preamble 9 | 10 | Celestia uses [a data availability scheme](https://arxiv.org/abs/1809.09044) that allows nodes to determine whether a block's data was published without downloading the whole block. The core of this scheme is arranging data in a two-dimensional matrix then applying erasure coding to each row and column. This document describes the rationale for how data—transactions, messages, and other data—[is actually arranged](../specs/data_structures.md#arranging-available-data-into-shares). Familiarity with the [originally proposed data layout format](https://arxiv.org/abs/1809.09044) is assumed. 11 | 12 | ## Message Layout Rationale 13 | 14 | Block data consists of transactions (which modify the Celestia chain's state), intermediate state roots (required for fraud proofs of the aforementioned transactions), messages (binary blobs which do not modify the Celestia state, but which are intended for a Celestia application identified with a provided namespace ID), and other relevant pieces of data (e.g. evidence for slashing). We want to arrange this data into a `k * k` matrix of fixed-sized shares, which will later be committed to in [Namespace Merkle Trees (NMTs)](../specs/data_structures.md#namespace-merkle-tree). 15 | 16 | The simplest way we can imagine arranging block data is to simply serialize it all in no particular order, split it into fixed-sized shares, then arrange those shares into the `k * k` matrix in row-major order. However, this naive scheme can be improved in a number of ways, described below. 17 | 18 | First, we impose some ground rules: 19 | 20 | 1. Data must be ordered by namespace ID. This makes queries into a NMT commitment of that data more efficient. 21 | 1. Since non-message data are not naturally intended for particular namespaces, we assign reserved namespaces for them. A range of namespaces is reserved for this purpose, starting from the lowest possible namespace ID. 22 | 1. By construction, the above two rules mean that non-message data always precedes message data in the row-major matrix, even when considering single rows or columns. 23 | 1. Data with different namespaces must not be in the same share. This might cause a small amount of wasted block space, but makes the NMT easier to reason about in general since leaves are guaranteed to belong to a single namespace. 24 | 25 | Transactions can pay fees for a message to be included in the same block as the transaction itself. However, we do not want serialized transactions to include the entire message they pay for (which is the case in other blockchains with native execution, e.g. calldata in Ethereum transactions or OP_RETURN data in Bitcoin transactions), otherwise every node that validates the sanctity of the Celestia coin would need to download all message data. Transactions must therefore only include a commitment to (i.e. some hash of) the message they pay fees for. If implemented naively (e.g. with a simple hash of the message, or a simple binary Merkle tree root of the message), this can lead to a data availability problem, as there are no guarantees that the data behind these commitments is actually part of the block data. 26 | 27 | To that end, we impose some additional rules onto _messages only_: messages must be placed is a way such that both the transaction sender and the block producer can be held accountable—a necessary property for e.g. fee burning. Accountable in this context means that 28 | 29 | 1. The transaction sender must pay sufficient fees for message inclusion. 30 | 1. The block proposer cannot claim that a message was included when it was not (which implies that a transaction and the message it pays for must be included in the same block). 31 | 32 | Specifically, messages must begin at a new share, unlike non-message data which can span multiple shares. We note a nice property from this rule: if the transaction sender knows 1) `k`, the size of the matrix, 2) the starting location of their message in a row, and 3) the length of the message (they know this since they are sending the message), then they can actually compute a sequence of roots to _subtrees in the row NMTs_. More importantly, anyone can compute this, and can compute _the simple Merkle root of these subtree roots_. 33 | 34 | This, however, requires the block producer to interact with the transaction sender to provide them the starting location of their message. This can be done selectively, but is not ideal as a default for e.g. end-user wallets. 35 | 36 | ### Non-Interactive Default Rules 37 | 38 | As a non-consensus-critical default, we can impose some additional rules on message placement to make the possible starting locations of messages sufficiently predictable and constrained such that users can deterministically compute subtree roots without interaction: 39 | 40 | 1. Messages that span multiple rows must begin at the start of a row (this can occur if a message is longer than `k` shares _or_ if the block producer decides to start a message partway through a row and it cannot fit). 41 | 1. Messages begin at a location aligned with the largest power of 2 that is not larger than the message length or `k`. 42 | 43 | With the above constraints, we can compute subtree roots deterministically easily: simply slice off either the largest power of 2 that isn't larger than the remaining message length, or `k`, whichever is smaller. This is done recursively. As an example, with `k = 4` and message length of `11`, the message would be sliced with lengths `4, 4, 2, 1`. The resulting slices are the leaves of subtrees whose roots can be computed. Due to the rules above, the subtrees are guaranteed to be aligned to powers of 2, and thus the subtree roots will be present as internal nodes in the NMT of _some_ row(s). 44 | 45 | This is similar to [Merkle Mountain Ranges](https://www.usenix.org/legacy/event/sec09/tech/full_papers/crosby.pdf), though with the largest subtree bounded by `k` rather than being unbounded. 46 | 47 | The last piece of the puzzle is determining _which_ row the message is placed at (or, more specifically, the starting location). This is needed to keep the block producer accountable. To this end, the block producer simply augments each fee-paying transaction with some metadata: the starting location of the message the transaction pays for. 48 | 49 | ### Caveats 50 | 51 | The message placement rules described above conflict with the first rule that shares must be ordered by namespace ID, as shares between two messages that are not placed adjacent to each other do not have a natural namespace they belong to. This is resolved by requiring that such shares have a value of zero and a namespace ID equal to the preceding message's. Since their value is known, they can be omitted from NMT proofs of all shares of a given namespace ID. 52 | -------------------------------------------------------------------------------- /src/rationale/rationale.md: -------------------------------------------------------------------------------- 1 | # Rationale 2 | 3 | - [Block Rewards](./rewards.md) 4 | - [Distributing Rewards and Penalties](./distributing_rewards.md) 5 | - [Fork Choice Rule with Data Availability Sampling](./fork_choice_das.md) 6 | - [Message Layout](./message_block_layout.md) 7 | -------------------------------------------------------------------------------- /src/rationale/rewards.md: -------------------------------------------------------------------------------- 1 | # Block Rewards 2 | 3 | Block rewards scale with the inverse square root of the total validating stake. This gives us a nice property: as the total validating stake decreases, returns per validator increases. This encourages additional validators to join and makes the system as a whole more robust even in the presence of secondary uses of the staking coin, e.g. being used as collateral in Decentralized Finance protocols. 4 | 5 | Note that non-constant reward scaling opens up the system to [gatekeeping attacks](https://arxiv.org/abs/1811.00742), whereby validators are incentivized to prevent new validators from joining the validator set to keep their returns high. This should not be an issue in practice in the same way as [feather forks](https://bitcointalk.org/index.php?topic=312668.0) are not an issue in practice, but is nonetheless a theoretical issue that is noted here. 6 | 7 | The formula to calculate the reward per block uses the following symbols: 8 | 9 | | symbol | note | 10 | |-------------|-----------------------------------| 11 | | \\( R_B \\) | Rewards per block, in coins. | 12 | | \\( I_T \\) | Target annual issuance, in coins. | 13 | | \\( t_B \\) | Block time, in seconds. | 14 | | \\( t_Y \\) | Seconds per year. | 15 | | \\( S_0 \\) | Initial coin supply. | 16 | | \\( S_T \\) | Total staked coins. | 17 | 18 | Note that for the seconds per year we use a fixed `31,536,000`, omitting leap seconds for simplicity. 19 | 20 | The reward for a given block is thus only dependent on the validating stake, with remaining terms being constant: 21 | 22 | $$ 23 | R_B(S_T) = I_T \frac{t_B}{t_Y} \frac{\sqrt{S_T}}{\sqrt{S_0}} = \left( \frac{I_T t_B }{t_Y \sqrt{S_0}} \right) \sqrt{S_T} 24 | $$ 25 | 26 | If 100% of the initial supply of coins stake, then exactly \\( I_T \\) new coins will be issued per \\( \frac{t_Y}{t_B} \\) blocks. If fewer than 100% of the initial supply of coins stake, then fewer than \\( I_T \\) new coins will be issued, but the decrease will be square-root rather than linear. If greater than 100% of the initial supply of coins stake (e.g. after some time, additional coins will be in circulation due to previous issuance), then greater than \\( I_T \\) new coins will be issued, but again scaling sub-linearly. 27 | -------------------------------------------------------------------------------- /src/specs/architecture.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | - [Common Terms and Expressions](#common-terms-and-expressions) 4 | - [System Architecture](#system-architecture) 5 | 6 | ## Common Terms and Expressions 7 | 8 | | name | description | 9 | |-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 10 | | App (application) | Alternate name for "[virtual sidechain](https://arxiv.org/abs/1905.09274)." Celestia apps are sidechains that post all their data onto the Celestia chain to share security. | 11 | | Transaction | Request that modifies the consensus-critical state (validator balances and statuses). | 12 | | Message | Request that is executed by a non-consensus-critical app. | 13 | 14 | ## System Architecture 15 | 16 | Celestia has a minimal state: the validator set (account balances, validator status, etc.). Changes to the validator set are done with native _transactions_, distinct from the _messages_ processed by apps. Transactions are signed and must be processed by clients to determine the validator set, while messages are un-signed data blobs that will usually represent an app's block data. 17 | 18 | Transactions pay fees similarly to how they would in a normal blockchain (e.g. Bitcoin), and their side effects are restricted to modifying the validator set and their balances. Transactions can additionally pay fees for the inclusion of a message (identified by a hash) in the same block. The validator set is committed to in the block header, and since the entire system state _is_ the validator set, this is the only state commitment needed in the header. 19 | 20 | One desideratum that will most likely be included is [burning a non-proportional amount of coins for each transaction as a network fee](https://github.com/celestiaorg/celestia-specs/blob/066e14fca9de22555abc70dd4bcf4017fd0bfc64/rationale/fees.md). This provides baseline demand for the native coin: as the chain is used more, more coins must be bought then burned to pay for fees. 21 | 22 | This architecture has the benefit of allowing a spectrum of clients. Since different components are made available through commitments, clients that are only interested in a portion of the block data do not need to download and process the whole block. 23 | 24 | Non-consensus full clients have easy and direct access to all the data they need to validate: the transactions. Messages do not need to be validated, as they do not change the state, they simply need to be verified as available. 25 | 26 | Light clients are almost identical to full clients here: they simply need to process all the validator set changes (i.e. transactions) and run data availability checks on the rest of the block. Unlike full clients, light clients do not need to verify the signatures of transactions and can instead trust the majority of validators to sign off on validator set changes, with the addition of fraud proofs in case of an invalid signature. 27 | -------------------------------------------------------------------------------- /src/specs/block_proposer.md: -------------------------------------------------------------------------------- 1 | # Honest Block Proposer 2 | 3 | - [Deciding on a Block Size](#deciding-on-a-block-size) 4 | - [Laying out Transactions and Messages](#laying-out-transactions-and-messages) 5 | 6 | This document describes the tasks of an honest block proposer to assemble a new block. Performing these actions is not enforced by the [consensus rules](./consensus.md), so long as a valid block is produced. 7 | 8 | ## Deciding on a Block Size 9 | 10 | Before [arranging available data into shares](./data_structures.md#arranging-available-data-into-shares), the size of the original data's square must be determined. 11 | 12 | There are two restrictions on the original data's square size: 13 | 14 | 1. It must be at most [`AVAILABLE_DATA_ORIGINAL_SQUARE_MAX`](./consensus.md#constants). 15 | 1. It must be a power of 2. 16 | 17 | With these restrictions in mind, the block proposer performs the following actions: 18 | 19 | 1. Collect as many transactions and messages from the mempool as possible, such that the total number of shares is at most [`AVAILABLE_DATA_ORIGINAL_SQUARE_MAX`](./consensus.md#constants). 20 | 1. Compute the smallest square size that is a power of 2 that can fit the number of shares. 21 | 1. Attempt to [lay out the collected transactions and messages](#laying-out-transactions-and-messages-in-a-block) in the current square. 22 | 1. If the square is too small to fit all transactions and messages (which may happen [due to needing to insert padding between messages](../rationale/message_block_layout.md)) and the square size is smaller than [`AVAILABLE_DATA_ORIGINAL_SQUARE_MAX`](./consensus.md#constants), double the size of the square and repeat the above step. 23 | 24 | Note: the maximum padding shares between messages should be at most twice the number of message shares. Doubling the square size (i.e. quadrupling the number of shares in the square) should thus only have to happen at most once. 25 | 26 | ## Laying out Transactions and Messages 27 | -------------------------------------------------------------------------------- /src/specs/consensus.md: -------------------------------------------------------------------------------- 1 | # Consensus Rules 2 | 3 | - [System Parameters](#system-parameters) 4 | - [Units](#units) 5 | - [Constants](#constants) 6 | - [Reserved Namespace IDs](#reserved-namespace-ids) 7 | - [Reserved State Subtree IDs](#reserved-state-subtree-ids) 8 | - [Rewards and Penalties](#rewards-and-penalties) 9 | - [Leader Selection](#leader-selection) 10 | - [Fork Choice](#fork-choice) 11 | - [Block Validity](#block-validity) 12 | - [Block Structure](#block-structure) 13 | - [`block.header`](#blockheader) 14 | - [`block.availableDataHeader`](#blockavailabledataheader) 15 | - [`block.lastCommit`](#blocklastcommit) 16 | - [`block.availableData`](#blockavailabledata) 17 | - [State Transitions](#state-transitions) 18 | - [`block.availableData.evidenceData`](#blockavailabledataevidencedata) 19 | - [`block.availableData.transactionData`](#blockavailabledatatransactiondata) 20 | - [SignedTransactionDataTransfer](#signedtransactiondatatransfer) 21 | - [SignedTransactionDataMsgPayForData](#signedtransactiondatamsgpayfordata) 22 | - [SignedTransactionDataCreateValidator](#signedtransactiondatacreatevalidator) 23 | - [SignedTransactionDataBeginUnbondingValidator](#signedtransactiondatabeginunbondingvalidator) 24 | - [SignedTransactionDataUnbondValidator](#signedtransactiondataunbondvalidator) 25 | - [SignedTransactionDataCreateDelegation](#signedtransactiondatacreatedelegation) 26 | - [SignedTransactionDataBeginUnbondingDelegation](#signedtransactiondatabeginunbondingdelegation) 27 | - [SignedTransactionDataUnbondDelegation](#signedtransactiondataunbonddelegation) 28 | - [SignedTransactionDataBurn](#signedtransactiondataburn) 29 | - [SignedTransactionRedelegateCommission](#signedtransactionredelegatecommission) 30 | - [SignedTransactionRedelegateReward](#signedtransactionredelegatereward) 31 | - [Begin Block](#begin-block) 32 | - [End Block](#end-block) 33 | 34 | ## System Parameters 35 | 36 | ### Units 37 | 38 | | name | SI | value | description | 39 | |------|-------|---------|---------------------| 40 | | `1u` | `1u` | `10**0` | `1` unit. | 41 | | `2u` | `k1u` | `10**3` | `1000` units. | 42 | | `3u` | `M1u` | `10**6` | `1000000` units. | 43 | | `4u` | `G1u` | `10**9` | `1000000000` units. | 44 | 45 | ### Constants 46 | 47 | | name | type | value | unit | description | 48 | |-----------------------------------------|----------|--------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| 49 | | `AVAILABLE_DATA_ORIGINAL_SQUARE_MAX` | `uint64` | | `share` | Maximum number of rows/columns of the original data [shares](data_structures.md#share) in [square layout](data_structures.md#arranging-available-data-into-shares). | 50 | | `AVAILABLE_DATA_ORIGINAL_SQUARE_TARGET` | `uint64` | | `share` | Target number of rows/columns of the original data [shares](data_structures.md#share) in [square layout](data_structures.md#arranging-available-data-into-shares). | 51 | | `BLOCK_TIME` | `uint64` | | second | Block time, in seconds. | 52 | | `CHAIN_ID` | `string` | `"Celestia"` | | Chain ID. Each chain assigns itself a (unique) ID. | 53 | | `GENESIS_COIN_COUNT` | `uint64` | `10**8` | `4u` | `(= 100000000)` Number of coins at genesis. | 54 | | `MAX_GRAFFITI_BYTES` | `uint64` | `32` | `byte` | Maximum size of transaction graffiti, in bytes. | 55 | | `MAX_VALIDATORS` | `uint16` | `64` | | Maximum number of active validators. | 56 | | `NAMESPACE_ID_BYTES` | `uint64` | `8` | `byte` | Size of namespace ID, in bytes. | 57 | | `NAMESPACE_ID_MAX_RESERVED` | `uint64` | `255` | | Value of maximum reserved namespace ID (inclusive). 1 byte worth of IDs. | 58 | | `SHARE_RESERVED_BYTES` | `uint64` | `1` | `byte` | Bytes reserved at the beginning of each [share](data_structures.md#share). Must be sufficient to represent `SHARE_SIZE`. | 59 | | `SHARE_SIZE` | `uint64` | `256` | `byte` | Size of transaction and message [shares](data_structures.md#share), in bytes. | 60 | | `STATE_SUBTREE_RESERVED_BYTES` | `uint64` | `1` | `byte` | Number of bytes reserved to identify state subtrees. | 61 | | `UNBONDING_DURATION` | `uint32` | | `block` | Duration, in blocks, for unbonding a validator or delegation. | 62 | | `VERSION_APP` | `uint64` | `1` | | Version of the Celestia application. Breaking changes (hard forks) must update this parameter. | 63 | | `VERSION_BLOCK` | `uint64` | `1` | | Version of the Celestia chain. Breaking changes (hard forks) must update this parameter. | 64 | 65 | ### Reserved Namespace IDs 66 | 67 | | name | type | value | description | 68 | |-----------------------------------------|---------------|----------------------|--------------------------------------------------------------------------------------------| 69 | | `TRANSACTION_NAMESPACE_ID` | `NamespaceID` | `0x0000000000000001` | Transactions: requests that modify the state. | 70 | | `INTERMEDIATE_STATE_ROOT_NAMESPACE_ID` | `NamespaceID` | `0x0000000000000002` | Intermediate state roots, committed after every transaction. | 71 | | `EVIDENCE_NAMESPACE_ID` | `NamespaceID` | `0x0000000000000003` | Evidence: fraud proofs or other proof of slashable action. | 72 | | `TAIL_TRANSACTION_PADDING_NAMESPACE_ID` | `NamespaceID` | `0x00000000000000FF` | Tail padding for transactions: padding after all transactions but before messages. | 73 | | `TAIL_PADDING_NAMESPACE_ID` | `NamespaceID` | `0xFFFFFFFFFFFFFFFE` | Tail padding for messages: padding after all messages to fill up the original data square. | 74 | | `PARITY_SHARE_NAMESPACE_ID` | `NamespaceID` | `0xFFFFFFFFFFFFFFFF` | Parity shares: extended shares in the available data matrix. | 75 | 76 | ### Reserved State Subtree IDs 77 | 78 | | name | type | value | 79 | |----------------------------------|------------------|--------| 80 | | `ACCOUNTS_SUBTREE_ID` | `StateSubtreeID` | `0x01` | 81 | | `ACTIVE_VALIDATORS_SUBTREE_ID` | `StateSubtreeID` | `0x02` | 82 | | `INACTIVE_VALIDATORS_SUBTREE_ID` | `StateSubtreeID` | `0x03` | 83 | | `DELEGATIONS_SUBTREE_ID` | `StateSubtreeID` | `0x04` | 84 | | `MESSAGE_PAID_SUBTREE_ID` | `StateSubtreeID` | `0x05` | 85 | 86 | ### Rewards and Penalties 87 | 88 | | name | type | value | unit | description | 89 | |--------------------------|----------|-------------|--------|---------------------------------------------------------| 90 | | `SECONDS_PER_YEAR` | `uint64` | `31536000` | second | Seconds per year. Omit leap seconds. | 91 | | `TARGET_ANNUAL_ISSUANCE` | `uint64` | `2 * 10**6` | `4u` | `(= 2000000)` Target number of coins to issue per year. | 92 | 93 | ## Leader Selection 94 | 95 | TODO 96 | 97 | ## Fork Choice 98 | 99 | The Tendermint consensus protocol is fork-free by construction under an honest majority of stake assumption. 100 | 101 | If a block has a [valid commit](#blocklastcommit), it is part of the canonical chain. If equivocation [evidence](./data_structures.md#evidence) is detected for more than 1/3 of voting power, the node must halt. See [rationale doc](../rationale/fork_choice_das.md) for more information. 102 | 103 | ## Block Validity 104 | 105 | The validity of a newly-seen block, `block`, is determined by two components, detailed in subsequent sections: 106 | 107 | 1. [Block structure](#block-structure): whether the block header is valid, and data in a block is arranged into a valid and matching data root (i.e. syntax). 108 | 1. [State transition](#state-transitions): whether the application of transactions in the block produces a matching and valid state root (i.e. semantics). 109 | 110 | Pseudocode in this section is not in any specific language and should be interpreted as being in a neutral and sane language. 111 | 112 | ## Block Structure 113 | 114 | Before executing [state transitions](#state-transitions), the structure of the [block](./data_structures.md#block) must be verified. 115 | 116 | The following block fields are acquired from the network and parsed (i.e. [deserialized](./data_structures.md#serialization)). If they cannot be parsed, the block is ignored but is not explicitly considered invalid by consensus rules. Further implications of ignoring a block are found in the [networking spec](./networking.md). 117 | 118 | 1. [block.header](./data_structures.md#header) 119 | 1. [block.availableDataHeader](./data_structures.md#availabledataheader) 120 | 1. [block.lastCommit](./data_structures.md#commit) 121 | 122 | If the above fields are parsed successfully, the available data `block.availableData` is acquired in erasure-coded form as [a list of share rows](./networking.md#availabledata), then parsed. If it cannot be parsed, the block is ignored but not explicitly invalid, as above. 123 | 124 | ### `block.header` 125 | 126 | The [block header](./data_structures.md#header) `block.header` (`header` for short) is the first thing that is downloaded from the new block, and commits to everything inside the block in some way. For previous block `prev` (if `prev` is not known, then the block is ignored), and previous block header `prev.header`, the following checks must be `true`: 127 | 128 | `availableDataOriginalSquareSize` is computed as described [here](./data_structures.md#header). 129 | 130 | 1. `header.height` == `prev.header.height + 1`. 131 | 1. `header.timestamp` > `prev.header.timestamp`. 132 | 1. `header.lastHeaderHash` == the [header hash](./data_structures.md#header) of `prev`. 133 | 1. `header.lastCommitHash` == the [hash](./data_structures.md#hashing) of `lastCommit`. 134 | 1. `header.consensusHash` == the value computed [here](./data_structures.md#consensus-parameters). 135 | 1. `header.stateCommitment` == the root of the state, computed [with the application of all state transitions in this block](#state-transitions). 136 | 1. `availableDataOriginalSquareSize` <= [`AVAILABLE_DATA_ORIGINAL_SQUARE_MAX`](#constants). 137 | 1. `header.availableDataRoot` == the [Merkle root](./data_structures.md#binary-merkle-tree) of the tree with the row and column roots of `block.availableDataHeader` as leaves. 138 | 1. `header.proposerAddress` == the [leader](#leader-selection) for `header.height`. 139 | 140 | ### `block.availableDataHeader` 141 | 142 | The [available data header](./data_structures.md#availabledataheader) `block.availableDataHeader` (`availableDataHeader` for short) is then processed. This commits to the available data, which is only downloaded after the [consensus commit](#blocklastcommit) is processed. The following checks must be `true`: 143 | 144 | 1. Length of `availableDataHeader.rowRoots` == `availableDataOriginalSquareSize * 2`. 145 | 1. Length of `availableDataHeader.colRoots` == `availableDataOriginalSquareSize * 2`. 146 | 1. The length of each element in `availableDataHeader.rowRoots` and `availableDataHeader.colRoots` must be [`32`](./data_structures.md#hashing). 147 | 148 | ### `block.lastCommit` 149 | 150 | The last [commit](./data_structures.md#commit) `block.lastCommit` (`lastCommit` for short) is processed next. This is the Tendermint commit (i.e. polka of votes) _for the previous block_. For previous block `prev` and previous block header `prev.header`, the following checks must be `true`: 151 | 152 | 1. `lastCommit.height` == `prev.header.height`. 153 | 1. `lastCommit.round` >= `1`. 154 | 1. `lastCommit.headerHash` == the [header hash](./data_structures.md#header) of `prev`. 155 | 1. Length of `lastCommit.signatures` <= [`MAX_VALIDATORS`](#constants). 156 | 1. Each of `lastCommit.signatures` must be a valid [CommitSig](./data_structures.md#commitsig) 157 | 1. The sum of the votes for `prev` in `lastCommit` must be at least 2/3 (rounded up) of the voting power of `prev`'s next validator set. 158 | 159 | ### `block.availableData` 160 | 161 | The block's [available data](./data_structures.md#availabledata) (analogous to transactions in contemporary blockchain designs) `block.availableData` (`availableData` for short) is finally processed. The [list of share rows](./networking.md#availabledata) is parsed into the [actual data structures](./data_structures.md#availabledata) using the reverse of [the process to encode available data into shares](./data_structures.md#arranging-available-data-into-shares); if parsing fails here, the block is invalid. 162 | 163 | Once parsed, the following checks must be `true`: 164 | 165 | 1. The commitments of the [erasure-coded extended](./data_structures.md#2d-reed-solomon-encoding-scheme) `availableData` must match those in `header.availableDataHeader`. Implicitly, this means that both rows and columns must be ordered lexicographically by namespace ID since they are committed to in a [Namespace Merkle Tree](data_structures.md#namespace-merkle-tree). 166 | 1. Length of `availableData.intermediateStateRootData` == length of `availableData.transactionData` + length of `availableData.evidenceData` + 2. (Two additional state transitions are the [begin](#begin-block) and [end block](#end-block) implicit transitions.) 167 | 168 | ## State Transitions 169 | 170 | Once the basic structure of the block [has been validated](#block-structure), state transitions must be applied to compute the new state and state root. 171 | 172 | For this section, the variable `state` represents the [state tree](./data_structures.md#state), with `state.accounts[k]`, `state.inactiveValidatorSet[k]`, `state.activeValidatorSet[k]`, and `state.delegationSet[k]` being shorthand for the leaf in the state tree in the [accounts, inactive validator set, active validator set, and delegation set subtrees](./data_structures.md#state) with [pre-hashed key](./data_structures.md#state) `k`. E.g. `state.accounts[a]` is shorthand for `state[(ACCOUNTS_SUBTREE_ID << 8*(32-STATE_SUBTREE_RESERVED_BYTES)) | ((-1 >> 8*STATE_SUBTREE_RESERVED_BYTES) & hash(a))]`. 173 | 174 | State transitions are applied in the following order: 175 | 176 | 1. [Begin block](#begin-block). 177 | 1. [Evidence](#blockavailabledataevidencedata). 178 | 1. [Transactions](#blockavailabledatatransactiondata). 179 | 1. [End block](#end-block). 180 | 181 | ### `block.availableData.evidenceData` 182 | 183 | Evidence is the second set of state transitions that are applied, ahead of [transactions](#blockavailabledatatransactiondata). 184 | Each evidence represents proof of validator misbehavior, and causes a penalty against the validator(s). 185 | 186 | ### `block.availableData.transactionData` 187 | 188 | Once [evidence has been processed](#blockavailabledataevidencedata), transactions are applied to the state. Note that _transactions_ mutate the state (essentially, the validator set and minimal balances), while _messages_ do not. See [the architecture documentation](./architecture.md) for more info. 189 | 190 | `block.availableData.transactionData` is simply a list of [WrappedTransaction](./data_structures.md#wrappedtransaction)s. For each wrapped transaction in this list, `wrappedTransaction`, with index `i` (starting from `0`), the following checks must be `true`: 191 | 192 | 1. `wrappedTransaction.index` == `i`. 193 | 194 | For `wrappedTransaction`'s [transaction](./data_structures.md#transaction) `transaction`, the following checks must be `true`: 195 | 196 | 1. `transaction.signature` must be a [valid signature](./data_structures.md#public-key-cryptography) over `transaction.signedTransactionData`. 197 | 198 | Finally, each `wrappedTransaction` is processed depending on [its transaction type](./data_structures.md#signedtransactiondata). These are specified in the next subsections, where `tx` is short for `transaction.signedTransactionData`, and `sender` is the recovered signing [address](./data_structures.md#address). We will define a few helper functions: 199 | 200 | ```py 201 | tipCost(y, z) = y * z 202 | totalCost(x, y, z) = x + tipCost(y, z) 203 | ``` 204 | 205 | where `x` above is the amount of coins sent by the transaction authorizer, `y` above is the tip rate set in the transaction, and `z` above is the measure of the block space used by the transaction (i.e. size in bytes). 206 | 207 | Four additional helper functions are defined to manage the [validator queue](./data_structures.md#validator): 208 | 209 | 1. `findFromQueue(power)`, which returns the address of the last validator in the [validator queue](./data_structures.md#validator) with voting power greater than or equal to `power`, or `0` if the queue is empty or no validators in the queue have at least `power` voting power. 210 | 1. `parentFromQueue(address)`, which returns the address of the parent in the validator queue of the validator with address `address`, or `0` if `address` is not in the queue or is the head of the queue. 211 | 1. `validatorQueueInsert`, defined as 212 | 213 | ```py 214 | function validatorQueueInsert(validator) 215 | # Insert the new validator into the linked list 216 | parent = findFromQueue(validator.votingPower) 217 | if parent != 0 218 | if state.accounts[parent].status == AccountStatus.ValidatorBonded 219 | validator.next = state.activeValidatorSet[parent].next 220 | state.activeValidatorSet[parent].next = sender 221 | else 222 | validator.next = state.inactiveValidatorSet[parent].next 223 | state.inactiveValidatorSet[parent].next = sender 224 | else 225 | validator.next = state.validatorQueueHead 226 | state.validatorQueueHead = sender 227 | ``` 228 | 229 | 230 | 4. `validatorQueueRemove`, defined as 231 | 232 | ```py 233 | function validatorQueueRemove(validator, sender) 234 | # Remove existing validator from the linked list 235 | parent = parentFromQueue(sender) 236 | if parent != 0 237 | if state.accounts[parent].status == AccountStatus.ValidatorBonded 238 | state.activeValidatorSet[parent].next = validator.next 239 | validator.next = 0 240 | else 241 | state.inactiveValidatorSet[parent].next = validator.next 242 | validator.next = 0 243 | else 244 | state.validatorQueueHead = validator.next 245 | validator.next = 0 246 | ``` 247 | 248 | Note that light clients cannot perform a linear search through a linked list, and are instead provided logarithmic proofs (e.g. in the case of `parentFromQueue`, a proof to the parent is provided, which should have `address` as its next validator). 249 | 250 | In addition, three helper functions to manage the [message paid list](./data_structures.md#messagepaid): 251 | 252 | 1. `findFromMessagePaidList(start)`, which returns the transaction ID of the last transaction in the [message paid list](./data_structures.md#messagepaid) with `finish` greater than `start`, or `0` if the list is empty or no transactions in the list have at least `start` `finish`. 253 | 1. `parentFromMessagePaidList(txid)`, which returns the transaction ID of the parent in the message paid list of the transaction with ID `txid`, or `0` if `txid` is not in the list or is the head of the list. 254 | 1. `messagePaidListInsert`, defined as 255 | 256 | ```py 257 | function messagePaidListInsert(tx, txid) 258 | # Insert the new transaction into the linked list 259 | parent = findFromMessagePaidList(tx.messageStartIndex) 260 | state.messagesPaid[txid].start = tx.messageStartIndex 261 | numShares = ceil(tx.messageSize / SHARE_SIZE) 262 | state.messagesPaid[txid].finish = tx.messageStartIndex + numShares - 1 263 | if parent != 0 264 | state.messagesPaid[txid].next = state.messagesPaid[parent].next 265 | state.messagesPaid[parent].next = txid 266 | else 267 | state.messagesPaid[txid].next = state.messagePaidHead 268 | state.messagePaidHead = txid 269 | ``` 270 | 271 | We define a helper function to compute [F1 entries](../rationale/distributing_rewards.md): 272 | 273 | ```py 274 | function compute_new_entry(reward, power) 275 | if power == 0 276 | return 0 277 | return reward // power 278 | ``` 279 | 280 | After applying a transaction, the new state state root is computed. 281 | 282 | #### SignedTransactionDataTransfer 283 | 284 | ```py 285 | bytesPaid = len(tx) 286 | ``` 287 | 288 | The following checks must be `true`: 289 | 290 | 1. `tx.type` == [`TransactionType.Transfer`](./data_structures.md#signedtransactiondata). 291 | 1. `totalCost(tx.amount, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 292 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 293 | 294 | Apply the following to the state: 295 | 296 | ```py 297 | state.accounts[sender].nonce += 1 298 | 299 | state.accounts[sender].balance -= totalCost(tx.amount, tx.fee.tipRate, bytesPaid) 300 | state.accounts[tx.to].balance += tx.amount 301 | 302 | state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) 303 | ``` 304 | 305 | #### SignedTransactionDataMsgPayForData 306 | 307 | ```py 308 | bytesPaid = len(tx) + tx.messageSize 309 | currentStartFinish = state.messagesPaid[findFromMessagePaidList(tx.messageStartIndex)] 310 | parentStartFinish = state.messagesPaid[parentFromMessagePaidList(findFromMessagePaidList(tx.messageStartIndex))] 311 | ``` 312 | 313 | The following checks must be `true`: 314 | 315 | 1. `tx.type` == [`TransactionType.MsgPayForData`](./data_structures.md#signedtransactiondata). 316 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 317 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 318 | 1. The `ceil(tx.messageSize / SHARE_SIZE)` shares starting at index `tx.messageStartIndex` must: 319 | 1. Have namespace ID `tx.messageNamespaceID`. 320 | 1. `tx.messageShareCommitment` == computed as described [here](./data_structures.md#signedtransactiondatamsgpayfordata). 321 | 1. `parentStartFinish.finish` < `tx.messageStartIndex`. 322 | 1. `currentStartFinish.start` == `0` or `currentStartFinish.start` > `tx.messageStartIndex + ceil(tx.messageSize / SHARE_SIZE)`. 323 | 324 | Apply the following to the state: 325 | 326 | ```py 327 | state.accounts[sender].nonce += 1 328 | state.accounts[sender].balance -= totalCost(tx.amount, tx.fee.tipRate, bytesPaid) 329 | 330 | messagePaidListInsert(tx, id(tx)) 331 | 332 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 333 | ``` 334 | 335 | #### SignedTransactionDataCreateValidator 336 | 337 | ```py 338 | bytesPaid = len(tx) 339 | ``` 340 | 341 | The following checks must be `true`: 342 | 343 | 1. `tx.type` == [`TransactionType.CreateValidator`](./data_structures.md#signedtransactiondata). 344 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 345 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 346 | 1. `tx.commissionRate.denominator > 0`. 347 | 1. `tx.commissionRate.numerator <= tx.commissionRate.denominator`. 348 | 1. `state.accounts[sender].status` == `AccountStatus.None`. 349 | 350 | Apply the following to the state: 351 | 352 | ```py 353 | state.accounts[sender].nonce += 1 354 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 355 | state.accounts[sender].status = AccountStatus.ValidatorQueued 356 | 357 | validator = new Validator 358 | validator.commissionRate = tx.commissionRate 359 | validator.delegatedCount = 0 360 | validator.votingPower = 0 361 | validator.pendingRewards = 0 362 | validator.latestEntry = PeriodEntry(0) 363 | validator.unbondingHeight = 0 364 | validator.isSlashed = false 365 | 366 | validatorQueueInsert(validator) 367 | 368 | state.inactiveValidatorSet[sender] = validator 369 | 370 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 371 | ``` 372 | 373 | #### SignedTransactionDataBeginUnbondingValidator 374 | 375 | ```py 376 | bytesPaid = len(tx) 377 | ``` 378 | 379 | The following checks must be `true`: 380 | 381 | 1. `tx.type` == [`TransactionType.BeginUnbondingValidator`](./data_structures.md#signedtransactiondata). 382 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 383 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 384 | 1. `state.accounts[sender].status` == `AccountStatus.ValidatorQueued` or `state.accounts[sender].status` == `AccountStatus.ValidatorBonded`. 385 | 386 | Apply the following to the state: 387 | 388 | ```py 389 | state.accounts[sender].nonce += 1 390 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 391 | state.accounts[sender].status = ValidatorStatus.Unbonding 392 | 393 | if state.accounts[sender].status == AccountStatus.ValidatorQueued 394 | validator = state.inactiveValidatorSet[sender] 395 | else if state.accounts[sender].status == AccountStatus.ValidatorBonded 396 | validator = state.activeValidatorSet[sender] 397 | delete state.activeValidatorSet[sender] 398 | 399 | validator.unbondingHeight = block.height + 1 400 | validator.latestEntry += compute_new_entry(validator.pendingRewards, validator.votingPower) 401 | validator.pendingRewards = 0 402 | 403 | validatorQueueRemove(validator, sender) 404 | 405 | state.inactiveValidatorSet[sender] = validator 406 | 407 | state.activeValidatorSet.activeVotingPower -= validator.votingPower 408 | 409 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 410 | ``` 411 | 412 | #### SignedTransactionDataUnbondValidator 413 | 414 | ```py 415 | bytesPaid = len(tx) 416 | ``` 417 | 418 | The following checks must be `true`: 419 | 420 | 1. `tx.type` == [`TransactionType.UnbondValidator`](./data_structures.md#signedtransactiondata). 421 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 422 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 423 | 1. `state.accounts[sender].status` == `AccountStatus.ValidatorUnbonding`. 424 | 1. `state.inactiveValidatorSet[sender].unbondingHeight + UNBONDING_DURATION` < `block.height`. 425 | 426 | Apply the following to the state: 427 | 428 | ```py 429 | validator = state.inactiveValidatorSet[sender] 430 | 431 | state.accounts[sender].nonce += 1 432 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 433 | state.accounts[sender].status = AccountStatus.ValidatorUnbonded 434 | 435 | state.accounts[sender].balance += validator.commissionRewards 436 | 437 | state.inactiveValidatorSet[sender] = validator 438 | 439 | if validator.delegatedCount == 0 440 | state.accounts[sender].status = AccountStatus.None 441 | delete state.inactiveValidatorSet[sender] 442 | 443 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 444 | ``` 445 | 446 | #### SignedTransactionDataCreateDelegation 447 | 448 | ```py 449 | bytesPaid = len(tx) 450 | ``` 451 | 452 | The following checks must be `true`: 453 | 454 | 1. `tx.type` == [`TransactionType.CreateDelegation`](./data_structures.md#signedtransactiondata). 455 | 1. `totalCost(tx.amount, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 456 | 1. `state.accounts[tx.to].status` == `AccountStatus.ValidatorQueued` or `state.accounts[tx.to].status` == `AccountStatus.ValidatorBonded`. 457 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 458 | 1. `state.accounts[sender].status` == `AccountStatus.None`. 459 | 460 | Apply the following to the state: 461 | 462 | ```py 463 | state.accounts[sender].nonce += 1 464 | state.accounts[sender].balance -= totalCost(tx.amount, tx.fee.tipRate, bytesPaid) 465 | state.accounts[sender].status = AccountStatus.DelegationBonded 466 | 467 | if state.accounts[tx.to].status == AccountStatus.ValidatorQueued 468 | validator = state.inactiveValidatorSet[tx.to] 469 | else if state.accounts[tx.to].status == AccountStatus.ValidatorBonded 470 | validator = state.activeValidatorSet[tx.to] 471 | 472 | delegation = new Delegation 473 | delegation.status = DelegationStatus.Bonded 474 | delegation.validator = tx.to 475 | delegation.stakedBalance = tx.amount 476 | delegation.beginEntry = validator.latestEntry 477 | delegation.endEntry = PeriodEntry(0) 478 | delegation.unbondingHeight = 0 479 | 480 | validator.latestEntry += compute_new_entry(validator.pendingRewards, validator.votingPower) 481 | validator.pendingRewards = 0 482 | validator.delegatedCount += 1 483 | validator.votingPower += tx.amount 484 | 485 | # Update the validator in the linked list by first removing then inserting 486 | validatorQueueRemove(validator, delegation.validator) 487 | validatorQueueInsert(validator) 488 | 489 | state.delegationSet[sender] = delegation 490 | 491 | if state.accounts[tx.to].status == AccountStatus.ValidatorQueued 492 | state.inactiveValidatorSet[tx.to] = validator 493 | else if state.accounts[tx.to].status == AccountStatus.ValidatorBonded 494 | state.activeValidatorSet[tx.to] = validator 495 | state.activeValidatorSet.activeVotingPower += tx.amount 496 | 497 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 498 | ``` 499 | 500 | #### SignedTransactionDataBeginUnbondingDelegation 501 | 502 | ```py 503 | bytesPaid = len(tx) 504 | ``` 505 | 506 | The following checks must be `true`: 507 | 508 | 1. `tx.type` == [`TransactionType.BeginUnbondingDelegation`](./data_structures.md#signedtransactiondata). 509 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 510 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 511 | 1. `state.accounts[sender].status` == `AccountStatus.DelegationBonded`. 512 | 513 | Apply the following to the state: 514 | 515 | ```py 516 | state.accounts[sender].nonce += 1 517 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 518 | state.accounts[sender].status = AccountStatus.DelegationUnbonding 519 | 520 | delegation = state.delegationSet[sender] 521 | 522 | if state.accounts[delegation.validator].status == AccountStatus.ValidatorQueued || 523 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonding || 524 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonded 525 | validator = state.inactiveValidatorSet[delegation.validator] 526 | else if state.accounts[delegation.validator].status == AccountStatus.ValidatorBonded 527 | validator = state.activeValidatorSet[delegation.validator] 528 | 529 | delegation.status = DelegationStatus.Unbonding 530 | delegation.endEntry = validator.latestEntry 531 | delegation.unbondingHeight = block.height + 1 532 | 533 | validator.latestEntry += compute_new_entry(validator.pendingRewards, validator.votingPower) 534 | validator.pendingRewards = 0 535 | validator.delegatedCount -= 1 536 | validator.votingPower -= delegation.stakedBalance 537 | 538 | # Update the validator in the linked list by first removing then inserting 539 | # Only do this if the validator is actually in the queue (i.e. bonded or queued) 540 | if state.accounts[delegation.validator].status == AccountStatus.ValidatorBonded || 541 | state.accounts[delegation.validator].status == AccountStatus.ValidatorQueued 542 | validatorQueueRemove(validator, delegation.validator) 543 | validatorQueueInsert(validator) 544 | 545 | state.delegationSet[sender] = delegation 546 | 547 | if state.accounts[delegation.validator].status == AccountStatus.ValidatorQueued || 548 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonding || 549 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonded 550 | state.inactiveValidatorSet[delegation.validator] = validator 551 | else if state.accounts[delegation.validator].status == AccountStatus.ValidatorBonded 552 | state.activeValidatorSet[delegation.validator] = validator 553 | state.activeValidatorSet.activeVotingPower -= delegation.stakedBalance 554 | 555 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 556 | ``` 557 | 558 | #### SignedTransactionDataUnbondDelegation 559 | 560 | ```py 561 | bytesPaid = len(tx) 562 | ``` 563 | 564 | The following checks must be `true`: 565 | 566 | 1. `tx.type` == [`TransactionType.UnbondDelegation`](./data_structures.md#signedtransactiondata). 567 | 1. `totalCost(0, bytesPaid)` <= `state.accounts[sender].balance`. 568 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 569 | 1. `state.accounts[sender].status` == `AccountStatus.DelegationUnbonding`. 570 | 1. `state.delegationSet[sender].unbondingHeight + UNBONDING_DURATION` < `block.height`. 571 | 572 | Apply the following to the state: 573 | 574 | ```py 575 | delegation = state.accounts[sender].delegationInfo 576 | 577 | state.accounts[sender].nonce += 1 578 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 579 | state.accounts[sender].status = None 580 | 581 | # Return the delegated stake 582 | state.accounts[sender].balance += delegation.stakedBalance 583 | # Also disperse rewards (commission has already been levied) 584 | state.accounts[sender].balance += delegation.stakedBalance * (delegation.endEntry - delegation.beginEntry) 585 | 586 | if state.accounts[delegation.validator].status == AccountStatus.ValidatorQueued || 587 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonding 588 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonded 589 | validator = state.inactiveValidatorSet[delegation.validator] 590 | else if state.accounts[delegation.validator].status == AccountStatus.ValidatorBonded 591 | validator = state.activeValidatorSet[delegation.validator] 592 | 593 | if validator.delegatedCount == 0 && 594 | state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonded 595 | state.accounts[delegation.validator].status = AccountStatus.None 596 | delete state.inactiveValidatorSet[delegation.validator] 597 | 598 | delete state.accounts[sender].delegationInfo 599 | 600 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 601 | ``` 602 | 603 | #### SignedTransactionDataBurn 604 | 605 | ```py 606 | bytesPaid = len(tx) 607 | ``` 608 | 609 | The following checks must be `true`: 610 | 611 | 1. `tx.type` == [`TransactionType.Burn`](./data_structures.md#signedtransactiondata). 612 | 1. `totalCost(tx.amount, bytesPaid)` <= `state.accounts[sender].balance`. 613 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 614 | 615 | Apply the following to the state: 616 | 617 | ```py 618 | state.accounts[sender].nonce += 1 619 | state.accounts[sender].balance -= totalCost(tx.amount, tx.fee.tipRate, bytesPaid) 620 | 621 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 622 | ``` 623 | 624 | #### SignedTransactionRedelegateCommission 625 | 626 | ```py 627 | bytesPaid = len(tx) 628 | ``` 629 | 630 | The following checks must be `true`: 631 | 632 | 1. `tx.type` == [`TransactionType.RedelegateCommission`](./data_structures.md#signedtransactiondata). 633 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 634 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 635 | 1. `state.accounts[tx.to].status` == `AccountStatus.DelegationBonded`. 636 | 1. `state.accounts[sender].status` == `AccountStatus.ValidatorBonded`. 637 | 638 | Apply the following to the state: 639 | 640 | ```py 641 | state.accounts[sender].nonce += 1 642 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 643 | 644 | delegation = state.delegationSet[tx.to] 645 | validator = state.activeValidatorSet[delegation.validator] 646 | 647 | # Force-redelegate pending rewards for delegation 648 | pendingRewards = delegation.stakedBalance * (validator.latestEntry - delegation.beginEntry) 649 | delegation.stakedBalance += pendingRewards 650 | delegation.beginEntry = validator.latestEntry 651 | 652 | validator.latestEntry += compute_new_entry(validator.pendingRewards, validator.votingPower) 653 | validator.pendingRewards = 0 654 | 655 | # Assign pending commission rewards to delegation 656 | commissionRewards = validator.commissionRewards 657 | delegation.stakedBalance += commissionRewards 658 | validator.commissionRewards = 0 659 | 660 | # Update voting power 661 | validator.votingPower += pendingRewards + commissionRewards 662 | state.activeValidatorSet.activeVotingPower += pendingRewards + commissionRewards 663 | 664 | state.delegationSet[tx.to] = delegation 665 | state.activeValidatorSet[delegation.validator] = validator 666 | 667 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 668 | ``` 669 | 670 | #### SignedTransactionRedelegateReward 671 | 672 | ```py 673 | bytesPaid = len(tx) 674 | ``` 675 | 676 | The following checks must be `true`: 677 | 678 | 1. `tx.type` == [`TransactionType.RedelegateReward`](./data_structures.md#signedtransactiondata). 679 | 1. `totalCost(0, tx.fee.tipRate, bytesPaid)` <= `state.accounts[sender].balance`. 680 | 1. `tx.nonce` == `state.accounts[sender].nonce + 1`. 681 | 1. `state.accounts[sender].status` == `AccountStatus.DelegationBonded`. 682 | 1. `state.accounts[state.delegationSet[sender].validator].status` == `AccountStatus.ValidatorBonded`. 683 | 684 | Apply the following to the state: 685 | 686 | ```py 687 | state.accounts[sender].nonce += 1 688 | state.accounts[sender].balance -= totalCost(0, tx.fee.tipRate, bytesPaid) 689 | 690 | delegation = state.delegationSet[sender] 691 | validator = state.activeValidatorSet[delegation.validator] 692 | 693 | # Redelegate pending rewards for delegation 694 | pendingRewards = delegation.stakedBalance * (validator.latestEntry - delegation.beginEntry) 695 | delegation.stakedBalance += pendingRewards 696 | delegation.beginEntry = validator.latestEntry 697 | 698 | validator.latestEntry += compute_new_entry(validator.pendingRewards, validator.votingPower) 699 | validator.pendingRewards = 0 700 | 701 | # Update voting power 702 | validator.votingPower += pendingRewards 703 | state.activeValidatorSet.activeVotingPower += pendingRewards 704 | 705 | state.delegationSet[sender] = delegation 706 | state.activeValidatorSet[delegation.validator] = validator 707 | 708 | state.activeValidatorSet.proposerBlockReward += tipCost(tx.fee.tipRate, bytesPaid) 709 | ``` 710 | 711 | #### Begin Block 712 | 713 | At the beginning of the block, rewards are distributed to the block proposer. 714 | 715 | Apply the following to the state: 716 | 717 | ```py 718 | proposer = state.activeValidatorSet[block.header.proposerAddress] 719 | 720 | # Compute block subsidy and save to state for use in end block. 721 | rewardFactor = (TARGET_ANNUAL_ISSUANCE * BLOCK_TIME) / (SECONDS_PER_YEAR * sqrt(GENESIS_COIN_COUNT)) 722 | blockReward = rewardFactor * sqrt(state.activeValidatorSet.activeVotingPower) 723 | state.activeValidatorSet.proposerBlockReward = blockReward 724 | 725 | # Save proposer's initial voting power to state for use in end block. 726 | state.activeValidatorSet.proposerInitialVotingPower = proposer.votingPower 727 | 728 | state.activeValidatorSet[block.header.proposerAddress] = proposer 729 | ``` 730 | 731 | #### End Block 732 | 733 | Apply the following to the state: 734 | 735 | ```py 736 | account = state.accounts[block.header.proposerAddress] 737 | 738 | if account.status == AccountStatus.ValidatorUnbonding 739 | account.status == AccountStatus.ValidatorUnbonded 740 | proposer = state.inactiveValidatorSet[block.header.proposerAddress] 741 | else if account.status == AccountStatus.ValidatorBonded 742 | proposer = state.activeValidatorSet[block.header.proposerAddress] 743 | 744 | # Flush the outstanding pending rewards. 745 | proposer.latestEntry += compute_new_entry(proposer.pendingRewards, proposer.votingPower) 746 | proposer.pendingRewards = 0 747 | 748 | blockReward = state.activeValidatorSet.proposerBlockReward 749 | commissionReward = proposer.commissionRate.numerator * blockReward // proposer.commissionRate.denominator 750 | proposer.commissionRewards += commissionReward 751 | proposer.pendingRewards += blockReward - commissionReward 752 | 753 | # Even though the voting power hasn't changed yet, we consider this a period change. 754 | proposer.latestEntry += compute_new_entry(proposer.pendingRewards, state.activeValidatorSet.proposerInitialVotingPower) 755 | proposer.pendingRewards = 0 756 | 757 | if account.status == AccountStatus.ValidatorUnbonding 758 | account.status == AccountStatus.ValidatorUnbonded 759 | state.inactiveValidatorSet[block.header.proposerAddress] = proposer 760 | else if account.status == AccountStatus.ValidatorBonded 761 | state.activeValidatorSet[block.header.proposerAddress] = proposer 762 | ``` 763 | 764 | At the end of a block, the top `MAX_VALIDATORS` validators by voting power with voting power _greater than_ zero are or become active (bonded). For newly-bonded validators, the entire validator object is moved to the active validators subtree and their status is changed to bonded. For previously-bonded validators that are no longer in the top `MAX_VALIDATORS` validators begin unbonding. 765 | 766 | Bonding validators is simply setting their status to `AccountStatus.ValidatorBonded`. The logic for validator unbonding is found [here](#signedtransactiondatabeginunbondingvalidator), minus transaction sender updates (nonce, balance, and fee). 767 | 768 | Finally, the state subtree with ID [`MESSAGE_PAID_SUBTREE_ID`](#reserved-state-subtree-ids) is deleted. 769 | 770 | This end block implicit state transition is a single state transition, and [only has a single intermediate state root](#blockavailabledata) associated with it. 771 | -------------------------------------------------------------------------------- /src/specs/figures/block_data_structures.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | rankdir = "LR"; 3 | node [shape = record]; 4 | 5 | subgraph cluster_block { 6 | label = "block"; 7 | 8 | subgraph cluster_availableheader { 9 | label = "availableDataHeader"; 10 | struct4 [label = "{ | { rowRoots | colRoots } }"]; 11 | } 12 | 13 | subgraph cluster_body { 14 | label = "availableData"; 15 | struct3 [label = "{ | { transactionData | intermediateStateRoots | evidenceData | messageData } }"]; 16 | } 17 | 18 | subgraph cluster_lastcommit { 19 | label = "lastCommit"; 20 | struct2 [label = "{lastCommit}"]; 21 | } 22 | 23 | subgraph cluster_header { 24 | label = "header"; 25 | struct1 [label = "version | chainID | height | timestamp | lastHeaderHash | lastCommitHash | consensusHash | stateCommitment | availableDataOriginalSharesUsed | availableDataRoot | proposerAddress"]; 26 | } 27 | } 28 | 29 | struct1:f5 -> struct2; 30 | struct1:f9 -> struct4 [label = "Merkle root of"]; 31 | struct4:f0 -> struct3 [label = "NMT roots to\nerasure-coded data"]; 32 | 33 | edge [style = invis]; 34 | struct1 -> struct3; 35 | struct1 -> struct4; 36 | } -------------------------------------------------------------------------------- /src/specs/figures/block_data_structures.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | G 11 | 12 | 13 | cluster_block 14 | 15 | block 16 | 17 | 18 | cluster_availableheader 19 | 20 | availableDataHeader 21 | 22 | 23 | cluster_body 24 | 25 | availableData 26 | 27 | 28 | cluster_lastcommit 29 | 30 | lastCommit 31 | 32 | 33 | cluster_header 34 | 35 | header 36 | 37 | 38 | 39 | struct4 40 | 41 | 42 | 43 | rowRoots 44 | 45 | colRoots 46 | 47 | 48 | 49 | struct3 50 | 51 | 52 | 53 | transactionData 54 | 55 | intermediateStateRoots 56 | 57 | evidenceData 58 | 59 | messageData 60 | 61 | 62 | 63 | struct4:f0->struct3 64 | 65 | 66 | NMT roots to 67 | erasure-coded data 68 | 69 | 70 | 71 | struct2 72 | 73 | lastCommit 74 | 75 | 76 | 77 | struct1 78 | 79 | version 80 | 81 | chainID 82 | 83 | height 84 | 85 | timestamp 86 | 87 | lastHeaderHash 88 | 89 | lastCommitHash 90 | 91 | consensusHash 92 | 93 | stateCommitment 94 | 95 | availableDataOriginalSharesUsed 96 | 97 | availableDataRoot 98 | 99 | proposerAddress 100 | 101 | 102 | 103 | struct1:f9->struct4 104 | 105 | 106 | Merkle root of 107 | 108 | 109 | 110 | 111 | 112 | struct1:f5->struct2 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/specs/figures/data_root.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | row roots 4 | 5 | 6 | col roots 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | data root 39 | 40 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | r1 76 | rk 77 | r2k 78 | 79 | c1 80 | ck 81 | c2k 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | row roots 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | col roots 102 | 103 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d_extend.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | r1 82 | rk 83 | r2k 84 | 85 | c1 86 | ck 87 | c2k 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d_extending.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | r1 76 | rk 77 | r2k 78 | 79 | c1 80 | ck 81 | c2k 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | original data 113 | parity data 114 | parity data 115 | parity data 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d_originaldata_message.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | r1 22 | rk 23 | 24 | c1 25 | ck 26 | 27 | TXs 28 | ISRs 29 | evi. 30 | 31 | msg1 32 | msg1 33 | msg2 34 | 35 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d_originaldata_reserved.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | r1 22 | rk 23 | 24 | c1 25 | ck 26 | 27 | TXs 28 | ISRs 29 | evi. 30 | 31 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d_quadrants.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | r1 76 | rk 77 | r2k 78 | 79 | c1 80 | ck 81 | c2k 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | row roots 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | col roots 102 | 103 | 104 | 105 | 106 | 107 | 108 | Q0 109 | Q1 110 | Q2 111 | Q3 112 | 113 | -------------------------------------------------------------------------------- /src/specs/figures/rs2d_row.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | r1 76 | rk 77 | r2k 78 | 79 | c1 80 | ck 81 | c2k 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/specs/figures/share.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [shape = record, penwidth = 0]; 3 | 4 | share [label=< 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
08980200256
NID*end of tx2len(tx3)tx3zero-padding
24 | >]; 25 | } -------------------------------------------------------------------------------- /src/specs/figures/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | G 11 | 12 | 13 | 14 | share 15 | 16 | 0 17 | 8 18 | 9 19 | 80 20 | 200 21 | 256 22 | 23 | NID 24 | 25 | * 26 | 27 | end of tx2 28 | 29 | len(tx3) 30 | 31 | tx3 32 | 33 | zero-padding 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/specs/light_client.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celestiaorg/celestia-specs/e59efd63a2165866584833e91e1cb8a6ed8c8203/src/specs/light_client.md -------------------------------------------------------------------------------- /src/specs/networking.md: -------------------------------------------------------------------------------- 1 | # Networking 2 | 3 | - [Wire Format](#wire-format) 4 | - [AvailableData](#availabledata) 5 | - [AvailableDataRow](#availabledatarow) 6 | - [ConsensusProposal](#consensusproposal) 7 | - [MsgWirePayForData](#msgwirepayfordata) 8 | - [Invalid Erasure Coding](#invalid-erasure-coding) 9 | - [ShareProof](#shareproof) 10 | - [BadEncodingFraudProof](#badencodingfraudproof) 11 | - [Invalid State Update](#invalid-state-update) 12 | - [StateFraudProof](#statefraudproof) 13 | 14 | ## Wire Format 15 | 16 | ### AvailableData 17 | 18 | | name | type | description | 19 | |---------------------|-------------------------------------------|---------------| 20 | | `availableDataRows` | [AvailableDataRow](#availabledatarow)`[]` | List of rows. | 21 | 22 | ### AvailableDataRow 23 | 24 | | name | type | description | 25 | |----------|-----------------------------------------|------------------| 26 | | `shares` | [Share](./data_structures.md#share)`[]` | Shares in a row. | 27 | 28 | ### ConsensusProposal 29 | 30 | Defined as `ConsensusProposal`: 31 | 32 | ```protobuf 33 | {{#include ./proto/consensus.proto:ConsensusProposal}} 34 | ``` 35 | 36 | When receiving a new block proposal `proposal` from the network, the following steps are performed in order. _Must_ indicates that peers must be blacklisted (to prevent DoS attacks) and _should_ indicates that the network message can simply be ignored. 37 | 38 | 1. `proposal.type` must be a `SignedMsgType`. 39 | 1. `proposal.round` is processed identically to Tendermint. 40 | 1. `proposal.pol_round` is processed identically to Tendermint. 41 | 1. `proposal.header` must be well-formed. 42 | 1. `proposal.header.version.block` must be [`VERSION_BLOCK`](./consensus.md#constants). 43 | 1. `proposal.header.version.app` must be [`VERSION_APP`](./consensus.md#constants). 44 | 1. `proposal.header.height` should be previous known height + 1. 45 | 1. `proposal.header.chain_id` must be [`CHAIN_ID`](./consensus.md#constants). 46 | 1. `proposal.header.time` is processed identically to Tendermint. 47 | 1. `proposal.header.last_header_hash` must be previous block's header hash. 48 | 1. `proposal.header.last_commit_hash` must be the previous block's commit hash. 49 | 1. `proposal.header.consensus_hash` must be the hash of [consensus parameters](./data_structures.md#header). 50 | 1. `proposal.header.state_commitment` must be the state root after applying the previous block's transactions. 51 | 1. `proposal.header.available_data_original_shares_used` must be at most [`AVAILABLE_DATA_ORIGINAL_SQUARE_MAX ** 2`](./consensus.md#constants). 52 | 1. `proposal.header.available_data_root` must be the [root](./data_structures.md#availabledataheader) of `proposal.da_header`. 53 | 1. `proposal.header.proposer_address` must be the [correct leader](./consensus.md#leader-selection). 54 | 1. `proposal.da_header` must be well-formed. 55 | 1. The number of elements in `proposal.da_header.row_roots` and `proposal.da_header.row_roots` must be equal. 56 | 1. The number of elements in `proposal.da_header.row_roots` must be the same as computed [here](./data_structures.md#header). 57 | 1. `proposal.proposer_signature` must be a valid [digital signature](./data_structures.md#public-key-cryptography) over the header hash of `proposal.header` that recovers to `proposal.header.proposer_address`. 58 | 1. For [full nodes](./node_types.md#node-type-definitions), `proposal.da_header` must be the result of computing the roots of the shares (received separately). 59 | 1. For [light nodes](./node_types.md#node-type-definitions), `proposal.da_header` should be sampled from for availability. 60 | 61 | ### MsgWirePayForData 62 | 63 | Defined as `MsgWirePayForData`: 64 | 65 | ```protobuf 66 | {{#include ./proto/wire.proto:MsgWirePayForData}} 67 | ``` 68 | 69 | Accepting a `MsgWirePayForData` into the mempool requires different logic than other transactions in Celestia, since it leverages the paradigm of block proposers being able to malleate transaction data. Unlike [SignedTransactionDataMsgPayForData](./data_structures.md#signedtransactiondatamsgpayfordata) (the canonical data type that is included in blocks and committed to with a data root in the block header), each `MsgWirePayForData` (the over-the-wire representation of the same) has potentially multiple signatures. 70 | 71 | Transaction senders who want to pay for a message will create a [SignedTransactionDataMsgPayForData](./data_structures.md#signedtransactiondatamsgpayfordata) object, `stx`, filling in the `stx.messageShareCommitment` field [based on the non-interactive default rules](../rationale/message_block_layout.md#non-interactive-default-rules) for `k = AVAILABLE_DATA_ORIGINAL_SQUARE_MAX`, then signing it to get a [transaction](./data_structures.md#transaction) `tx`. This process is repeated with successively smaller `k`s, decreasing by powers of 2 until `k * k <= stx.messageSize`. At that point, there would be insufficient shares to include both the message and transaction. Using the rest of the signed transaction data along with the pairs of `(tx.signedTransactionData.messageShareCommitment, tx.signature)`, a `MsgWirePayForData` object is constructed. 72 | 73 | Receiving a `MsgWirePayForData` object from the network follows the reverse process: for each `message_commitment_and_signature`, verify using the [non-interactive default rules](../rationale/message_block_layout.md#non-interactive-default-rules) that the signature is valid. 74 | 75 | ## Invalid Erasure Coding 76 | 77 | If a malicious block producer incorrectly computes the 2D Reed-Solomon code for a block's data, a fraud proof for this can be presented. We assume that the light clients have the [AvailableDataHeader](./data_structures.md#availabledataheader) and the [Header](./data_structures.md#header) for each block. Hence, given a [ShareProof](#shareproof), they can verify if the `rowRoot` or `colRoot` specified by `isCol` and `position` commits to the corresponding [Share](./data_structures.md#share). Similarly, given the `height` of a block, they can access all elements within the [AvailableDataHeader](./data_structures.md#availabledataheader) and the [Header](./data_structures.md#header) of the block. 78 | 79 | ### ShareProof 80 | 81 | | name | type | description | 82 | |------------|---------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------| 83 | | `share` | [Share](./data_structures.md#share) | The share. | 84 | | `proof` | [NamespaceMerkleTreeInclusionProof](./data_structures.md#namespacemerkletreeinclusionproof) | The Merkle proof of the share in the offending row or column root. | 85 | | `isCol` | `bool` | A Boolean indicating if the proof is from a row root or column root; `false` if it is a row root. | 86 | | `position` | `uint64` | The index of the share in the offending row or column. | 87 | 88 | ### BadEncodingFraudProof 89 | 90 | Defined as `BadEncodingFraudProof`: 91 | 92 | ```protobuf 93 | {{#include ./proto/types.proto:BadEncodingFraudProof}} 94 | ``` 95 | 96 | | name | type | description | 97 | |---------------|---------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------| 98 | | `height` | [Height](./data_structures.md#type-aliases) | Height of the block with the offending row or column. | 99 | | `shareProofs` | [ShareProof](#shareproof)`[]` | The available shares in the offending row or column. | 100 | | `isCol` | `bool` | A Boolean indicating if it is an offending row or column; `false` if it is a row. | 101 | | `position` | `uint64` | The index of the offending row or column in the square. | 102 | 103 | ## Invalid State Update 104 | 105 | If a malicious block producer incorrectly computes the state, a fraud proof for this can be presented. We assume that the light clients have the [AvailableDataHeader](./data_structures.md#availabledataheader) and the [Header](./data_structures.md#header) for each block. Hence, given a [ShareProof](#shareproof), they can verify if the `rowRoot` or `colRoot` specified by `isCol` and `position` commits to the corresponding [Share](./data_structures.md#share). Similarly, given the `height` of a block, they can access all elements within the [AvailableDataHeader](./data_structures.md#availabledataheader) and the [Header](./data_structures.md#header) of the block. 106 | 107 | ### StateFraudProof 108 | 109 | Defined as `StateFraudProof`: 110 | 111 | ```protobuf 112 | {{#include ./proto/types.proto:StateFraudProof}} 113 | ``` 114 | 115 | | name | type | description | 116 | |----------------------------|------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 117 | | `height` | [Height](./data_structures.md#type-aliases) | Height of the block with the intermediate state roots. Subtracting one from `height` gives the height of the block with the transactions. | 118 | | `transactionShareProofs` | [ShareProof](#shareproof)`[]` | `isCol` of type `bool` must be `false`. | 119 | | `isrShareProofs` | [ShareProof](#shareproof)`[]` | `isCol` of type `bool` must be `false`. | 120 | | `index` | `uint64` | Index for connecting the [WrappedIntermediateStateRoot](./data_structures.md#wrappedintermediatestateroot) and [WrappedTransaction](./data_structures.md#wrappedtransaction) after shares are parsed. | 121 | | `intermediateStateElements`| [StateElement](./data_structures.md#stateelement)`[]` | State elements that were changed by the transactions. | 122 | | `stateInclusionProofs` | [SparseMerkleTreeInclusionProof](./data_structures.md#sparsemerkletreeinclusionproof)`[]`| SparseMerkleTree inclusion proofs for the state elements. | 123 | -------------------------------------------------------------------------------- /src/specs/node_types.md: -------------------------------------------------------------------------------- 1 | # Node Types 2 | 3 | - [Node Parameters](#node-parameters) 4 | - [Block Headers](#block-headers) 5 | - [Compact Block Headers](#compact-block-headers) 6 | - [Extended Block Headers](#extended-block-headers) 7 | - [Block Bodies](#block-bodies) 8 | - [No Bodies](#no-bodies) 9 | - [Sampled Bodies](#sampled-bodies) 10 | - [Partial Bodies](#partial-bodies) 11 | - [Full Bodies](#full-bodies) 12 | - [Transactions](#transactions) 13 | - [No Transactions](#no-transactions) 14 | - [Full Transactions](#full-transactions) 15 | - [Node Type Definitions](#node-type-definitions) 16 | 17 | ## Node Parameters 18 | 19 | Nodes that run the [Celestia protocol](./consensus.md) have a number of parameters that can be tweaked with regards to which parts of the data is downloaded, validated, and/or stored. All nodes process the `header` and `lastCommit` fields of each [block](./data_structures.md#block), but can handle the `availableDataHeader` and `availableData` fields differently. 20 | 21 | We define security assumptions as assumptions under which a given node is guaranteed accountable consensus safety (i.e. that finalized blocks will remain in the prefix of all future sequences of blocks accepted by the node, unless a supermajority (> 2/3) of validator voting power performs an attributable—and thus [penalizable](./consensus.md#blockavailabledataevidencedata)—malicious action) and state safety (i.e. that an invalid state transition will not be included in the chain accepted by the node). 22 | 23 | ### Block Headers 24 | 25 | #### Compact Block Headers 26 | 27 | Nodes that only process compact block headers will download and validate the [block header](./data_structures.md#header) _without_ downloading or validating the `availableDataHeader` [block](./data_structures.md#block) field that is committed to in the block header. These nodes cannot perform Data Availability Sampling on [block bodies](#block-bodies). 28 | 29 | Secure under an honest supermajority of validator voting power and a weak subjectivity assumption. 30 | 31 | #### Extended Block Headers 32 | 33 | Nodes that process extended block headers will download and validate both the [compact block header](#compact-block-headers) and the 34 | [`availableDataHeader`](./data_structures.md#availabledataheader) [block](./data_structures.md#block) field. These nodes can perform Data Availability Sampling on [block bodies](#block-bodies), and their security assumptions depend on how block bodies are handled. 35 | 36 | ### Block Bodies 37 | 38 | Block bodies (the `availableData` [block](./data_structures.md#block) field) can be downloaded and optionally stored and/or served. Storing and serving block body data has no effect on node security assumptions. 39 | 40 | #### No Bodies 41 | 42 | Nodes that only process [compact block headers](#compact-block-headers) have no need for block bodies and simply do not process block bodies. 43 | 44 | Secure under an honest supermajority of validator voting power and a weak subjectivity assumption. 45 | 46 | #### Sampled Bodies 47 | 48 | These nodes perform Data Availability Sampling on block bodies. 49 | 50 | Secure under an honest minority of nodes and a weak subjectivity assumption. 51 | 52 | #### Partial Bodies 53 | 54 | These nodes fully download and validate [the erasure coding](./data_structures.md#2d-reed-solomon-encoding-scheme) of a random subset of block bodies (configurable locally). Since the erasure coding of each block is stateless, nodes that perform validation of partial bodies contribute to the overall security of the network by being able to produce [fraud proofs of invalid erasure coding](./data_structures.md#invalid-erasure-coding). 55 | 56 | Secure under an honest minority of nodes and a weak subjectivity assumption. 57 | 58 | #### Full Bodies 59 | 60 | These nodes fully download and validate [the erasure coding](./data_structures.md#2d-reed-solomon-encoding-scheme) of all block bodies. 61 | 62 | If [transactions are not processed](#no-transactions), secure under an honest minority of nodes and a weak subjectivity assumption. If [transactions are processed](#full-transactions), secure under a weak subjectivity assumption. 63 | 64 | ### Transactions 65 | 66 | #### No Transactions 67 | 68 | These nodes do not process requests [with a reserved namespace ID](./data_structures.md#arranging-available-data-into-shares) and thus do not know the chain state without relying on a third party. 69 | 70 | At most secure under an honest minority of nodes and a weak subjectivity assumption. 71 | 72 | #### Full Transactions 73 | 74 | Nodes that wish to produce new blocks must know the [chain state](./data_structures.md#state). Processing all block bodies is actually not needed to know the Celestia state, as [transactions that pay for message inclusion commit to messages](../rationale/message_block_layout.md). These nodes process all requests [with a reserved namespace ID](./data_structures.md#arranging-available-data-into-shares) from block bodies and perform Data Availability Sampling for the remaining (message) data. 75 | 76 | At most secure under a weak subjectivity assumption. 77 | 78 | ## Node Type Definitions 79 | 80 | For convenience, we will define several common parameter configurations: 81 | 82 | 1. [Full nodes](https://en.bitcoin.it/wiki/Full_node) provide the strongest security guarantees. 83 | - Block headers: [Extended Block Headers](#extended-block-headers) 84 | - Block bodies: [Full Bodies](#full-bodies) 85 | - Storage: Not stored beyond a parameter 86 | - Serving: Served to the network 87 | - Transactions: [Full Transactions](#full-transactions) 88 | - Fraud proofs: 89 | - State transition: produce, consume 90 | - Erasure coding: produce, consume 91 | 1. Partial nodes are capable of producing fraud proofs of invalid transactions and contribute to validating the erasure coding of random blocks. 92 | - Block headers: [Extended Block Headers](#extended-block-headers) 93 | - Block bodies: [Partial Bodies](#partial-bodies) 94 | - Storage: Not stored beyond a parameter 95 | - Serving: Served to the network 96 | - Transactions: [Full Transactions](#full-transactions) 97 | - Fraud proofs: 98 | - State transition: produce, consume 99 | - Erasure coding: produce (partial), consume 100 | 1. Light nodes perform Data Availability Sampling (DAS) and are secure under an honest minority. 101 | - Block headers: [Extended Block Headers](#extended-block-headers) 102 | - Block bodies: [Sampled Bodies](#sampled-bodies) 103 | - Storage: Not stored beyond a parameter 104 | - Serving: Served to the network 105 | - Transactions: [No Transactions](#no-transactions) 106 | - Fraud proofs: 107 | - State transition: consume 108 | - Erasure coding: consume 109 | 1. Superlight nodes do not perform DAS and are secure under an honest majority. 110 | - Block headers: [Compact Block Headers](#compact-block-headers) 111 | - Block bodies: [No Bodies](#no-bodies) 112 | - Storage: Not stored 113 | - Serving: Not served to the network 114 | - Transactions: [No Transactions](#no-transactions) 115 | - Fraud proofs: 116 | - State transition: consume 117 | - Erasure coding: consume 118 | 1. Storage nodes provide the same security guarantees as full nodes. 119 | - Block headers: [Extended Block Headers](#extended-block-headers) 120 | - Block bodies: [Full Bodies](#full-bodies) 121 | - Storage: Fully stored in [erasure-coded form](./data_structures.md#2d-reed-solomon-encoding-scheme) 122 | - Serving: Served to the network 123 | - Transactions: [Full Transactions](#full-transactions) 124 | - Fraud proofs: 125 | - State transition: produce, consume 126 | - Erasure coding: produce, consume 127 | 1. Partial storage nodes provide the same security guarantees as partial nodes. 128 | - Block headers: [Extended Block Headers](#extended-block-headers) 129 | - Block bodies: [Full Bodies](#full-bodies) 130 | - Storage: Partially stored in [erasure-coded form](./data_structures.md#2d-reed-solomon-encoding-scheme) 131 | - Serving: Served to the network 132 | - Transactions: [Full Transactions](#full-transactions) 133 | - Fraud proofs: 134 | - State transition: produce, consume 135 | - Erasure coding: produce (partial), consume 136 | 1. Validator nodes are identical to full nodes, but should additionally create and sign consensus messages (proposals, votes, etc.). 137 | - Block headers: [Extended Block Headers](#extended-block-headers) 138 | - Block bodies: [Full Bodies](#full-bodies) 139 | - Storage: Not stored beyond a parameter 140 | - Serving: Served to the network 141 | - Transactions: [Full Transactions](#full-transactions) 142 | - Fraud proofs: 143 | - State transition: produce, consume 144 | - Erasure coding: produce, consume 145 | -------------------------------------------------------------------------------- /src/specs/proto/consensus.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/timestamp.proto"; 4 | 5 | import "types.proto"; 6 | 7 | // Define wire types for consensus messages. 8 | 9 | // SignedMsgType is a type of signed message in the consensus. 10 | enum SignedMsgType { 11 | SignedMsgTypeUnknown = 0; 12 | 13 | // Votes 14 | SignedMsgTypePrevote = 1; 15 | SignedMsgTypePrecommit = 2; 16 | 17 | // Proposals 18 | SignedMsgTypeProposal = 32; 19 | } 20 | 21 | // ANCHOR: ConsensusProposal 22 | message ConsensusProposal { 23 | SignedMsgType type = 1; 24 | int32 round = 2; 25 | int32 pol_round = 3; 26 | // 32-byte hash 27 | // Proposed block header 28 | Header header = 4; 29 | AvailableDataHeader da_header = 5; 30 | // 64-byte signature 31 | bytes proposer_signature = 6; 32 | } 33 | // ANCHOR_END: ConsensusProposal 34 | 35 | message ConsensusVote { 36 | SignedMsgType type = 1; 37 | int64 height = 2; 38 | int32 round = 3; 39 | // 32-byte hash 40 | bytes header_hash = 4; 41 | google.protobuf.Timestamp timestamp = 5; 42 | // 32-byte hash 43 | bytes validator_address = 6; 44 | int32 validator_index = 7; 45 | // 64-byte signature 46 | bytes signature = 8; 47 | } 48 | -------------------------------------------------------------------------------- /src/specs/proto/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/timestamp.proto"; 4 | 5 | // Define data structures. 6 | 7 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#header 8 | message Header { 9 | ConsensusVersion version = 1; 10 | string chain_id = 2; 11 | int64 height = 3; 12 | google.protobuf.Timestamp time = 4; 13 | // 32-byte hash 14 | BlockID last_header_hash = 5; 15 | // 32-byte hash 16 | bytes last_commit_hash = 6; 17 | // 32-byte hash 18 | bytes consensus_hash = 7; 19 | // 32-byte hash 20 | bytes state_commitment = 8; 21 | uint64 available_data_original_shares_used = 9; 22 | // 32-byte hash 23 | bytes available_data_root = 10; 24 | bytes proposer_address = 11; 25 | } 26 | 27 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#consensusversion 28 | message ConsensusVersion { 29 | uint64 block = 1; 30 | uint64 app = 2; 31 | } 32 | 33 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#availabledataheader 34 | message AvailableDataHeader { 35 | // array of 32-byte hashes 36 | repeated bytes row_roots = 1; 37 | // array of 32-byte hashes 38 | repeated bytes col_roots = 2; 39 | } 40 | 41 | // Protobuf definitions for the contents of state elements 42 | 43 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#account 44 | // AccountStatus 45 | enum AccountStatus : uint8_t { 46 | None = 1, 47 | DelegationBonded = 2, 48 | DelegationUnbonding = 3, 49 | ValidatorQueued = 4, 50 | ValidatorBonded = 5, 51 | ValidatorUnbonding = 6, 52 | ValidatorUnbonded = 7, 53 | } 54 | 55 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#periodentry 56 | message PeriodEntry { 57 | // Rewards per unit of voting power accumulated so far, in 1u 58 | uint64 rewardRate = 1; 59 | } 60 | 61 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#decimal 62 | message Decimal { 63 | // Rational numerator 64 | uint64 numerator = 1; 65 | // Rational denominator 66 | uint64 denominator = 1; 67 | } 68 | 69 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#messagepaid 70 | message MessagePaid { 71 | // Share index (in row-major order) of first share paid for (inclusive) 72 | uint64 start = 1; 73 | // Share index (in row-major order) of last share paid for (inclusive) 74 | uint64 finish = 2; 75 | // Next transaction ID in the list 76 | // 32-byte hash 77 | bytes next = 3; 78 | } 79 | 80 | // Protobuf definitions for the state elements 81 | 82 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#account 83 | message Account { 84 | // Coin balance 85 | uint64 balance = 1; 86 | // Account nonce. Every outgoing transaction from this account increments the nonce. 87 | uint64 nonce = 2; 88 | // Validator or delegation status of this account 89 | AccountStatus status = 3; 90 | } 91 | 92 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#delegation 93 | message Delegation { 94 | // The validator being delegating to 95 | // 32-bytes 96 | bytes validator = 1; 97 | // Delegated stake, in 4u 98 | uint64 stakedBalance = 2; 99 | // Entry when delegation began 100 | PeriodEntry beginEntry = 3; 101 | // Entry when delegation ended (i.e. began unbonding) 102 | PeriodEntry endEntry = 4; 103 | // Block height delegation began unbonding 104 | int64 unbondingHeight = 5; 105 | } 106 | 107 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#validator 108 | message StateValidator { 109 | // Validator's commission rewards, in 1u 110 | uint64 commissionRewards = 1; 111 | // Commission rate 112 | Decimal commissionRate = 2; 113 | // Number of accounts delegating to the validator 114 | uint32 delegatedCount = 3; 115 | // Total voting power as staked balance + delegated stake, in 4u 116 | uint64 votingPower = 4; 117 | // Rewards collected so far this period, in 1u 118 | uint64 pendingRewards = 5; 119 | // Latest entry, used for calculating reward distribution 120 | PeriodEntry latestEntry = 6; 121 | // Block height validator began unbonding 122 | int64 unbondingHeight = 7; 123 | // If this validator has been slashed or not 124 | bool isSlashed = 8; 125 | // Rate at which this validator has been slashed 126 | // slashRate should be zero if isSlashed is false. 127 | Decimal slashRate = 9; 128 | // Next validator in the queue. Zero if this validator is not in the queue 129 | // 32-bytes 130 | bytes next = 10; 131 | } 132 | 133 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#activevalidatorcount 134 | message ActiveValidatorCount { 135 | // Number of active validators 136 | uint32 numValidators = 1; 137 | } 138 | 139 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#activevotingpower 140 | message ActiveVotingPower { 141 | // Active voting power 142 | uint64 votingPower = 1; 143 | } 144 | 145 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#proposerblockreward 146 | message ProposerBlockReward { 147 | // Total block reward (subsidy + fees) in current block so far. Reset each block 148 | uint64 reward = 1; 149 | } 150 | 151 | 152 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#proposerinitialvotingpower 153 | message ProposerInitialVotingPower { 154 | // Voting power of the proposer at the start of each block. Set each block 155 | uint64 votingPower = 1; 156 | } 157 | 158 | 159 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#validatorqueuehead 160 | message ValidatorQueueHead { 161 | // Address of inactive validator at the head of the validator queue 162 | // 32-bytes 163 | bytes head = 1; 164 | } 165 | 166 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#messagepaidhead 167 | message MessagePaidHead { 168 | // Transaction hash at the head of the list (has the smallest start index) 169 | // 32-byte hash 170 | bytes head = 1; 171 | } 172 | 173 | //Protobuf definitions for the contents of fraud proofs 174 | 175 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#namespacemerkletreeinclusionproof 176 | message NamespaceMerkleTreeInclusionProof { 177 | // sibling hash values, ordered starting from the leaf's neighbor 178 | // array of 32-byte hashes 179 | repeated bytes siblingValues = 1; 180 | // sibling min namespace IDs 181 | // array of NAMESPACE_ID_BYTES-bytes 182 | repeated bytes siblingMins = 2; 183 | // sibling max namespace IDs 184 | // array of NAMESPACE_ID_BYTES-bytes 185 | repeated bytes siblingMaxes = 3; 186 | } 187 | 188 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#sparsemerkletreeinclusionproof 189 | message SparseMerkleTreeInclusionProof { 190 | // depth of the leaf node, must be <= 256 191 | // The root node is at depth 0. 192 | uint16 depth = 1; 193 | // sibling hash values, ordered starting from the leaf's neighbor 194 | // array of 32-byte hashes 195 | repeated bytes siblings = 2; 196 | // bitfield of explicitly included sibling hashes 197 | // 32-byte 198 | bytes includedSiblings = 3; 199 | } 200 | 201 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#share 202 | message Share { 203 | // namespace ID of the share 204 | // NAMESPACE_ID_BYTES-bytes 205 | bytes namespaceID = 1; 206 | // raw share data 207 | // SHARE_SIZE-bytes 208 | bytes rawData = 2; 209 | } 210 | 211 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#stateelement 212 | message StateElement { 213 | // key of the state element 214 | // 32-bytes 215 | bytes key = 1; 216 | // key of the state element 217 | // value can be of different types depending on the state element. 218 | // There exists a unique protobuf for different state elements. 219 | oneof value = { 220 | Account account = 2; 221 | Delegation delegation = 3; 222 | StateValidator stateValidator = 4; 223 | ActiveValidatorCount activeValidatorCount = 5; 224 | ActiveVotingPower activeVotingPower = 6; 225 | ProposerBlockReward proposerBlockReward = 7; 226 | ProposerInitialVotingPower proposerInitialVotingPower = 8; 227 | ValidatorQueueHead validatorQueueHead = 9; 228 | MessagePaid messagePaid = 10; 229 | MessagePaidHead messagePaidHead = 11; 230 | } 231 | } 232 | 233 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#shareproof 234 | message ShareProof { 235 | // the share 236 | Share share = 1; 237 | // the Merkle proof of the share in the offending row or column root 238 | NamespaceMerkleTreeInclusionProof proof = 2; 239 | // a Boolean indicating if the Merkle proof is from a row root or column root; false if it is a row root 240 | bool isCol = 3; 241 | // the index of the share in the offending row or column 242 | uint64 position = 4; 243 | } 244 | 245 | //Protobuf definitions for the fraud proofs 246 | 247 | // ANCHOR: BadEncodingFraudProof 248 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#badencodingfraudproof 249 | message BadEncodingFraudProof { 250 | // height of the block with the offending row or column 251 | int64 height = 1; 252 | // the available shares in the offending row or column and their Merkle proofs 253 | // array of ShareProofs 254 | repeated ShareProof shareProofs = 2; 255 | // a Boolean indicating if it is an offending row or column; false if it is a row 256 | bool isCol = 3; 257 | // the index of the offending row or column in the square 258 | uint64 position = 4; 259 | } 260 | // ANCHOR_END: BadEncodingFraudProof 261 | 262 | // ANCHOR: StateFraudProof 263 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#statefraudproof 264 | message StateFraudProof { 265 | // height of the block with the intermediate state roots 266 | // Subtracting one from height gives the height of the block with the transactions. 267 | int64 height = 1; 268 | // shares containing the transactions and their Merkle proofs 269 | // isCol within the ShareProof must be false. 270 | // array of ShareProofs 271 | repeated ShareProof transactionShareProofs = 2; 272 | // shares containing the intermediate state roots and their Merkle proofs 273 | // isCol within the ShareProof must be false. 274 | // array of ShareProofs 275 | repeated ShareProof isrShareProofs = 3; 276 | // index for connecting the WrappedIntermediateStateRoot and WrappedTransaction after shares are parsed 277 | uint64 index = 4; 278 | // state elements that were changed by the transactions 279 | // array of StateElements 280 | repeated StateElement intermediateStateElements = 5; 281 | // sparse Merkle tree inclusion proofs for the state elements 282 | // array of SparseMerkleTreeInclusionProofs 283 | repeated SparseMerkleTreeInclusionProof stateInclusionProofs = 6; 284 | } 285 | // ANCHOR_END: StateFraudProof 286 | -------------------------------------------------------------------------------- /src/specs/proto/wire.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // Define wire messages for transaction types. 4 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#signedtransactiondata 5 | 6 | message TransactionFee { 7 | // Tip fee rate (rewarded to block proposer) 8 | uint64 tip_rate = 1; 9 | } 10 | 11 | message Decimal { 12 | // https://github.com/celestiaorg/celestia-specs/issues/120 13 | uint64 todo = 1; 14 | } 15 | 16 | message MessageCommitmentAndSignature { 17 | // Original square size the share commitment was computed for 18 | uint64 k = 1; 19 | // 32-byte hash: commitment to message shares 20 | bytes share_commitment = 2; 21 | // 64-byte signature a single SignedTransactionPayForMessage 22 | // https://github.com/celestiaorg/celestia-specs/blob/master/specs/data_structures.md#signedtransactiondatamsgpayfordata 23 | bytes signature = 3; 24 | } 25 | 26 | message WireTxTransfer { 27 | uint64 amount = 1; 28 | // 32-byte address 29 | bytes to = 2; 30 | TransactionFee fee = 3; 31 | uint64 nonce = 4; 32 | } 33 | 34 | // ANCHOR: MsgWirePayForData 35 | message MsgWirePayForData { 36 | TransactionFee fee = 1; 37 | uint64 nonce = 2; 38 | // 8-byte namespace ID 39 | bytes message_namespace_id = 3; 40 | uint64 message_size = 4; 41 | bytes message = 5; 42 | repeated MessageCommitmentAndSignature message_commitment_and_signature = 6; 43 | } 44 | // ANCHOR_END: MsgWirePayForData 45 | 46 | message WireTxCreateValidator { 47 | TransactionFee fee = 1; 48 | uint64 nonce = 2; 49 | Decimal commission_rate = 3; 50 | } 51 | 52 | message WireTxBeginUnbondingValidator { 53 | TransactionFee fee = 1; 54 | uint64 nonce = 2; 55 | } 56 | 57 | message WireTxUnbondValidator { 58 | TransactionFee fee = 1; 59 | uint64 nonce = 2; 60 | } 61 | 62 | message WireTxCreateDelegation { 63 | uint64 amount = 1; 64 | // 32-byte address 65 | bytes to = 2; 66 | TransactionFee fee = 3; 67 | uint64 nonce = 4; 68 | } 69 | 70 | message WireTxBeginUnbondingDelegation { 71 | TransactionFee fee = 1; 72 | uint64 nonce = 2; 73 | } 74 | 75 | message WireTxUnbondDelegation { 76 | TransactionFee fee = 1; 77 | uint64 nonce = 2; 78 | } 79 | 80 | message WireTxBurn { 81 | uint64 amount = 1; 82 | TransactionFee fee = 2; 83 | uint64 nonce = 3; 84 | // 32-byte graffiti 85 | bytes graffiti = 4; 86 | } 87 | 88 | message WireTxRedelegateCommission { 89 | // 32-byte address 90 | bytes to = 1; 91 | TransactionFee fee = 2; 92 | uint64 nonce = 3; 93 | } 94 | 95 | message WireTxRedelegateReward { 96 | TransactionFee fee = 1; 97 | uint64 nonce = 2; 98 | } 99 | -------------------------------------------------------------------------------- /src/specs/specification.md: -------------------------------------------------------------------------------- 1 | # Specification 2 | 3 | - [Architecture](./architecture.md) 4 | - [Data Structures](./data_structures.md) 5 | - [Consensus](./consensus.md) 6 | - [Block Proposer](./block_proposer.md) 7 | - [Networking](./networking.md) 8 | - [Light client](./light_client.md) 9 | - [Node types](./node_types.md) 10 | --------------------------------------------------------------------------------