ABCI++ markdown integration into Tendermint (for spec)#283
ABCI++ markdown integration into Tendermint (for spec)#283ValarDragon wants to merge 12 commits intotendermint:masterfrom
Conversation
liamsi
left a comment
There was a problem hiding this comment.
Left some nits on v0.md will go through the others shortly.
spec/abci++/v0.md
Outdated
| h_p = 0 | ||
| round_p = 0 | ||
| step_p is one of {propose, prevote, precommit} | ||
| decision_p = nil | ||
| lockedValue_p = nil | ||
| validValue_p = nil | ||
| validRound_p = -1 |
There was a problem hiding this comment.
This is somewhat orthogonal to abci++ but I still wanted to comment on this: I know that _p comes from the original bft paper but I'm wondering if we should just leave away the subscript here and express the proposer(h_p, round_p) = p slightly differently? IMO, it is clear that all this code runs in/on one "process" and dragging this subscript around everywhere just decreases readability. As I said, this highly unrelated to abci++ but I see these markdown files as becoming the place to look up how tendermint consensus works and how to implement it. So I'm in favour of expressing this as accessible and readable as possible (not necessarily in this PR though).
There was a problem hiding this comment.
Hrmm, cc @milosevic @konnov @josef-widder for thoughts on this.
Its not super clear to me, since I think removing it may make it easier to conclude that when height / round increments, it increments for every validator.
(Its also not entirely clear to me, how / if ever this markdown will get merged)
There was a problem hiding this comment.
I agree that the subscript _p is not optimal. However, it is crucial to see who is executing the code. This prefix is the only indication that the code is executed by a process p.
There was a problem hiding this comment.
Given that some upon rules work as pattern matching, it is very important to distinguish the process variables (e.g., round_p) from those "temporary" variables (e.g., vr, r).
| proposal ← validValue_p | ||
| } | ||
| else { | ||
| txdata ← mempool.GetBlock() |
There was a problem hiding this comment.
Speaking about the mempool, I think it would be great if the mempool connection was also mentioned somewhere (not necessarily in the pseudo code and just for the sake of completeness).
konnov
left a comment
There was a problem hiding this comment.
Made a pass over v0.md. There are several inconsistencies with the original pseudo-code. I am not sure, whether some of them were intended, or they are just typos.
spec/abci++/v0.md
Outdated
| h_p ← 0 | ||
| round_p ← 0 | ||
| step_p is one of {propose, prevote, precommit} | ||
| decision_p ← nil |
There was a problem hiding this comment.
In the Tendermint paper, decision_p is a dynamically growing vector of values. I guess, that is why the authors wrote decision_p[] := nil in the paper. Can we write:
decision_p ← Vector()
Or Array(), or List(). I am fine with any choice here.
There was a problem hiding this comment.
Set to vector for now, but I wonder if for technical correctness we should actually put Map, since its indexed by h_p, and we don't initialize at 0
| @@ -0,0 +1,156 @@ | |||
| # Tendermint v0 Markdown pseudocode | |||
|
|
|||
| This translates the latex code for Tendermint consensus from the Tendermint paper into markdown. | |||
There was a problem hiding this comment.
The case of OnTimeoutPropose is missing.
konnov
left a comment
There was a problem hiding this comment.
Versions 1-2 helped me a lot to see the envisioned changes. Several comments on v1-v2.
|
|
||
| ```go | ||
| upon ⟨PROPOSAL, h_p, round_p, v, −1) from proposer(h_p, round_p) while step_p = propose do { | ||
| if valid(v) ∧ ABCI.ProcessProposal(h_p, v).accept ∧ (lockedRound_p = −1 ∨ lockedValue_p = v) { |
There was a problem hiding this comment.
Trying to clarify things for myself: Is v understood as a block header or a complete block? It would be great to have a comment that explains the expected behavior of ABCI.ProcessProposal(h_p, v).
There was a problem hiding this comment.
Also, does ProcessProposal interact with mempool?
There was a problem hiding this comment.
v here is understood as the complete block in v2. In v3 and v4 we can split up the methods according to when they are received in implementation, should we reflect this in the pseudocode?
(This would be done by changing the upon's usage of v to be v_header, and then if VerifyHeader passes, we have a line saying something to the effect of wait to receive the complete block proposal v that corresponds to v_header, and specifying that we cancel this wait if we get to TimeoutPropose)
ProcessProposal does not interact with the mempool, I'll add some more comments around here
There was a problem hiding this comment.
I think that having v_header would help!
| ```go | ||
| function OnTimeoutPrevote(height, round) { | ||
| if (height = h_p && round = round_p && step_p = prevote) { | ||
| precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) |
There was a problem hiding this comment.
Could you add an example of precommit_extension in a comment?
Also, why would one need to extend the vote on nil? Is it a form of piggy-backing?
There was a problem hiding this comment.
Sure, I'll add an example of precommit_extension!
You have a good point, I don't actually foresee a reason to extend a nil vote (since that precommit won't make it into a block).
That would only make sense in a consensus algorithm in which nil precommits made in time still get included in the next block, which doesn't happen at the moment. The only reasons I can imagine for this seem pretty contrived, so I'll remove it
| if (step_p = prevote) { | ||
| lockedValue_p ← v | ||
| lockedRound_p ← round_p | ||
| precommit_extension ← ABCI.ExtendVote(h_p, round_p, id(v)) |
There was a problem hiding this comment.
Same here. An example of precommit_extension would be great.
|
|
||
| ### Upon receiving a precommit | ||
|
|
||
| Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true` |
There was a problem hiding this comment.
should the call to ABCI.VerifyVoteExtension include the height h_p and value v as parameters?
spec/abci++/v2.md
Outdated
| upon ⟨PROPOSAL, h_p, round_p, v, -1⟩ | ||
| from proposer(h_p, round_p) | ||
| AND 2f + 1 ⟨ PRECOMMIT, h_p, vr, id(v)⟩ |
There was a problem hiding this comment.
Here the difference between the original pseudo-code and v0-v2 is crucial. In the original pseudo-code, a process could catch up with a round r that is different from round_p. I guess, you would like to avoid catch up here, as the application has partially processed the block for round_p.
There was a problem hiding this comment.
If it is the case, we need a comment here.
konnov
left a comment
There was a problem hiding this comment.
Having read v4, I can say that this is a great presentation of the proposed changes to ABCI++. Added a few comments.
| while step_p = propose ∧ (vr ≥ 0 ∧ vr < round_p) do { | ||
| if valid(v) ∧ ABCI.VerifyHeader(h_p, v.header) ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) { | ||
| // We fork process proposal into a parallel process | ||
| Fork ABCI.ProcessProposal(h_p, v) |
There was a problem hiding this comment.
Can a single validator run two instances of ProcessProposal at the same time? If so, what do we assume about the state of the ABCI app?
There was a problem hiding this comment.
I think it should be impossible per the spec at the moment for this to happen? (I believe every process proposal process gets joined prior to incrementing rounds locally)
There was a problem hiding this comment.
For instance, the last rule "Upon Receiving 2f + 1 precommits" can fire anytime. This would advance the height and start a new round. What happens to the ProcessProposal thread that is still running?
I think we should understand how the ABCI app is supposed to work with these requests:
- The app processes requests one-by-one, that is, only a single
ProcessProposalis working till it is joined, or - The app maintains potential future states (produced by different transactions) and then it finalizes one of them.
It looks like the pseudo code is written with Scenario 1 in mind. Then we have to be very careful about not sending concurrent requests to the app.
There was a problem hiding this comment.
When I am saying "the app" here, I really mean the layer below Tendermint. So it is either the app or the ABCI layer resolving concurrency for the app.
Co-authored-by: Igor Konnov <igor.konnov@gmail.com>
| upon ⟨PROPOSAL, h_p, round_p, v, −1) from proposer(h_p, round_p) while step_p = propose do { | ||
| if valid(v) ∧ ABCI.VerifyHeader(h_p, v.header) ∧ (lockedRound_p = −1 ∨ lockedValue_p = v) { | ||
| // We fork process proposal into a parallel process | ||
| Fork ABCI.ProcessProposal(h_p, v) |
There was a problem hiding this comment.
We discussed in slack but just noting here the concern with this is that we could end up with a POLKA for a block that turns out to be invalid (ie. fails ProcessProposal) and this could have liveness consequences.
So we need to carefully work through the consequences of this and adjust as necessary or else just suffer the latency cost of completing ProcessProposal before prevoting.
There was a problem hiding this comment.
It might be fine prevoting for v without waiting ProcessProposal(v) to return, but we should probably not set locked/valid value if ProcessProposal(v) hasn't returned successfully. Therefore, we ensure that validValue/lockedValue is never set to invalid block, and this ensures that invalid block is never committed.
| decision_p[h_p] ← v | ||
| h_p ← h_p + 1 | ||
| reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values | ||
| ABCI.FinalizeBlock() |
There was a problem hiding this comment.
What happens with precommit_extension upon commit?
There was a problem hiding this comment.
They are handled by PrepareProposal, the data may be included within the block (either in the header or tx data), or not included at all and just have been there as an intermediate step.
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
| @@ -0,0 +1,200 @@ | |||
| # Tendermint v3 Markdown pseudocode | |||
There was a problem hiding this comment.
and the updates in the following lines 3~5?
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
This PR adds a markdown version of how ABCI++ aims to hook into Tendermint from a spec perspective, based on a prior discussion with Igor, Josef and Zarko.
The structure of the code is as follows:
v0.md contains my transcription of the Tendermint BFT paper into markdown directly.
v1.md contains how I think ABCI hooks in.
v2.md contains a conversion to single-threaded ABCI++, with no optimization for splitting up verification of the header and block data
v3.md contains the optimization for splitting up verification of the header and block data
v4.md contains the multi-threaded optimization for delaying the end of ProcessProposal.
The structure for reviewing this is to review it commit by commit. Each commit shows the update to one file, so that it may be incrementally reviewed. (As a side effect, it also involves copying the file as well).
So the v0 commit contains the code for v0, and a duplicate called v1.md.
The v1 commit contains the diff on v1.md for the change, and a duplicate of v1.md called v2.md.
The v2 commit contains the diff on v2.md for its change, and a duplciate called v3.md, etc.
Left as a draft until the complete v3.md and v4.md are included.