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

fix(unorderedtx): issues reported in audit (#21467) #23727

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 48 additions & 39 deletions baseapp/abci_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

type (
// ValidatorStore defines the interface contract require for verifying vote
// ValidatorStore defines the interface contract required for verifying vote
// extension signatures. Typically, this will be implemented by the x/staking
// module, which has knowledge of the CometBFT public key.
ValidatorStore interface {
Expand Down Expand Up @@ -83,7 +83,7 @@ func ValidateVoteExtensions(
totalVP += vote.Validator.Power

// Only check + include power if the vote is a commit vote. There must be super-majority, otherwise the
// previous block (the block vote is for) could not have been committed.
// previous block (the block the vote is for) could not have been committed.
if vote.BlockIdFlag != cmtproto.BlockIDFlagCommit {
continue
}
Expand Down Expand Up @@ -286,35 +286,41 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
invalidTxs []sdk.Tx // invalid txs to be removed out of the loop to avoid dead lock
)
mempool.SelectBy(ctx, h.mempool, req.Txs, func(memTx sdk.Tx) bool {
signerData, err := h.signerExtAdapter.GetSigners(memTx)
if err != nil {
// propagate the error to the caller
resError = err
return false
}

// If the signers aren't in selectedTxsSignersSeqs then we haven't seen them before
// so we add them and continue given that we don't need to check the sequence.
shouldAdd := true
unorderedTx, ok := memTx.(sdk.TxWithUnordered)
isUnordered := ok && unorderedTx.GetUnordered()
txSignersSeqs := make(map[string]uint64)
for _, signer := range signerData {
seq, ok := selectedTxsSignersSeqs[signer.Signer.String()]
if !ok {
txSignersSeqs[signer.Signer.String()] = signer.Sequence
continue

// if the tx is unordered, we don't need to check the sequence, we just add it
if !isUnordered {
signerData, err := h.signerExtAdapter.GetSigners(memTx)
if err != nil {
// propagate the error to the caller
resError = err
return false
}

// If we have seen this signer before in this block, we must make
// sure that the current sequence is seq+1; otherwise is invalid
// and we skip it.
if seq+1 != signer.Sequence {
shouldAdd = false
break
// If the signers aren't in selectedTxsSignersSeqs then we haven't seen them before
// so we add them and continue given that we don't need to check the sequence.
shouldAdd := true
for _, signer := range signerData {
seq, ok := selectedTxsSignersSeqs[signer.Signer.String()]
if !ok {
txSignersSeqs[signer.Signer.String()] = signer.Sequence
continue
}

// If we have seen this signer before in this block, we must make
// sure that the current sequence is seq+1; otherwise is invalid
// and we skip it.
if seq+1 != signer.Sequence {
shouldAdd = false
break
}
txSignersSeqs[signer.Signer.String()] = signer.Sequence
}
if !shouldAdd {
return true
}
txSignersSeqs[signer.Signer.String()] = signer.Sequence
}
if !shouldAdd {
return true
}

// NOTE: Since transaction verification was already executed in CheckTx,
Expand All @@ -331,18 +337,21 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
}

txsLen := len(h.txSelector.SelectedTxs(ctx))
for sender, seq := range txSignersSeqs {
// If txsLen != selectedTxsNums is true, it means that we've
// added a new tx to the selected txs, so we need to update
// the sequence of the sender.
if txsLen != selectedTxsNums {
selectedTxsSignersSeqs[sender] = seq
} else if _, ok := selectedTxsSignersSeqs[sender]; !ok {
// The transaction hasn't been added but it passed the
// verification, so we know that the sequence is correct.
// So we set this sender's sequence to seq-1, in order
// to avoid unnecessary calls to PrepareProposalVerifyTx.
selectedTxsSignersSeqs[sender] = seq - 1
// If the tx is unordered, we don't need to update the sender sequence.
if !isUnordered {
for sender, seq := range txSignersSeqs {
// If txsLen != selectedTxsNums is true, it means that we've
// added a new tx to the selected txs, so we need to update
// the sequence of the sender.
if txsLen != selectedTxsNums {
selectedTxsSignersSeqs[sender] = seq
} else if _, ok := selectedTxsSignersSeqs[sender]; !ok {
// The transaction hasn't been added but it passed the
// verification, so we know that the sequence is correct.
// So we set this sender's sequence to seq-1, in order
// to avoid unnecessary calls to PrepareProposalVerifyTx.
selectedTxsSignersSeqs[sender] = seq - 1
}
}
}
selectedTxsNums = txsLen
Expand Down
10 changes: 8 additions & 2 deletions runtime/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"slices"

abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/x/auth/ante/unorderedtx"

runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
Expand Down Expand Up @@ -40,8 +41,10 @@ import (
type App struct {
*baseapp.BaseApp

ModuleManager *module.Manager
configurator module.Configurator
ModuleManager *module.Manager
UnorderedTxManager *unorderedtx.Manager

configurator module.Configurator // nolint:staticcheck // SA1019: Configurator is deprecated but still used in runtime v1.
config *runtimev1alpha1.Module
storeKeys []storetypes.StoreKey
interfaceRegistry codectypes.InterfaceRegistry
Expand Down Expand Up @@ -157,6 +160,9 @@ func (a *App) Load(loadLatest bool) error {

// PreBlocker application updates every pre block
func (a *App) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
if a.UnorderedTxManager != nil {
a.UnorderedTxManager.OnNewBlock(ctx.BlockTime())
}
return a.ModuleManager.PreBlock(ctx)
}

Expand Down
2 changes: 1 addition & 1 deletion simapp/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, options.TxManager),
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, options.TxManager, ante.DefaultSha256Cost),
ante.NewValidateMemoDecorator(options.AccountKeeper),
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
Expand Down
3 changes: 2 additions & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ func NewSimApp(

app.GovKeeper = *govKeeper.SetHooks(
govtypes.NewMultiGovHooks(
// register the governance hooks
// register the governance hooks
),
)

Expand Down Expand Up @@ -637,6 +637,7 @@ func (app *SimApp) Name() string { return app.BaseApp.Name() }

// PreBlocker application updates every pre block
func (app *SimApp) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
app.UnorderedTxManager.OnNewBlock(ctx.BlockTime())
return app.ModuleManager.PreBlock(ctx)
}

Expand Down
19 changes: 19 additions & 0 deletions types/mempool/priority_nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@
sender := sig.Signer.String()
priority := mp.cfg.TxPriority.GetTxPriority(ctx, tx)
nonce := sig.Sequence

// if it's an unordered tx, we use the gas instead of the nonce
if unordered, ok := tx.(sdk.TxWithUnordered); ok && unordered.GetUnordered() {
gasLimit, err := unordered.GetGasLimit()

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / dependency-review

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (02)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (03)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 228 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)
nonce = gasLimit
if err != nil {
return err
}
}

key := txMeta[C]{nonce: nonce, priority: priority, sender: sender}

senderIndex, ok := mp.senderIndices[sender]
Expand Down Expand Up @@ -458,6 +468,15 @@
sender := sig.Signer.String()
nonce := sig.Sequence

// if it's an unordered tx, we use the gas instead of the nonce
if unordered, ok := tx.(sdk.TxWithUnordered); ok && unordered.GetUnordered() {
gasLimit, err := unordered.GetGasLimit()

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / dependency-review

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (02)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (03)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 473 in types/mempool/priority_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)
nonce = gasLimit
if err != nil {
return err
}
}

scoreKey := txMeta[C]{nonce: nonce, sender: sender}
score, ok := mp.scores[scoreKey]
if !ok {
Expand Down
18 changes: 18 additions & 0 deletions types/mempool/sender_nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@
snm.senders[sender] = senderTxs
}

// if it's an unordered tx, we use the gas instead of the nonce
if unordered, ok := tx.(sdk.TxWithUnordered); ok && unordered.GetUnordered() {
gasLimit, err := unordered.GetGasLimit()

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / dependency-review

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (02)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (03)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 150 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)
nonce = gasLimit
if err != nil {
return err
}
}

senderTxs.Set(nonce, tx)

key := txKey{nonce: nonce, address: sender}
Expand Down Expand Up @@ -227,6 +236,15 @@
sender := sdk.AccAddress(sig.PubKey.Address()).String()
nonce := sig.Sequence

// if it's an unordered tx, we use the gas instead of the nonce
if unordered, ok := tx.(sdk.TxWithUnordered); ok && unordered.GetUnordered() {
gasLimit, err := unordered.GetGasLimit()

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / dependency-review

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (02)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (01)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (03)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)

Check failure on line 241 in types/mempool/sender_nonce.go

View workflow job for this annotation

GitHub Actions / tests (00)

unordered.GetGasLimit undefined (type "github.com/cosmos/cosmos-sdk/types".TxWithUnordered has no field or method GetGasLimit)
nonce = gasLimit
if err != nil {
return err
}
}

senderTxs, found := snm.senders[sender]
if !found {
return ErrTxNotFound
Expand Down
13 changes: 11 additions & 2 deletions types/tx_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
fmt "fmt"
strings "strings"
"time"

"github.com/cosmos/gogoproto/proto"
protov2 "google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -71,6 +72,14 @@ type (
GetMemo() string
}

// TxWithTimeoutTimeStamp extends the Tx interface by allowing a transaction to
// set a timeout timestamp.
TxWithTimeoutTimeStamp interface {
Tx

GetTimeoutTimeStamp() time.Time
}

// TxWithTimeoutHeight extends the Tx interface by allowing a transaction to
// set a height timeout.
TxWithTimeoutHeight interface {
Expand All @@ -80,9 +89,9 @@ type (
}

// TxWithUnordered extends the Tx interface by allowing a transaction to set
// the unordered field, which implicitly relies on TxWithTimeoutHeight.
// the unordered field, which implicitly relies on TxWithTimeoutTimeStamp.
TxWithUnordered interface {
TxWithTimeoutHeight
TxWithTimeoutTimeStamp

GetUnordered() bool
}
Expand Down
Loading
Loading