From aedfc4076a806a4c8c15a3f15499d63dbf27e9c5 Mon Sep 17 00:00:00 2001 From: Hyoung-yoon Kim Date: Thu, 21 Dec 2023 14:57:01 +0900 Subject: [PATCH] feat: add NewSeed spam check and default proposal checks --- app/app.go | 17 +++--- cmd/seda-chaind/cmd/init.go | 17 +++--- cmd/seda-chaind/cmd/init_cmds.go | 6 +- cmd/seda-chaind/utils/vrf_key.go | 5 ++ x/randomness/keeper/abci.go | 93 ++++++++++++++++++++++++------- x/randomness/keeper/msg_server.go | 16 +----- x/randomness/types/tx.go | 20 ------- x/staking/types/msg.go | 5 ++ 8 files changed, 101 insertions(+), 78 deletions(-) delete mode 100644 x/randomness/types/tx.go diff --git a/app/app.go b/app/app.go index b16ef5fe..bb779e9b 100644 --- a/app/app.go +++ b/app/app.go @@ -51,7 +51,6 @@ import ( servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/std" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/mempool" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" @@ -305,12 +304,7 @@ func NewApp( std.RegisterLegacyAminoCodec(legacyAmino) std.RegisterInterfaces(interfaceRegistry) - // TO-DO // building BaseApp - nonceMempool := mempool.NewSenderNonceMempool() - mempoolOpt := baseapp.SetMempool(nonceMempool) - baseAppOptions = append(baseAppOptions, mempoolOpt) - bApp := baseapp.NewBaseApp(Name, logger, db, txConfig.TxDecoder(), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetVersion(version.Version) @@ -874,11 +868,15 @@ func NewApp( if vrfKey == nil || err != nil { // TO-DO if validator, panic app.Logger().Info("failed to load vrf key") - vrfKey = nil + if err != nil { + app.Logger().Error(err.Error()) + } } + proposalHandler := randomnesskeeper.NewDefaultProposalHandler(bApp) + app.SetPrepareProposal( - randomnesskeeper.PrepareProposalHandler( + proposalHandler.PrepareProposalHandler( txConfig, vrfKey, app.RandomnessKeeper, @@ -888,8 +886,7 @@ func NewApp( ) app.SetProcessProposal( - randomnesskeeper.ProcessProposalHandler( - txConfig, + proposalHandler.ProcessProposalHandler( vrfKey, app.RandomnessKeeper, app.StakingKeeper, diff --git a/cmd/seda-chaind/cmd/init.go b/cmd/seda-chaind/cmd/init.go index 476ef671..97623205 100644 --- a/cmd/seda-chaind/cmd/init.go +++ b/cmd/seda-chaind/cmd/init.go @@ -7,16 +7,18 @@ import ( "path/filepath" "strings" + "github.com/cosmos/go-bip39" "github.com/pkg/errors" "github.com/spf13/cobra" cfg "github.com/cometbft/cometbft/config" cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/privval" - "github.com/cometbft/cometbft/types" + + // "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/go-bip39" + "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/sedaprotocol/seda-chain/cmd/seda-chaind/utils" ) @@ -76,16 +78,11 @@ func downloadAndApplyNetworkConfig(network, moniker string, config *cfg.Config) } // check genesis file - genFile := config.GenesisFile() - jsonBlob, err := os.ReadFile(genFile) - if err != nil { - return "", "", err - } - genDoc, err := types.GenesisDocFromJSON(jsonBlob) + appGenesis, err := types.AppGenesisFromFile(config.GenesisFile()) if err != nil { - return "", "", errors.Wrapf(err, "error reading GenesisDoc at %s", genFile) + return "", "", errors.Wrapf(err, "failed to read genesis doc file %s", config.GenesisFile()) } - chainID = genDoc.ChainID + chainID = appGenesis.ChainID // obtain seeds from seeds file, if exists, and write to config file seedsBytes, err := os.ReadFile(seedsFile) diff --git a/cmd/seda-chaind/cmd/init_cmds.go b/cmd/seda-chaind/cmd/init_cmds.go index 6461fdab..86eb9ee0 100644 --- a/cmd/seda-chaind/cmd/init_cmds.go +++ b/cmd/seda-chaind/cmd/init_cmds.go @@ -171,12 +171,13 @@ $ %s init join moniker --network devnet network, _ := cmd.Flags().GetString(FlagNetwork) var seeds, chainID string - if network == "mainnet" || network == "devnet" || network == "testnet" || network == "localnet" { + switch network { + case "mainnet", "devnet", "testnet", "localnet": chainID, seeds, err = downloadAndApplyNetworkConfig(network, args[0], config) if err != nil { return err } - } else { + default: return fmt.Errorf("unsupported network type: %s", network) } @@ -205,6 +206,5 @@ $ %s init join moniker --network devnet if err != nil { panic(err) } - return cmd } diff --git a/cmd/seda-chaind/utils/vrf_key.go b/cmd/seda-chaind/utils/vrf_key.go index f473a246..041d89d1 100644 --- a/cmd/seda-chaind/utils/vrf_key.go +++ b/cmd/seda-chaind/utils/vrf_key.go @@ -32,6 +32,7 @@ type VRFSigner interface { VRFVerify(publicKey, alpha, pi []byte) (beta []byte, err error) SignTransaction(ctx sdk.Context, txBuilder client.TxBuilder, txConfig client.TxConfig, signMode signing.SignMode, account sdk.AccountI) (signing.SignatureV2, error) + IsNil() bool } var _ VRFSigner = &VRFKey{} @@ -150,6 +151,10 @@ func (v *VRFKey) SignTransaction( return sigV2, nil } +func (v *VRFKey) IsNil() bool { + return v == nil +} + // NewVRFKey generates a new VRFKey from the given key and key file path. func NewVRFKey(privKey crypto.PrivKey, keyFilePath string) (*VRFKey, error) { vrfStruct := vrf.NewK256VRF() diff --git a/x/randomness/keeper/abci.go b/x/randomness/keeper/abci.go index 88c33f80..24e9efbe 100644 --- a/x/randomness/keeper/abci.go +++ b/x/randomness/keeper/abci.go @@ -7,6 +7,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" @@ -15,7 +16,19 @@ import ( "github.com/sedaprotocol/seda-chain/x/randomness/types" ) -func PrepareProposalHandler( +type ProposalHandler struct { + txVerifier baseapp.ProposalTxVerifier + txSelector baseapp.TxSelector +} + +func NewDefaultProposalHandler(txVerifier baseapp.ProposalTxVerifier) *ProposalHandler { + return &ProposalHandler{ + txVerifier: txVerifier, + txSelector: baseapp.NewDefaultTxSelector(), + } +} + +func (h *ProposalHandler) PrepareProposalHandler( txConfig client.TxConfig, vrfSigner utils.VRFSigner, keeper Keeper, @@ -23,16 +36,41 @@ func PrepareProposalHandler( stakingKeeper types.StakingKeeper, ) sdk.PrepareProposalHandler { return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - if vrfSigner == nil { - return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil + if vrfSigner.IsNil() { + return nil, fmt.Errorf("vrf signer is nil") + } + + // Default prepare proposal - check max block gas and req.MaxTxBytes + var maxBlockGas uint64 + if b := ctx.ConsensusParams().Block; b != nil { + maxBlockGas = uint64(b.MaxGas) } - // TO-DO run DefaultProposalHandler.PrepareProposalHandler first? + defer h.txSelector.Clear() + for _, txBz := range req.Txs { + tx, err := h.txVerifier.TxDecode(txBz) + if err != nil { + return nil, err + } + + // do not include any NewSeed txs + _, ok := decodeNewSeedTx(tx) + if ok { + continue + } + + stop := h.txSelector.SelectTxForProposal(ctx, uint64(req.MaxTxBytes), maxBlockGas, tx, txBz) + if stop { + break + } + } + + // Seed transaction // alpha = (seed_{i-1} || timestamp) prevSeed := keeper.GetSeed(ctx) if prevSeed == "" { - panic("seed should never be empty") + return nil, fmt.Errorf("previous seed is empty - this should never happen") } timestamp, err := req.Time.MarshalBinary() if err != nil { @@ -46,7 +84,7 @@ func PrepareProposalHandler( return nil, err } - // create a NewSeed tx + // generate and sign NewSeed tx pubKey, err := keeper.GetValidatorVRFPubKey(ctx, sdk.ConsAddress(req.ProposerAddress).String()) if err != nil { return nil, err @@ -67,20 +105,40 @@ func PrepareProposalHandler( // prepend to list of txs and return res := new(abci.ResponsePrepareProposal) - res.Txs = append([][]byte{newSeedTx}, req.Txs...) + res.Txs = append([][]byte{newSeedTx}, h.txSelector.SelectedTxs(ctx)...) return res, nil } } -func ProcessProposalHandler( - txConfig client.TxConfig, +func (h *ProposalHandler) ProcessProposalHandler( vrfSigner utils.VRFSigner, keeper Keeper, stakingKeeper types.StakingKeeper, ) sdk.ProcessProposalHandler { return func(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { - msg, err := decodeNewSeedTx(txConfig, req.Txs[0]) + defer h.txSelector.Clear() + + for _, txBz := range req.Txs[1:] { + tx, err := h.txVerifier.TxDecode(txBz) + if err != nil { + return nil, err + } + + // reject proposal that includes NewSeed tx in any position other + // than top of tx list + _, ok := decodeNewSeedTx(tx) + if ok { + return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, err + } + } + + tx, err := h.txVerifier.TxDecode(req.Txs[0]) if err != nil { + return nil, err + } + + msg, ok := decodeNewSeedTx(tx) + if !ok { return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, err } @@ -89,9 +147,6 @@ func ProcessProposalHandler( fmt.Errorf("the NewSeed transaction must be from the block proposer") } - // TO-DO run DefaultProposalHandler.ProcessProposalHandler first? - // TO-DO Validate()? - // get block proposer's validator public key pubKey, err := keeper.GetValidatorVRFPubKey(ctx, sdk.ConsAddress(req.ProposerAddress).String()) if err != nil { @@ -165,18 +220,14 @@ func generateAndSignNewSeedTx(ctx sdk.Context, txConfig client.TxConfig, vrfSign return txBytes, nil } -func decodeNewSeedTx(txConfig client.TxConfig, txBytes []byte) (*types.MsgNewSeed, error) { - tx, err := txConfig.TxDecoder()(txBytes) - if err != nil { - return nil, err - } +func decodeNewSeedTx(tx sdk.Tx) (*types.MsgNewSeed, bool) { msgs := tx.GetMsgs() if len(msgs) != 1 { - return nil, err + return nil, false } msgNewSeed, ok := msgs[0].(*types.MsgNewSeed) if !ok { - return nil, err + return nil, false } - return msgNewSeed, nil + return msgNewSeed, true } diff --git a/x/randomness/keeper/msg_server.go b/x/randomness/keeper/msg_server.go index 24fc07a0..1bae3d33 100644 --- a/x/randomness/keeper/msg_server.go +++ b/x/randomness/keeper/msg_server.go @@ -21,22 +21,10 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} func (k msgServer) NewSeed(goCtx context.Context, msg *types.MsgNewSeed) (*types.MsgNewSeedResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - // TO-DO spam prevention? - - k.Keeper.SetSeed(ctx, msg.Beta) + sdkCtx := sdk.UnwrapSDKContext(goCtx) + k.Keeper.SetSeed(sdkCtx, msg.Beta) // TO-DO event? - // err = ctx.EventManager().EmitTypedEvent( - // &types.EventNewSeed{ - // Hash: hashString, - // WasmType: msg.WasmType, - // Bytecode: msg.Wasm, - // }) - // if err != nil { - // return nil, err - // } return &types.MsgNewSeedResponse{}, nil } diff --git a/x/randomness/types/tx.go b/x/randomness/types/tx.go deleted file mode 100644 index b8b0a9ec..00000000 --- a/x/randomness/types/tx.go +++ /dev/null @@ -1,20 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func (msg MsgNewSeed) Route() string { - return RouterKey -} - -func (msg MsgNewSeed) Type() string { - return "new-seed" -} - -func (msg MsgNewSeed) ValidateBasic() error { - if _, err := sdk.AccAddressFromBech32(msg.Proposer); err != nil { - return err - } - return nil -} diff --git a/x/staking/types/msg.go b/x/staking/types/msg.go index dd163fdf..9de45f95 100644 --- a/x/staking/types/msg.go +++ b/x/staking/types/msg.go @@ -12,6 +12,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) +var ( + _ sdk.Msg = &MsgCreateValidatorWithVRF{} + _ codectypes.UnpackInterfacesMessage = (*MsgCreateValidatorWithVRF)(nil) +) + // NewMsgCreateValidator creates a new MsgCreateValidator instance. // Delegator address and validator address are the same. func NewMsgCreateValidatorWithVRF(