Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 13 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ import (
ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint"

// "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/common"
cosmoscorevm "github.com/ethereum/go-ethereum/core/vm"
chainante "github.com/pushchain/push-chain-node/app/ante"

Expand Down Expand Up @@ -289,6 +290,8 @@ type ChainApp struct {
appCodec codec.Codec
txConfig client.TxConfig
interfaceRegistry types.InterfaceRegistry
clientCtx client.Context
pendingTxListeners []func(common.Hash)

// keys to access the substores
keys map[string]*storetypes.KVStoreKey
Expand Down Expand Up @@ -793,7 +796,6 @@ func NewChainApp(
app.EVMKeeper,
app.GovKeeper,
app.SlashingKeeper,
app.EvidenceKeeper,
appCodec,
)

Expand Down Expand Up @@ -1550,6 +1552,16 @@ func (app *ChainApp) RegisterNodeService(clientCtx client.Context, cfg config.Co
nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg)
}

// SetClientCtx sets the client context on the app (required by evmserver.Application).
func (app *ChainApp) SetClientCtx(clientCtx client.Context) {
app.clientCtx = clientCtx
}

// RegisterPendingTxListener registers a listener for pending EVM transactions (required by evmserver.Application).
func (app *ChainApp) RegisterPendingTxListener(listener func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, listener)
}

// GetMaccPerms returns a copy of the module account permissions
//
// NOTE: This is solely to be used for testing purposes.
Expand Down
62 changes: 46 additions & 16 deletions app/precompiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"maps"

evidencekeeper "cosmossdk.io/x/evidence/keeper"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/codec"
distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
Expand All @@ -14,7 +14,6 @@ import (
"github.com/cosmos/evm/precompiles/bech32"
cmn "github.com/cosmos/evm/precompiles/common"
distprecompile "github.com/cosmos/evm/precompiles/distribution"
evidenceprecompile "github.com/cosmos/evm/precompiles/evidence"
govprecompile "github.com/cosmos/evm/precompiles/gov"
ics20precompile "github.com/cosmos/evm/precompiles/ics20"
"github.com/cosmos/evm/precompiles/p256"
Expand All @@ -26,11 +25,43 @@ import (
channelkeeper "github.com/cosmos/ibc-go/v10/modules/core/04-channel/keeper"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"

"cosmossdk.io/core/address"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// Optionals define some optional params that can be applied to _some_ precompiles.
type Optionals struct {
AddressCodec address.Codec
ValidatorAddrCodec address.Codec
ConsensusAddrCodec address.Codec
}

func defaultOptionals() Optionals {
return Optionals{
AddressCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
ValidatorAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
ConsensusAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
}
}

type Option func(opts *Optionals)

func WithAddressCodec(c address.Codec) Option {
return func(opts *Optionals) { opts.AddressCodec = c }
}

func WithValidatorAddrCodec(c address.Codec) Option {
return func(opts *Optionals) { opts.ValidatorAddrCodec = c }
}

func WithConsensusAddrCodec(c address.Codec) Option {
return func(opts *Optionals) { opts.ConsensusAddrCodec = c }
}

const bech32PrecompileBaseGas = 6_000

// NewAvailableStaticPrecompiles returns the list of all available static precompiled contracts from EVM.
// NewAvailableStaticPrecompiles returns the list of all available static precompiled contracts.
//
// NOTE: this should only be used during initialization of the Keeper.
func NewAvailableStaticPrecompiles(
Expand All @@ -43,11 +74,16 @@ func NewAvailableStaticPrecompiles(
evmKeeper *evmkeeper.Keeper,
govKeeper govkeeper.Keeper,
slashingKeeper slashingkeeper.Keeper,
evidenceKeeper evidencekeeper.Keeper,
appCodec codec.Codec,
opts ...Option,
) map[common.Address]vm.PrecompiledContract {
options := defaultOptionals()
for _, opt := range opts {
opt(&options)
}

// Clone the mapping from the latest EVM fork.
precompiles := maps.Clone(vm.PrecompiledContractsBerlin)
precompiles := maps.Clone(vm.PrecompiledContractsPrague)

// secp256r1 precompile as per EIP-7212
p256Precompile := &p256.Precompile{}
Expand All @@ -57,24 +93,24 @@ func NewAvailableStaticPrecompiles(
panic(fmt.Errorf("failed to instantiate bech32 precompile: %w", err))
}

stakingPrecompile, err := stakingprecompile.NewPrecompile(stakingKeeper, bankKeeper)
stakingPrecompile, err := stakingprecompile.NewPrecompile(stakingKeeper, options.AddressCodec)
if err != nil {
panic(fmt.Errorf("failed to instantiate staking precompile: %w", err))
}

distributionPrecompile, err := distprecompile.NewPrecompile(
distributionKeeper,
bankKeeper,
stakingKeeper,
evmKeeper,
options.AddressCodec,
)
if err != nil {
panic(fmt.Errorf("failed to instantiate distribution precompile: %w", err))
}

ibcTransferPrecompile, err := ics20precompile.NewPrecompile(
stakingKeeper,
bankKeeper,
stakingKeeper,
transferKeeper,
channelKeeper,
evmKeeper,
Expand All @@ -88,21 +124,16 @@ func NewAvailableStaticPrecompiles(
panic(fmt.Errorf("failed to instantiate bank precompile: %w", err))
}

govPrecompile, err := govprecompile.NewPrecompile(govKeeper, bankKeeper, appCodec)
govPrecompile, err := govprecompile.NewPrecompile(govKeeper, appCodec, options.AddressCodec)
if err != nil {
panic(fmt.Errorf("failed to instantiate gov precompile: %w", err))
}

slashingPrecompile, err := slashingprecompile.NewPrecompile(slashingKeeper, bankKeeper)
slashingPrecompile, err := slashingprecompile.NewPrecompile(slashingKeeper, options.ValidatorAddrCodec, options.ConsensusAddrCodec)
if err != nil {
panic(fmt.Errorf("failed to instantiate slashing precompile: %w", err))
}

evidencePrecompile, err := evidenceprecompile.NewPrecompile(evidenceKeeper, bankKeeper)
if err != nil {
panic(fmt.Errorf("failed to instantiate evidence precompile: %w", err))
}

// Stateless precompiles
precompiles[bech32Precompile.Address()] = bech32Precompile
precompiles[p256Precompile.Address()] = p256Precompile
Expand All @@ -114,7 +145,6 @@ func NewAvailableStaticPrecompiles(
precompiles[bankPrecompile.Address()] = bankPrecompile
precompiles[govPrecompile.Address()] = govPrecompile
precompiles[slashingPrecompile.Address()] = slashingPrecompile
precompiles[evidencePrecompile.Address()] = evidencePrecompile

return precompiles
}
3 changes: 3 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
evmpreinstalls "github.com/pushchain/push-chain-node/app/upgrades/evm-preinstalls"
ethhashfix "github.com/pushchain/push-chain-node/app/upgrades/eth-hash-fix"
evmrpcfix "github.com/pushchain/push-chain-node/app/upgrades/evm-rpc-fix"
evmv040 "github.com/pushchain/push-chain-node/app/upgrades/evm-v0-4-0"
feeabs "github.com/pushchain/push-chain-node/app/upgrades/fee-abs"
gasoracle "github.com/pushchain/push-chain-node/app/upgrades/gas-oracle"
"github.com/pushchain/push-chain-node/app/upgrades/noop"
Expand Down Expand Up @@ -70,6 +71,7 @@ var Upgrades = []upgrades.Upgrade{
removeutxverifier.NewUpgrade(),
tssfundmigrationfixes.NewUpgrade(),
contractauditchanges.NewUpgrade(),
evmv040.NewUpgrade(),
evmparamsmigration.NewUpgrade(),
evmchainidffix.NewUpgrade(),
evmpreinstalls.NewUpgrade(),
Expand All @@ -91,6 +93,7 @@ func (app *ChainApp) RegisterUpgradeHandlers() {
Codec: app.appCodec,
GetStoreKey: app.GetKey,
EVMKeeper: app.EVMKeeper,
Erc20Keeper: &app.Erc20Keeper,
BankKeeper: app.BankKeeper,

// Module keepers
Expand Down
115 changes: 115 additions & 0 deletions app/upgrades/evm-v0-4-0/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package evmv040

import (
"context"
"fmt"

storetypes "cosmossdk.io/store/types"
upgradetypes "cosmossdk.io/x/upgrade/types"

"github.com/ethereum/go-ethereum/common"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
erc20types "github.com/cosmos/evm/x/erc20/types"

"github.com/pushchain/push-chain-node/app/upgrades"
)

// Upgrade for the pushchain/evm dependency bump from v0.3.x to v0.4.0.
//
// Key changes shipped in cosmos/evm v0.4.0:
// - Post-audit security fixes (batches 1–5) applied to EVM state machine and precompiles
// - Enforce single EVM transaction per Cosmos transaction (#294)
// - Evidence precompile removed (#305) — push-chain did not register it; no cleanup needed
// - ERC20 precompile storage format changed: DynamicPrecompiles and NativePrecompiles moved
// from concatenated hex strings under single keys to per-address prefix-store entries.
// - Various bug fixes: revert reason format, address codec, estimate gas, blockHash RPCs, etc.
const UpgradeName = "evm-v0-4-0"

func NewUpgrade() upgrades.Upgrade {
return upgrades.Upgrade{
UpgradeName: UpgradeName,
CreateUpgradeHandler: CreateUpgradeHandler,
StoreUpgrades: storetypes.StoreUpgrades{
Added: []string{},
Deleted: []string{},
},
}
}

func CreateUpgradeHandler(
mm upgrades.ModuleManager,
configurator module.Configurator,
keepers *upgrades.AppKeepers,
) upgradetypes.UpgradeHandler {
return func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
logger := sdkCtx.Logger().With("upgrade", UpgradeName)
logger.Info("Starting upgrade handler")
logger.Info("pushchain/evm v0.3.x → v0.4.0: security audit patches, single-EVM-tx enforcement, ERC20 precompile storage migration")

versionMap, err := mm.RunMigrations(ctx, configurator, fromVM)
if err != nil {
return nil, fmt.Errorf("RunMigrations: %w", err)
}

if err := migrateERC20Precompiles(sdkCtx, keepers); err != nil {
return nil, fmt.Errorf("migrateERC20Precompiles: %w", err)
}

logger.Info("Upgrade complete", "upgrade", UpgradeName)
return versionMap, nil
}
}

// migrateERC20Precompiles migrates DynamicPrecompiles and NativePrecompiles from the
// legacy storage format (concatenated 42-char hex strings under a single key) to the
// new per-address prefix-store format introduced in cosmos/evm v0.4.0.
func migrateERC20Precompiles(ctx sdk.Context, keepers *upgrades.AppKeepers) error {
store := ctx.KVStore(keepers.GetStoreKey(erc20types.StoreKey))
logger := ctx.Logger().With("migration", "erc20-precompiles")

const addressLength = 42

migrations := []struct {
oldKey string
setter func(sdk.Context, common.Address)
description string
}{
{
oldKey: erc20types.CtxKeyDynamicPrecompiles,
setter: keepers.Erc20Keeper.SetDynamicPrecompile,
description: "dynamic precompiles",
},
{
oldKey: erc20types.CtxKeyNativePrecompiles,
setter: keepers.Erc20Keeper.SetNativePrecompile,
description: "native precompiles",
},
}

for _, m := range migrations {
oldData := store.Get([]byte(m.oldKey))
if len(oldData) == 0 {
logger.Info("No legacy data found, skipping", "type", m.description)
continue
}

count := 0
for i := 0; i+addressLength <= len(oldData); i += addressLength {
addr := common.HexToAddress(string(oldData[i : i+addressLength]))
if addr == (common.Address{}) {
logger.Warn("Skipping zero address", "type", m.description, "position", i)
continue
}
m.setter(ctx, addr)
count++
}

store.Delete([]byte(m.oldKey))
logger.Info("Migration complete", "type", m.description, "count", count)
}

return nil
}
2 changes: 2 additions & 0 deletions app/upgrades/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
erc20keeper "github.com/cosmos/evm/x/erc20/keeper"
evmkeeper "github.com/cosmos/evm/x/vm/keeper"
uexecutorkeeper "github.com/pushchain/push-chain-node/x/uexecutor/keeper"
uregistrykeeper "github.com/pushchain/push-chain-node/x/uregistry/keeper"
Expand All @@ -31,6 +32,7 @@ type AppKeepers struct {
CapabilityKeeper *capabilitykeeper.Keeper
IBCKeeper *ibckeeper.Keeper
EVMKeeper *evmkeeper.Keeper
Erc20Keeper *erc20keeper.Keeper
BankKeeper bankkeeper.BaseKeeper

// Module keepers
Expand Down
11 changes: 8 additions & 3 deletions cmd/pchaind/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,19 @@ func initRootCmd(
cfg := sdk.GetConfig()
cfg.Seal()

// pruning.Cmd and snapshot.Cmd still expect servertypes.Application, so wrap newApp.
sdkAppCreator := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application {
return newApp(l, d, w, ao)
}

rootCmd.AddCommand(
genutilcli.InitCmd(chainApp.BasicModuleManager, app.DefaultNodeHome),
genutilcli.Commands(chainApp.TxConfig(), chainApp.BasicModuleManager, app.DefaultNodeHome),
cmtcli.NewCompletionCmd(rootCmd, true),
debug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(newApp, app.DefaultNodeHome),
snapshot.Cmd(newApp),
pruning.Cmd(sdkAppCreator, app.DefaultNodeHome),
snapshot.Cmd(sdkAppCreator),
)

wasmcli.ExtendUnsafeResetAllCmd(rootCmd)
Expand Down Expand Up @@ -226,7 +231,7 @@ func newApp(
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) servertypes.Application {
) cosmosevmserver.Application {
baseappOptions := sdkserver.DefaultBaseappOptions(appOpts)

var wasmOpts []wasmkeeper.Option
Expand Down
Loading
Loading