|
| 1 | +# Ledger verification |
| 2 | + |
| 3 | +1. Blocks and transactions verification requires many steps, and some of the steps' outcomes |
| 4 | +may become clear only in the middle of the process. Therefore, in order to check a transaction/ |
| 5 | +block one should repeat the whole process of block application. |
| 6 | +Therefore, an idea of combined verification/application process is considered, we call it |
| 7 | +ledger verification. |
| 8 | + |
| 9 | +2. The ledger always checks transactions for all errors and performs all necessary checks. |
| 10 | +But the reaction to the errors can be different. |
| 11 | + |
| 12 | +# Two types of reaction |
| 13 | + |
| 14 | +We have basically two variants of behaviour if the error in application/verification occurred: |
| 15 | + |
| 16 | +1. Print an error message. This happens when an incorrect block apply happened, which probably |
| 17 | + broke the state. We can only hope that further blockchain rollbacks will correct it. |
| 18 | + |
| 19 | +2. Send a ValidationStatus::NoGo message via corresponding channel (each module has a validation |
| 20 | + outcome channel). These channels are listened by Consensus module, and if at least one of them |
| 21 | + sent 'NoGo' outcome, then the block is rejected. |
| 22 | + |
| 23 | + If the block was validated as 'NoGo', the ledger state should remain intact. |
| 24 | + |
| 25 | +# How it works |
| 26 | + |
| 27 | +1. Blocks and transactions can appear in Acropolis from two sources: |
| 28 | + * Mithril/other trusted source --- blocks, already accepted by the blockchain. |
| 29 | + If everything is ok, then the block is applied, internal structures updated, and next block |
| 30 | + is processed. |
| 31 | + If something is not correct, then the whole blockchain is broken, and outside intervention |
| 32 | + is required. |
| 33 | + |
| 34 | + * Mempool or consensus blocks --- proposals for the blockchain. If the block/transaction is not |
| 35 | + successfully verified, then it could be refused. |
| 36 | + If block is verified, it may be included into chain as mutable (optimistic version), |
| 37 | + or checked in two phases: first verified, and then processed as trusted block (pessimistic). |
| 38 | + |
| 39 | +2. `BlockInfo` data structure contains `BlockIntent` field, which (along with block number) control |
| 40 | + this process of block and transaction application and verification. |
| 41 | + |
| 42 | +## `BlockIntent` field |
| 43 | + |
| 44 | +1. There are three different ways of block processing (specified in `BlockIntent`): |
| 45 | + * `Validate`: check block and send ValidateState::Go (if block is valid) or NoGo (block is invalid). |
| 46 | + Internal state should not change. |
| 47 | + * `Apply`: apply block, print error if it's incorrect (internal structures correctness is not |
| 48 | + guaranteed, behaviour after such application is undefined). |
| 49 | + * `ValidateAndApply`: check block and apply it if block is correct, send error if it's incorrect. |
| 50 | + Internal state should not change if the block is incorrect. |
| 51 | + |
| 52 | + The `Apply` variant is easier to implement, since it does not require integrity of data structures |
| 53 | + if incorrect transaction/block is applied. |
| 54 | + |
| 55 | +2. Application can be initiated by Mithril/Upstream fetcher/Peer network interface modules: modules that |
| 56 | + trust the sources. It has no natural reaction to errors. |
| 57 | + |
| 58 | +3. Validation is initiated by Consensus module. That module check block candidates one by one, and it |
| 59 | + handles error reaction by rejecting the block candidate and trying the next one. |
| 60 | + |
| 61 | +## Block number and rollbacks |
| 62 | + |
| 63 | +1. `BlockInfo` keeps track of the current block number. Blocks are numbered sequentially. So if the |
| 64 | + number equal to the previous one (or smaller than it), then Rollback takes place (all info from |
| 65 | + blocks with this or greater number should be deleted, and new attempt to apply block is done). |
| 66 | + In another words, applying of block N may be possible only if module state is actual for block N-1. |
| 67 | + |
| 68 | +2. So, if the block applied unsuccessfully (and internal structures are broken), the situation |
| 69 | + can be corrected by rolling back to last correct block and applying different (correct) block |
| 70 | + after it. |
| 71 | + |
| 72 | + However, after unsucessful application and before successful rollback the state of the node is |
| 73 | + incorrect. |
| 74 | + |
| 75 | +## Mulit-module ledger specifics |
| 76 | + |
| 77 | +Ledger is split into several modules, so it gives additional challenges to the validation process. |
| 78 | + |
| 79 | +1. And if one module verified the block, then next modules in chain of messages may still reject it. |
| 80 | + We should guarantee that the state of the ledger would not become inconsistent in this situation. |
| 81 | + |
| 82 | +2. In case some block is invalid, we may skip sending messages to further dependent modules. |
| 83 | + This makes synchronisation harder. Consensus module should check not only message numbers, |
| 84 | + but also block data (hash, etc), and skip all replies that do not correspond to current verification. |
| 85 | + |
| 86 | + Instead, it should wait either for all 'Go' (from all modules), or for at least one 'NoGo', |
| 87 | + and do not wait for further messages. |
0 commit comments