Skip to content

abci: Process Proposal#6480

Closed
ValarDragon wants to merge 6 commits intotendermint:abci++from
sikkatech:dev/process_proposal
Closed

abci: Process Proposal#6480
ValarDragon wants to merge 6 commits intotendermint:abci++from
sikkatech:dev/process_proposal

Conversation

@ValarDragon
Copy link
Contributor

@ValarDragon ValarDragon commented May 19, 2021

This PR adds an implementation of synchronous ProcessProposal into the ABCI++ branch. The main part to review is the consensus/state.go and state/execution.go, and then the rest should be checking if I implemented the ABCI++ scaffolding correctly. (Please do check this, its not really clear to me!)

I'm currently working on tests for state.go, and adding app-returned evidence to the local evidence pool. Currently there is only one added test for block execution.

ref #6066

@codecov
Copy link

codecov bot commented May 20, 2021

Codecov Report

❗ No coverage uploaded for pull request base (abci++@a9e231b). Click here to learn what that means.
The diff coverage is n/a.

❗ Current head b49e37c differs from pull request most recent head 216b838. Consider uploading reports for the commit 216b838 to get more accurate results

@@            Coverage Diff            @@
##             abci++    #6480   +/-   ##
=========================================
  Coverage          ?   60.81%           
=========================================
  Files             ?      288           
  Lines             ?    27144           
  Branches          ?        0           
=========================================
  Hits              ?    16508           
  Misses            ?     8934           
  Partials          ?     1702           

}

// NOTE: call is synchronous, use ctx to break early if needed
func (cli *grpcClient) processProposalAsync(ctx context.Context, params types.RequestProcessProposal) (*ReqRes, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? Can't we just move it into ProcessProposalSync?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we can remove this. I originally added it since I thought it was part of the required ABCI method scaffolding, but I don't think it is

@@ -117,6 +118,12 @@ message RequestApplySnapshotChunk {
string sender = 3;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting seems off in this file?

Comment on lines +136 to +139
// TODO: Need to implement deserialization of evidence bytes
// for _, ev := range resp.Evidence {
// blockExec.evpool.AddEvidence()
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The godoc states we process evidence by adding it to the evidence pool. Do we actually do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The evidence pool interface only takes in things of type types.Evidence

type EvidencePool interface {
PendingEvidence(maxBytes int64) (ev []types.Evidence, size int64)
AddEvidence(types.Evidence) error
Update(State, types.EvidenceList)
CheckEvidence(types.EvidenceList) error
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want abci.ResponseProcessProposal to return []types.Evidence instead of [][]byte

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More broadly, how is Tendermint expected to process application evidence? When it receives it from the application here does it just trust that it is valid. When it receives application evidence from another node does it run something like CheckEvidence() to make the application validate it.

return
}

if mustVoteNil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain when this happens and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, all the validation for determining whether or not we should vote nil happens in the doprevote method. However we want to process the proposal/header earlier on, and not duplicate that. So I added this as a parameter called mustVoteNil. (If process proposal says the block is invalid, vote nil)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized, an alternate approach would be to add a check for if cs.ValidBlock == nil, and then make this gate the block being deemed valid.

Copy link
Contributor

@cmwaters cmwaters May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☝️ This would also make sense to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah no wait ValidBlock depends on 2/3+ prevotes which wouldn't work.

I would instead add the check:

stateMachineValidBlock, err := cs.blockExec.ProcessProposal(cs.ProposalBlock)
if err != nil {
	cs.Logger.Error("state machine returned an error when trying to process proposal block", "err", err)
}

here as opposed to in addProposalBlockPart(). I also think we should be validating the block before getting the application to process it

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Jun 8, 2021

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.

@github-actions github-actions bot added the stale for use by stalebot label Jun 8, 2021
@tac0turtle tac0turtle added C:abci Component: Application Blockchain Interface S:blocked Status: Blocked (link to issue which is needed to unblock) and removed stale for use by stalebot labels Jun 8, 2021
@github-actions
Copy link

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.

@github-actions github-actions bot added the stale for use by stalebot label Jun 19, 2021
@tac0turtle tac0turtle removed the stale for use by stalebot label Jun 19, 2021
@github-actions
Copy link

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.

@github-actions github-actions bot added the stale for use by stalebot label Jun 30, 2021
@tac0turtle tac0turtle removed the stale for use by stalebot label Jun 30, 2021
@@ -1939,7 +1953,8 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.NodeID)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we rename cs.ValidBlock to cs.TwoThirdPrevoteBlock, to indicate that its just about whether there are two/thirds prevotes, and not about Tendermint / App-determined validity of the block.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a great idea. The naming should at least communicate if this is a block pre-finalization or after.

@cmwaters
Copy link
Contributor

I've been digging around consensus for a bit and I want to try get some of the nomenclature clarified:

There are two places where we set ValidBlock:

  1. cs.ValidBlock = cs.ProposalBlock
  2. cs.ValidBlock = cs.ProposalBlock

This indicates that a ValidBlock is when 2f + 1 have prevoted for the node's ProposalBlock.

LockedBlock only gets set in one place:

cs.LockedBlock = cs.ProposalBlock

This is when we've received 2f + 1 votes for the ProposalBlock and just before the node precommits.
So these two things are subtly different.

I do think that calling it Valid could be confusing since it doesn't relate to whether Tendermint or the application find the block to be valid but instead signals that 2/3 have prevoted on it (and thus think it is valid). We could add an extra field POLBlock if we wanted to distinguish these two or we could leave it as is and instead make cs.ProposalBlock nil if either Tendermint or the application find the proposed block to be invalid.

Option 1: Introduce POLBlock:

  1. Update cs.RoundState to include POLBlock and POLRound (and remove ValidRound)
  2. Rename all uses of ValidBlock to POLBlock and ValidRound to POLRound
  3. Add the following lines after
    cs.ProposalBlock = block
// application validity check
appValid, err := cs.blockExec.ProcessProposal(block)
if err != nil {
	cs.Logger.Error("state machine returned an error when trying to process proposal block", "err", err)
}

// tendermint validity check
tmValid := cs.blockExec.ValidateBlock(cs.state, block)
if tmValid != nil {
    cs.Logger.Error("Proposal block is invalid, "err", err)
}

if err != nil && appValid {
    cs.ValidBlock = block
}

and then check if we can update POLBlock and POLRound:

// Update Valid* if we can.
prevotes := cs.Votes.Prevotes(cs.Round)
blockID, hasTwoThirds := prevotes.TwoThirdsMajority()
if hasTwoThirds && !blockID.IsZero() && (cs.ValidRound < cs.Round) {
if cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.Logger.Debug(
"updating valid block to new proposal block",
"valid_round", cs.Round,
"valid_block_hash", cs.ProposalBlock.Hash(),
)
cs.ValidRound = cs.Round
cs.ValidBlock = cs.ProposalBlock
cs.ValidBlockParts = cs.ProposalBlockParts

4. In defaultDoPrevote remove the ValidateBlock check and add:

if cs.ValidBlock == nil {
    logger.Debug("prevote step: ValidBlock is nil")
    cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
    return
}

Option 2: Ensure ProposalBlock is always valid

  1. Add
// application validity check
appValid, err := cs.blockExec.ProcessProposal(block)
if err != nil {
	cs.Logger.Error("state machine returned an error when trying to process proposal block", "err", err)
}

// tendermint validity check
err := cs.blockExec.ValidateBlock(cs.state, block)
if err != nil {
    cs.Logger.Error("Proposal block is invalid, "err", err)
}

if err != nil && appValid {
    // now we can add this block as the proposal block
    cs.ProposalBlock = block

    // publish event
    // check if we can update valid
}

// do the state transition logic
  1. Add a case in tryFinalizeCommit for when ProposalBlock is nil

Option 2 has a smaller surface area which for me makes it more desirable

Wdyt? @ValarDragon @milosevic

@cmwaters cmwaters changed the title Process Proposal abci: Process Proposal Jul 13, 2021
@cmwaters
Copy link
Contributor

Another thought I had: Would we ever need to call ProcessProposal whilst processing the blocks in fast sync?

@github-actions
Copy link

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.

@github-actions github-actions bot added the stale for use by stalebot label Jul 25, 2021
@ValarDragon
Copy link
Contributor Author

Apologies for the delay in response here. I prefer option 2, always ensuring the proposal block is valid

@cmwaters
Copy link
Contributor

Okay, that sounds good. Let's add the logic then to addProposalBlockPart()

@github-actions github-actions bot removed the stale for use by stalebot label Jul 27, 2021
@github-actions
Copy link

github-actions bot commented Aug 7, 2021

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.

@github-actions github-actions bot added the stale for use by stalebot label Aug 7, 2021
@cmwaters cmwaters removed the stale for use by stalebot label Aug 9, 2021
@liamsi
Copy link
Contributor

liamsi commented Aug 13, 2021

I see the blocked label applied. Is this blocked only because of time constraints or actually blocked on or due to some other issue? Is there anything we can do to unblock this PR?

@github-actions
Copy link

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.

@github-actions github-actions bot added the stale for use by stalebot label Aug 24, 2021
@ValarDragon
Copy link
Contributor Author

I'm finally with more access to coding time this week, will get this done by Thursday.

@cmwaters cmwaters removed the stale for use by stalebot label Aug 24, 2021
@github-actions
Copy link

github-actions bot commented Sep 4, 2021

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.

@github-actions github-actions bot added the stale for use by stalebot label Sep 4, 2021
@github-actions github-actions bot closed this Sep 9, 2021
@tac0turtle tac0turtle reopened this Sep 10, 2021
@tac0turtle tac0turtle removed the stale for use by stalebot label Sep 10, 2021
@ValarDragon
Copy link
Contributor Author

Joon's taking this over in a separate PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C:abci Component: Application Blockchain Interface S:blocked Status: Blocked (link to issue which is needed to unblock)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants