-
Notifications
You must be signed in to change notification settings - Fork 37
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
Conversation
So this PR is essentially blocked on getting the repo migrated to use I've started this work here: #383 Currently blocked on getting it across the finish line... |
There was a problem hiding this 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.
// 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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. |
There was a problem hiding this 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.
cases := map[string]struct { | ||
setLoader func(*BaseApp) | ||
// write some data in substore | ||
kv, _ := rs.GetStore(key).(storetypes.KVStore) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
if app.prepareProposal == nil { | ||
app.SetPrepareProposal(app.DefaultPrepareProposal()) | ||
} | ||
|
||
if app.processProposal == nil { | ||
app.SetProcessProposal(app.DefaultProcessProposal()) | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct!
There was a problem hiding this 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!
// is used in both steps, and applications must ensure that this is the case in | ||
// non-default handlers. |
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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...
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} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
func (app *BaseApp) SetCMS(cms store.CommitMultiStore) { | ||
if app.sealed { | ||
panic("SetEndBlocker() on sealed BaseApp") | ||
panic("SetCMS() on sealed BaseApp") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
tx.getSigs = func() ([]txsigning.SignatureV2, error) { | ||
return nil, nil | ||
} | ||
require.Error(t, s.mempool.Insert(ctx, tx)) | ||
require.Error(t, s.mempool.Remove(tx)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's happening here?
There was a problem hiding this comment.
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.
if snm.maxTx < 0 { | ||
return nil | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if len(sigs) == 0 { | ||
return fmt.Errorf("tx must have at least one signer") | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Changelog
PrepareProposal
supportProcessProposal
supportMempool
PrepareProposal
handlerProcessProposal
handler