Skip to content

Commit

Permalink
Problem: e2ee module not up to date (crypto-org-chain#1610)
Browse files Browse the repository at this point in the history
* Problem: e2ee module not up to date

Solution:
- sync with 1.3.x branch

* Update CHANGELOG.md

Signed-off-by: yihuang <[email protected]>

* Update integration_tests/cosmoscli.py

Signed-off-by: yihuang <[email protected]>

* fix proto

* update store-block-list message

* update swagger

* fix integration test

* fix integration test

* cleanup

* fix test

* temp

* add address codec

* add refresh

* d

* no panic

---------

Signed-off-by: yihuang <[email protected]>
Co-authored-by: mmsqe <[email protected]>
  • Loading branch information
yihuang and mmsqe authored Sep 27, 2024
1 parent bbdd407 commit 3d3cfed
Show file tree
Hide file tree
Showing 29 changed files with 2,025 additions and 158 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [#1592](https://github.com/crypto-org-chain/cronos/pull/1592) Change the default parallelism of the block-stm to minimum between GOMAXPROCS and NumCPU
* [#1600](https://github.com/crypto-org-chain/cronos/pull/1600) Update ethermint to avoid unnecessary block result in header related api call.
* [#1606](https://github.com/crypto-org-chain/cronos/pull/1606) Fix pebbledb support.
* [#1610](https://github.com/crypto-org-chain/cronos/pull/1610) Sync e2ee module with v1.3.x branch.

### Bug Fixes

Expand Down
106 changes: 88 additions & 18 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ import (
stdruntime "runtime"
"sort"

"filippo.io/age"

abci "github.com/cometbft/cometbft/abci/types"
tmos "github.com/cometbft/cometbft/libs/os"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/gogoproto/proto"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -159,6 +162,7 @@ import (
cronosprecompiles "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper/precompiles"
"github.com/crypto-org-chain/cronos/v2/x/cronos/middleware"
cronostypes "github.com/crypto-org-chain/cronos/v2/x/cronos/types"
e2eekeyring "github.com/crypto-org-chain/cronos/v2/x/e2ee/keyring"

e2ee "github.com/crypto-org-chain/cronos/v2/x/e2ee"
e2eekeeper "github.com/crypto-org-chain/cronos/v2/x/e2ee/keeper"
Expand All @@ -179,7 +183,8 @@ const (
// NOTE: In the SDK, the default value is 255.
AddrLen = 20

FlagBlockedAddresses = "blocked-addresses"
FlagBlockedAddresses = "blocked-addresses"
FlagUnsafeIgnoreBlockListFailure = "unsafe-ignore-block-list-failure"
)

var Forks = []Fork{}
Expand Down Expand Up @@ -342,6 +347,8 @@ type App struct {
configurator module.Configurator

qms storetypes.RootMultiStore

blockProposalHandler *ProposalHandler
}

// New returns a reference to an initialized chain.
Expand All @@ -360,23 +367,67 @@ func New(
cdc := encodingConfig.Amino
txConfig := encodingConfig.TxConfig
interfaceRegistry := encodingConfig.InterfaceRegistry

txDecoder := txConfig.TxDecoder()
eip712.SetEncodingConfig(encodingConfig)

homePath := cast.ToString(appOpts.Get(flags.FlagHome))
var identity age.Identity
{
if cast.ToString(appOpts.Get("mode")) == "validator" {
krBackend := cast.ToString(appOpts.Get(flags.FlagKeyringBackend))
kr, err := e2eekeyring.New("cronosd", krBackend, homePath, os.Stdin)
if err != nil {
panic(err)
}
bz, err := kr.Get(e2eetypes.DefaultKeyringName)
if err != nil {
logger.Error("e2ee identity for validator not found", "error", err)
identity = noneIdentity{}
} else {
identity, err = age.ParseX25519Identity(string(bz))
if err != nil {
logger.Error("e2ee identity for validator is invalid", "error", err)
identity = noneIdentity{}
}
}
}
}

addressCodec := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix())

var mpool mempool.Mempool
if maxTxs := cast.ToInt(appOpts.Get(server.FlagMempoolMaxTxs)); maxTxs >= 0 {
// NOTE we use custom transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx
// Setup Mempool and Proposal Handlers
mempool := mempool.NewPriorityMempool(mempool.PriorityNonceMempoolConfig[int64]{
mpool = mempool.NewPriorityMempool(mempool.PriorityNonceMempoolConfig[int64]{
TxPriority: mempool.NewDefaultTxPriority(),
SignerExtractor: evmapp.NewEthSignerExtractionAdapter(mempool.NewDefaultSignerExtractionAdapter()),
MaxTx: maxTxs,
})
baseAppOptions = append(baseAppOptions, baseapp.SetMempool(mempool))
} else {
mpool = mempool.NoOpMempool{}
}
blockProposalHandler := NewProposalHandler(txDecoder, identity, addressCodec)
baseAppOptions = append(baseAppOptions, func(app *baseapp.BaseApp) {
app.SetMempool(mpool)

// Re-use the default prepare proposal handler, extend the transaction validation logic
defaultProposalHandler := baseapp.NewDefaultProposalHandler(mpool, app)
defaultProposalHandler.SetTxSelector(NewExtTxSelector(
baseapp.NewDefaultTxSelector(),
txDecoder,
blockProposalHandler.ValidateTransaction,
))

app.SetPrepareProposal(defaultProposalHandler.PrepareProposalHandler())

// The default process proposal handler do nothing when the mempool is noop,
// so we just implement a new one.
app.SetProcessProposal(blockProposalHandler.ProcessProposalHandler())
})

blockSTMEnabled := cast.ToString(appOpts.Get(srvflags.EVMBlockExecutor)) == "block-stm"

homePath := cast.ToString(appOpts.Get(flags.FlagHome))
var cacheSize int
if !blockSTMEnabled {
// only enable memiavl cache if block-stm is not enabled, because it's not concurrency-safe.
Expand All @@ -399,16 +450,17 @@ func New(

invCheckPeriod := cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod))
app := &App{
BaseApp: bApp,
cdc: cdc,
txConfig: txConfig,
appCodec: appCodec,
interfaceRegistry: interfaceRegistry,
invCheckPeriod: invCheckPeriod,
keys: keys,
tkeys: tkeys,
okeys: okeys,
memKeys: memKeys,
BaseApp: bApp,
cdc: cdc,
txConfig: txConfig,
appCodec: appCodec,
interfaceRegistry: interfaceRegistry,
invCheckPeriod: invCheckPeriod,
keys: keys,
tkeys: tkeys,
okeys: okeys,
memKeys: memKeys,
blockProposalHandler: blockProposalHandler,
}

app.SetDisableBlockGasMeter(true)
Expand Down Expand Up @@ -449,7 +501,7 @@ func New(
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
ethermint.ProtoAccount,
maccPerms,
authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
addressCodec,
sdk.GetConfig().GetBech32AccountAddrPrefix(),
authAddr,
)
Expand Down Expand Up @@ -976,6 +1028,15 @@ func New(
tmos.Exit(fmt.Sprintf("versiondb version %d lag behind iavl version %d", v1, v2))
}
}

if err := app.RefreshBlockList(app.NewUncachedContext(false, cmtproto.Header{})); err != nil {
if !cast.ToBool(appOpts.Get(FlagUnsafeIgnoreBlockListFailure)) {
panic(err)
}

// otherwise, just emit error log
app.Logger().Error("failed to update blocklist", "error", err)
}
}

app.ScopedIBCKeeper = scopedIBCKeeper
Expand Down Expand Up @@ -1025,7 +1086,7 @@ func (app *App) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64, bl
return fmt.Errorf("invalid bech32 address: %s, err: %w", str, err)
}

blockedMap[string(addr)] = struct{}{}
blockedMap[addr.String()] = struct{}{}
}
blockAddressDecorator := NewBlockAddressesDecorator(blockedMap)
options := evmante.HandlerOptions{
Expand Down Expand Up @@ -1085,7 +1146,16 @@ func (app *App) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) {

// EndBlocker application updates every end block
func (app *App) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) {
return app.ModuleManager.EndBlock(ctx)
rsp, err := app.ModuleManager.EndBlock(ctx)
if err := app.RefreshBlockList(ctx); err != nil {
app.Logger().Error("failed to update blocklist", "error", err)
}
return rsp, err
}

func (app *App) RefreshBlockList(ctx sdk.Context) error {
// refresh blocklist
return app.blockProposalHandler.SetBlockList(app.CronosKeeper.GetBlockList(ctx))
}

// InitChainer application update at chain initialization
Expand Down
2 changes: 1 addition & 1 deletion app/block_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (bad BlockAddressesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
return ctx, err
}
for _, signer := range signers {
if _, ok := bad.blockedMap[string(signer)]; ok {
if _, ok := bad.blockedMap[sdk.AccAddress(signer).String()]; ok {
return ctx, fmt.Errorf("signer is blocked: %s", sdk.AccAddress(signer).String())
}
}
Expand Down
172 changes: 172 additions & 0 deletions app/proposal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package app

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"

"filippo.io/age"

"cosmossdk.io/core/address"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
)

type BlockList struct {
Addresses []string `mapstructure:"addresses"`
}

var _ baseapp.TxSelector = &ExtTxSelector{}

// ExtTxSelector extends a baseapp.TxSelector with extra tx validation method
type ExtTxSelector struct {
baseapp.TxSelector
TxDecoder sdk.TxDecoder
ValidateTx func(sdk.Tx) error
}

func NewExtTxSelector(parent baseapp.TxSelector, txDecoder sdk.TxDecoder, validateTx func(sdk.Tx) error) *ExtTxSelector {
return &ExtTxSelector{
TxSelector: parent,
TxDecoder: txDecoder,
ValidateTx: validateTx,
}
}

func (ts *ExtTxSelector) SelectTxForProposal(ctx context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte, gasWanted uint64) bool {
var err error
if memTx == nil {
memTx, err = ts.TxDecoder(txBz)
if err != nil {
return false
}
}

if err := ts.ValidateTx(memTx); err != nil {
return false
}

// don't pass `memTx` to parent selector so it don't check tx gas wanted against block gas limit,
// it conflicts with the max-tx-gas-wanted logic.
return ts.TxSelector.SelectTxForProposal(ctx, maxTxBytes, maxBlockGas, nil, txBz, gasWanted)
}

type ProposalHandler struct {
TxDecoder sdk.TxDecoder
// Identity is nil if it's not a validator node
Identity age.Identity
blocklist map[string]struct{}
lastBlockList []byte
addressCodec address.Codec
}

func NewProposalHandler(txDecoder sdk.TxDecoder, identity age.Identity, addressCodec address.Codec) *ProposalHandler {
return &ProposalHandler{
TxDecoder: txDecoder,
Identity: identity,
blocklist: make(map[string]struct{}),
addressCodec: addressCodec,
}
}

// SetBlockList don't fail if the identity is not set or the block list is empty.
func (h *ProposalHandler) SetBlockList(blob []byte) error {
if h.Identity == nil {
return nil
}

if bytes.Equal(h.lastBlockList, blob) {
return nil
}
h.lastBlockList = make([]byte, len(blob))
copy(h.lastBlockList, blob)

if len(blob) == 0 {
h.blocklist = make(map[string]struct{})
return nil
}

reader, err := age.Decrypt(bytes.NewBuffer(blob), h.Identity)
if err != nil {
return err
}

data, err := io.ReadAll(reader)
if err != nil {
return err
}

var blocklist BlockList
if err := json.Unmarshal(data, &blocklist); err != nil {
return err
}

// convert to map
m := make(map[string]struct{}, len(blocklist.Addresses))
for _, s := range blocklist.Addresses {
addr, err := h.addressCodec.StringToBytes(s)
if err != nil {
return fmt.Errorf("invalid bech32 address: %s, err: %w", s, err)
}
encoded, err := h.addressCodec.BytesToString(addr)
if err != nil {
return fmt.Errorf("invalid bech32 address: %s, err: %w", s, err)
}
m[encoded] = struct{}{}
}

h.blocklist = m
return nil
}

func (h *ProposalHandler) ValidateTransaction(tx sdk.Tx) error {
sigTx, ok := tx.(signing.SigVerifiableTx)
if !ok {
return fmt.Errorf("tx of type %T does not implement SigVerifiableTx", tx)
}

signers, err := sigTx.GetSigners()
if err != nil {
return err
}
for _, signer := range signers {
encoded, err := h.addressCodec.BytesToString(signer)
if err != nil {
return fmt.Errorf("invalid bech32 address: %s, err: %w", signer, err)
}
if _, ok := h.blocklist[encoded]; ok {
return fmt.Errorf("signer is blocked: %s", encoded)
}
}
return nil
}

func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
return func(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
for _, txBz := range req.Txs {
memTx, err := h.TxDecoder(txBz)
if err != nil {
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}

if err := h.ValidateTransaction(memTx); err != nil {
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}

return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
}

// noneIdentity is a dummy identity which postpone the failure to the decryption time
type noneIdentity struct{}

var _ age.Identity = noneIdentity{}

func (noneIdentity) Unwrap([]*age.Stanza) ([]byte, error) {
return nil, age.ErrIncorrectIdentity
}
Loading

0 comments on commit 3d3cfed

Please sign in to comment.