github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/docs/rfc/rfc-013-abci++.md (about) 1 # RFC 013: ABCI++ 2 3 ## Changelog 4 5 - 2020-01-11: initialized 6 - 2021-02-11: Migrate RFC to tendermint repo (Originally [RFC 004](https://github.com/tendermint/spec/pull/254)) 7 8 ## Author(s) 9 10 - Dev (@valardragon) 11 - Sunny (@sunnya97) 12 13 ## Context 14 15 ABCI is the interface between the consensus engine and the application. 16 It defines when the application can talk to consensus during the execution of a blockchain. 17 At the moment, the application can only act at one phase in consensus, immediately after a block has been finalized. 18 19 This restriction on the application prohibits numerous features for the application, including many scalability improvements that are now better understood than when ABCI was first written. 20 For example, many of the scalability proposals can be boiled down to "Make the miner / block proposers / validators do work, so the network does not have to". 21 This includes optimizations such as tx-level signature aggregation, state transition proofs, etc. 22 Furthermore, many new security properties cannot be achieved in the current paradigm, as the application cannot enforce validators do more than just finalize txs. 23 This includes features such as threshold cryptography, and guaranteed IBC connection attempts. 24 We propose introducing three new phases to ABCI to enable these new features, and renaming the existing methods for block execution. 25 26 #### Prepare Proposal phase 27 28 This phase aims to allow the block proposer to perform more computation, to reduce load on all other full nodes, and light clients in the network. 29 It is intended to enable features such as batch optimizations on the transaction data (e.g. signature aggregation, zk rollup style validity proofs, etc.), enabling stateless blockchains with validator provided authentication paths, etc. 30 31 This new phase will only be executed by the block proposer. The application will take in the block header and raw transaction data output by the consensus engine's mempool. It will then return block data that is prepared for gossip on the network, and additional fields to include into the block header. 32 33 #### Process Proposal Phase 34 35 This phase aims to allow applications to determine validity of a new block proposal, and execute computation on the block data, prior to the blocks finalization. 36 It is intended to enable applications to reject block proposals with invalid data, and to enable alternate pipelined execution models. (Such as Ethereum-style immediate execution) 37 38 This phase will be executed by all full nodes upon receiving a block, though on the application side it can do more work in the even that the current node is a validator. 39 40 #### Vote Extension Phase 41 42 This phase aims to allow applications to require their validators do more than just validate blocks. 43 Example usecases of this include validator determined price oracles, validator guaranteed IBC connection attempts, and validator based threshold crypto. 44 45 This adds an app-determined data field that every validator must include with their vote, and these will thus appear in the header. 46 47 #### Rename {BeginBlock, [DeliverTx], EndBlock} to FinalizeBlock 48 49 The prior phases gives the application more flexibility in their execution model for a block, and they obsolete the current methods for how the consensus engine relates the block data to the state machine. Thus we refactor the existing methods to better reflect what is happening in the new ABCI model. 50 51 This rename doesn't on its own enable anything new, but instead improves naming to clarify the expectations from the application in this new communication model. The existing ABCI methods `BeginBlock, [DeliverTx], EndBlock` are renamed to a single method called `FinalizeBlock`. 52 53 #### Summary 54 55 We include a more detailed list of features / scaling improvements that are blocked, and which new phases resolve them at the end of this document. 56 57 <image src="images/abci.png" style="float: left; width: 40%;" /> <image src="images/abci++.png" style="float: right; width: 40%;" /> 58 On the top is the existing definition of ABCI, and on the bottom is the proposed ABCI++. 59 60 ## Proposal 61 62 Below we suggest an API to add these three new phases. 63 In this document, sometimes the final round of voting is referred to as precommit for clarity in how it acts in the Tendermint case. 64 65 ### Prepare Proposal 66 67 *Note, APIs in this section will change after Vote Extensions, we list the adjusted APIs further in the proposal.* 68 69 The Prepare Proposal phase allows the block proposer to perform application-dependent work in a block, to lower the amount of work the rest of the network must do. This enables batch optimizations to a block, which has been empirically demonstrated to be a key component for scaling. This phase introduces the following ABCI method 70 71 ```rust 72 fn PrepareProposal(Block) -> BlockData 73 ``` 74 75 where `BlockData` is a type alias for however data is internally stored within the consensus engine. In Tendermint Core today, this is `[]Tx`. 76 77 The application may read the entire block proposal, and mutate the block data fields. Mutated transactions will still get removed from the mempool later on, as the mempool rechecks all transactions after a block is executed. 78 79 The `PrepareProposal` API will be modified in the vote extensions section, for allowing the application to modify the header. 80 81 ### Process Proposal 82 83 The Process Proposal phase sends the block data to the state machine, prior to running the last round of votes on the state machine. This enables features such as allowing validators to reject a block according to whether state machine deems it valid, and changing block execution pipeline. 84 85 We introduce three new methods, 86 87 ```rust 88 fn VerifyHeader(header: Header, isValidator: bool) -> ResponseVerifyHeader {...} 89 fn ProcessProposal(block: Block) -> ResponseProcessProposal {...} 90 fn RevertProposal(height: usize, round: usize) {...} 91 ``` 92 93 where 94 95 ```rust 96 struct ResponseVerifyHeader { 97 accept_header: bool, 98 evidence: Vec<Evidence> 99 } 100 struct ResponseProcessProposal { 101 accept_block: bool, 102 evidence: Vec<Evidence> 103 } 104 ``` 105 106 Upon receiving a block header, every validator runs `VerifyHeader(header, isValidator)`. The reason for why `VerifyHeader` is split from `ProcessProposal` is due to the later sections for Preprocess Proposal and Vote Extensions, where there may be application dependent data in the header that must be verified before accepting the header. 107 If the returned `ResponseVerifyHeader.accept_header` is false, then the validator must precommit nil on this block, and reject all other precommits on this block. `ResponseVerifyHeader.evidence` is appended to the validators local `EvidencePool`. 108 109 Upon receiving an entire block proposal (in the current implementation, all "block parts"), every validator runs `ProcessProposal(block)`. If the returned `ResponseProcessProposal.accept_block` is false, then the validator must precommit nil on this block, and reject all other precommits on this block. `ResponseProcessProposal.evidence` is appended to the validators local `EvidencePool`. 110 111 Once a validator knows that consensus has failed to be achieved for a given block, it must run `RevertProposal(block.height, block.round)`, in order to signal to the application to revert any potentially mutative state changes it may have made. In Tendermint, this occurs when incrementing rounds. 112 113 **RFC**: How do we handle the scenario where honest node A finalized on round x, and honest node B finalized on round x + 1? (e.g. when 2f precommits are publicly known, and a validator precommits themself but doesn't broadcast, but they increment rounds) Is this a real concern? The state root derived could change if everyone finalizes on round x+1, not round x, as the state machine can depend non-uniformly on timestamp. 114 115 The application is expected to cache the block data for later execution. 116 117 The `isValidator` flag is set according to whether the current node is a validator or a full node. This is intended to allow for beginning validator-dependent computation that will be included later in vote extensions. (An example of this is threshold decryptions of ciphertexts.) 118 119 ### DeliverTx rename to FinalizeBlock 120 121 After implementing `ProcessProposal`, txs no longer need to be delivered during the block execution phase. Instead, they are already in the state machine. Thus `BeginBlock, DeliverTx, EndBlock` can all be replaced with a single ABCI method for `ExecuteBlock`. Internally the application may still structure its method for executing the block as `BeginBlock, DeliverTx, EndBlock`. However, it is overly restrictive to enforce that the block be executed after it is finalized. There are multiple other, very reasonable pipelined execution models one can go for. So instead we suggest calling this succession of methods `FinalizeBlock`. We propose the following API 122 123 Replace the `BeginBlock, DeliverTx, EndBlock` ABCI methods with the following method 124 125 ```rust 126 fn FinalizeBlock() -> ResponseFinalizeBlock 127 ``` 128 129 where `ResponseFinalizeBlock` has the following API, in terms of what already exists 130 131 ```rust 132 struct ResponseFinalizeBlock { 133 updates: ResponseEndBlock, 134 tx_results: Vec<ResponseDeliverTx> 135 } 136 ``` 137 138 `ResponseEndBlock` should then be renamed to `ConsensusUpdates` and `ResponseDeliverTx` should be renamed to `ResponseTx`. 139 140 ### Vote Extensions 141 142 The Vote Extensions phase allow applications to force their validators to do more than just validate within consensus. This is done by allowing the application to add more data to their votes, in the final round of voting. (Namely the precommit) 143 This additional application data will then appear in the block header. 144 145 First we discuss the API changes to the vote struct directly 146 147 ```rust 148 fn ExtendVote(height: u64, round: u64) -> (UnsignedAppVoteData, SelfAuthenticatingAppData) 149 fn VerifyVoteExtension(signed_app_vote_data: Vec<u8>, self_authenticating_app_vote_data: Vec<u8>) -> bool 150 ``` 151 152 There are two types of data that the application can enforce validators to include with their vote. 153 There is data that the app needs the validator to sign over in their vote, and there can be self-authenticating vote data. Self-authenticating here means that the application upon seeing these bytes, knows its valid, came from the validator and is non-malleable. We give an example of each type of vote data here, to make their roles clearer. 154 155 - Unsigned app vote data: A use case of this is if you wanted validator backed oracles, where each validator independently signs some oracle data in their vote, and the median of these values is used on chain. Thus we leverage consensus' signing process for convenience, and use that same key to sign the oracle data. 156 - Self-authenticating vote data: A use case of this is in threshold random beacons. Every validator produces a threshold beacon share. This threshold beacon share can be verified by any node in the network, given the share and the validators public key (which is not the same as its consensus public key). However, this decryption share will not make it into the subsequent block's header. They will be aggregated by the subsequent block proposer to get a single random beacon value that will appear in the subsequent block's header. Everyone can then verify that this aggregated value came from the requisite threshold of the validator set, without increasing the bandwidth for full nodes or light clients. To achieve this goal, the self-authenticating vote data cannot be signed over by the consensus key along with the rest of the vote, as that would require all full nodes & light clients to know this data in order to verify the vote. 157 158 The `CanonicalVote` struct will acommodate the `UnsignedAppVoteData` field by adding another string to its encoding, after the `chain-id`. This should not interfere with existing hardware signing integrations, as it does not affect the constant offset for the `height` and `round`, and the vote size does not have an explicit upper bound. (So adding this unsigned app vote data field is equivalent from the HSM's perspective as having a superlong chain-ID) 159 160 **RFC**: Please comment if you think it will be fine to have elongate the message the HSM signs, or if we need to explore pre-hashing the app vote data. 161 162 The flow of these methods is that when a validator has to precommit, Tendermint will first produce a precommit canonical vote without the application vote data. It will then pass it to the application, which will return unsigned application vote data, and self authenticating application vote data. It will bundle the `unsigned_application_vote_data` into the canonical vote, and pass it to the HSM to sign. Finally it will package the self-authenticating app vote data, and the `signed_vote_data` together, into one final Vote struct to be passed around the network. 163 164 #### Changes to Prepare Proposal Phase 165 166 There are many use cases where the additional data from vote extensions can be batch optimized. 167 This is mainly of interest when the votes include self-authenticating app vote data that be batched together, or the unsigned app vote data is the same across all votes. 168 To allow for this, we change the PrepareProposal API to the following 169 170 ```rust 171 fn PrepareProposal(Block, UnbatchedHeader) -> (BlockData, Header) 172 ``` 173 174 where `UnbatchedHeader` essentially contains a "RawCommit", the `Header` contains a batch-optimized `commit` and an additional "Application Data" field in its root. This will involve a number of changes to core data structures, which will be gone over in the ADR. 175 The `Unbatched` header and `rawcommit` will never be broadcasted, they will be completely internal to consensus. 176 177 #### Inter-process communication (IPC) effects 178 179 For brevity in exposition above, we did not discuss the trade-offs that may occur in interprocess communication delays that these changs will introduce. 180 These new ABCI methods add more locations where the application must communicate with the consensus engine. 181 In most configurations, we expect that the consensus engine and the application will be either statically or dynamically linked, so all communication is a matter of at most adjusting the memory model the data is layed out within. 182 This memory model conversion is typically considered negligible, as delay here is measured on the order of microseconds at most, whereas we face milisecond delays due to cryptography and network overheads. 183 Thus we ignore the overhead in the case of linked libraries. 184 185 In the case where the consensus engine and the application are ran in separate processes, and thus communicate with a form of Inter-process communication (IPC), the delays can easily become on the order of miliseconds based upon the data sent. Thus its important to consider whats happening here. 186 We go through this phase by phase. 187 188 ##### Prepare proposal IPC overhead 189 190 This requires a round of IPC communication, where both directions are quite large. Namely the proposer communicating an entire block to the application. 191 However, this can be mitigated by splitting up `PrepareProposal` into two distinct, async methods, one for the block IPC communication, and one for the Header IPC communication. 192 193 Then for chains where the block data does not depend on the header data, the block data IPC communication can proceed in parallel to the prior block's voting phase. (As a node can know whether or not its the leader in the next round) 194 195 Furthermore, this IPC communication is expected to be quite low relative to the amount of p2p gossip time it takes to send the block data around the network, so this is perhaps a premature concern until more sophisticated block gossip protocols are implemented. 196 197 ##### Process Proposal IPC overhead 198 199 This phase changes the amount of time available for the consensus engine to deliver a block's data to the state machine. 200 Before, the block data for block N would be delivered to the state machine upon receiving a commit for block N and then be executed. 201 The state machine would respond after executing the txs and before prevoting. 202 The time for block delivery from the consensus engine to the state machine after this change is the time of receiving block proposal N to the to time precommit on proposal N. 203 It is expected that this difference is unimportant in practice, as this time is in parallel to one round of p2p communication for prevoting, which is expected to be significantly less than the time for the consensus engine to deliver a block to the state machine. 204 205 ##### Vote Extension IPC overhead 206 207 This has a small amount of data, but does incur an IPC round trip delay. This IPC round trip delay is pretty negligible as compared the variance in vote gossip time. (the IPC delay is typically on the order of 10 microseconds) 208 209 ## Status 210 211 Proposed 212 213 ## Consequences 214 215 ### Positive 216 217 - Enables a large number of new features for applications 218 - Supports both immediate and delayed execution models 219 - Allows application specific data from each validator 220 - Allows for batch optimizations across txs, and votes 221 222 ### Negative 223 224 - This is a breaking change to all existing ABCI clients, however the application should be able to have a thin wrapper to replicate existing ABCI behavior. 225 - PrepareProposal - can be a no-op 226 - Process Proposal - has to cache the block, but can otherwise be a no-op 227 - Vote Extensions - can be a no-op 228 - Finalize Block - Can black-box call BeginBlock, DeliverTx, EndBlock given the cached block data 229 230 - Vote Extensions adds more complexity to core Tendermint Data Structures 231 - Allowing alternate alternate execution models will lead to a proliferation of new ways for applications to violate expected guarantees. 232 233 ### Neutral 234 235 - IPC overhead considerations change, but mostly for the better 236 237 ## References 238 239 Reference for IPC delay constants: <http://pages.cs.wisc.edu/~adityav/Evaluation_of_Inter_Process_Communication_Mechanisms.pdf> 240 241 ### Short list of blocked features / scaling improvements with required ABCI++ Phases 242 243 | Feature | PrepareProposal | ProcessProposal | Vote Extensions | 244 | :--- | :---: | :---: | :---: | 245 | Tx based signature aggregation | X | | | 246 | SNARK proof of valid state transition | X | | | 247 | Validator provided authentication paths in stateless blockchains | X | | | 248 | Immediate Execution | | X | | 249 | Simple soft forks | | X | | 250 | Validator guaranteed IBC connection attempts | | | X | 251 | Validator based price oracles | | | X | 252 | Immediate Execution with increased time for block execution | X | X | X | 253 | Threshold Encrypted txs | X | X | X |