Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ABCI 1.0 Phase I Completion #382

Merged
merged 25 commits into from
Dec 13, 2022
Merged

Conversation

alexanderbez
Copy link
Collaborator

@alexanderbez alexanderbez commented Nov 15, 2022

Changelog

  • Downstream changes from the core SDK repo that contains the necessary components for ABCI 1.0 phase I completion. This includes but is not limited to:
    • PrepareProposal support
    • ProcessProposal support
    • Default nonce-based Mempool
    • Default PrepareProposal handler
    • Default ProcessProposal handler

@alexanderbez
Copy link
Collaborator Author

So this PR is essentially blocked on getting the repo migrated to use cosmos/gogoproto instead of the deprecated github.com/gogo/protobuf, due to Tendermint also migrating (v0.37.x).

I've started this work here: #383

Currently blocked on getting it across the finish line...

@github-actions github-actions bot removed the C:x/bank label Dec 1, 2022
@alexanderbez alexanderbez marked this pull request as ready for review December 7, 2022 16:33
Copy link
Member

@p0mvn p0mvn left a comment

Choose a reason for hiding this comment

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

Great work!

I'm going to be reviewing in stages since the PR is large.

So far, reviewed everything on a high level and focused on the mempool in-detail. Still need to review mempool tests.

types/mempool/nonce.go Outdated Show resolved Hide resolved
types/mempool/nonce.go Outdated Show resolved Hide resolved
types/mempool/nonce.go Outdated Show resolved Hide resolved
types/mempool/nonce.go Outdated Show resolved Hide resolved
types/mempool/nonce.go Outdated Show resolved Hide resolved
// txs, or some combination of both.
Select(ctx types.Context, txs [][]byte, maxBytes int) ([]Tx, error)
// Select returns an Iterator over the app-side mempool. If txs are specified,
// then they shall be incorporated into the Iterator. The Iterator must
Copy link
Member

Choose a reason for hiding this comment

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

In our implementation, the transactions are not incorporated but just ignored. Is that expected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Correct, Osmosis (and simapp) just ignore them and use the FIFO-ordered txs that are sent from Tendermint.

types/mempool/nonce.go Outdated Show resolved Hide resolved
@alexanderbez
Copy link
Collaborator Author

Sorry @p0mvn didn't mean to waste your time on reviewing the (default) mempool implementation. The implementation was actually changed to be by random sender, instead of lowest-nonce. So this should give a reasonably more fair mempool. However, there are still DoS vectors. Regardless, this is just the default implementation and not something Osmosis will even use anyway.

Copy link
Member

@p0mvn p0mvn left a comment

Choose a reason for hiding this comment

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

LGTM.

Per earlier review strategy suggestions: reviewed abci and baseapp in-detail. Skimmed mempool.

Have some questions about tests but non-blocking since I expect the majority of testing is done at the integration level.

x/auth/tx/service_test.go Show resolved Hide resolved
baseapp/baseapp_test.go Outdated Show resolved Hide resolved
cases := map[string]struct {
setLoader func(*BaseApp)
// write some data in substore
kv, _ := rs.GetStore(key).(storetypes.KVStore)
Copy link
Member

Choose a reason for hiding this comment

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

I'm not really following the test changes in this file. Could you please summarize why these are needed and what is the relationship to ABCI?

Copy link
Collaborator Author

@alexanderbez alexanderbez Dec 13, 2022

Choose a reason for hiding this comment

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

The changes to baseapp tests are essentially captured in this PR. The tests prior to this were messy, scattered all over the place and not well organized.

So in this PR, since I had to port the ABCI 1.0 baseapp tests, I figured I'd clean it up all in one go.

So TL;DR

  • Better organization
  • Tests for ABCI 1.0

//
// If any transaction fails to pass either condition, the proposal is rejected.
// Note that step (2) is identical to the validation step performed in
// DefaultPrepareProposal. It is very important that the same validation logic
Copy link
Member

Choose a reason for hiding this comment

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

This question might be related to Adam's point of custom PrepareProposal

Assuming that ProcessProposal and PrepareProposal have validations stronger than runtTx. Does it mean that a validator who modifies a mempool and relaxes their PrepareProposal validation, can now cause chain halt?

Copy link
Collaborator Author

@alexanderbez alexanderbez Dec 13, 2022

Choose a reason for hiding this comment

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

This is captured in the ABCI spec HERE under Requirement 3.

For an honest process, if PrepareProposal passes validity checks for a proposal, those same validity checks must pass during ProcessProposal. Otherwise, you face liveness issues. In practice, this should not be a concern:

If a node is running a custom mempool and thus a custom PrepareProposal and other processes reject that proposal during ProcessProposal, Tendermint will move on to the next proposer. So it should not be a concern unless a majority of validators are running a custom PrepareProposal, then you could face liveness faults.

Comment on lines +176 to +182
if app.prepareProposal == nil {
app.SetPrepareProposal(app.DefaultPrepareProposal())
}

if app.processProposal == nil {
app.SetProcessProposal(app.DefaultProcessProposal())
}
Copy link
Member

Choose a reason for hiding this comment

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

Could you please remind me - in Osmosis, both of these are custom to perform a nonce validation check while relying on Tendermint's FIFO, is that correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Correct!

baseapp/baseapp.go Show resolved Hide resolved
Copy link
Member

@czarcas7ic czarcas7ic left a comment

Choose a reason for hiding this comment

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

Going to have to give this another pass later since it is a lot to take in. Have some clarifying questions here, thanks!

baseapp/abci.go Show resolved Hide resolved
Comment on lines +899 to +900
// is used in both steps, and applications must ensure that this is the case in
// non-default handlers.
Copy link
Member

Choose a reason for hiding this comment

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

What does it mean to use a "non-default handler"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Handler's that are not DefaultPrepareProposal and DefaultProcessProposal.


bz, err := app.txEncoder(memTx)
if err != nil {
panic(err)
Copy link
Member

Choose a reason for hiding this comment

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

If a tx fails encoding in the mempool, shouldnt we remove it from the mempool as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the upstream repo's behavior. I think the idea was that this should never happen. An alternative would be to remove and continue.

Let me ask some other people...

Comment on lines +957 to +971
return func(ctx sdk.Context, req abci.RequestProcessProposal) abci.ResponseProcessProposal {
for _, txBytes := range req.Txs {
_, err := app.txDecoder(txBytes)
if err != nil {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
}

_, _, _, err = app.runTx(runTxProcessProposal, txBytes)
if err != nil {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
}
}

return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
}
Copy link
Member

Choose a reason for hiding this comment

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

Should processProposal also be checking the byteCount as we do in prepareProposal?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, maxBytes is a local operator (proposer) configuration. Different nodes will have different preferences on MaxBytes.

Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this mean a proposer could upload an arbitrarily large block, or are maxBytes orthogonal?

Comment on lines 142 to +144
func (app *BaseApp) SetCMS(cms store.CommitMultiStore) {
if app.sealed {
panic("SetEndBlocker() on sealed BaseApp")
panic("SetCMS() on sealed BaseApp")
Copy link
Member

Choose a reason for hiding this comment

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

nit: I would prefer the func name to be SetCommitMultiStore, unless CMS is a terms that many know.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's an API breaking changes, which I'm not against.

baseapp/testutil/messages.go Show resolved Hide resolved
Comment on lines +164 to +168
tx.getSigs = func() ([]txsigning.SignatureV2, error) {
return nil, nil
}
require.Error(t, s.mempool.Insert(ctx, tx))
require.Error(t, s.mempool.Remove(tx))
Copy link
Member

@czarcas7ic czarcas7ic Dec 12, 2022

Choose a reason for hiding this comment

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

What's happening here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ensuring that such a tx cannot be inserted. And when attempted to be removed, errors as well.

Comment on lines +98 to +100
if snm.maxTx < 0 {
return nil
}
Copy link
Member

Choose a reason for hiding this comment

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

Why is no error returned when trying to insert a tx into a mempool with a maxTx of less than zero?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Comment on lines +164 to +166
if len(sigs) == 0 {
return fmt.Errorf("tx must have at least one signer")
}
Copy link
Member

Choose a reason for hiding this comment

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

Why are we checking for this in the removal? If a tx somehow got into the mempool with no signers, we should want to be able to remove that, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need to get the sender in order to properly remove the tx. If we cannot get the sender, we cannot remove.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

3 participants