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
4 changes: 4 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
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"
evmv050 "github.com/pushchain/push-chain-node/app/upgrades/evm-v0-5-0"
evmderivedgasprice "github.com/pushchain/push-chain-node/app/upgrades/evm-derived-gas-price"
evmv060 "github.com/pushchain/push-chain-node/app/upgrades/evm-v0-6-0"
feeabs "github.com/pushchain/push-chain-node/app/upgrades/fee-abs"
gasoracle "github.com/pushchain/push-chain-node/app/upgrades/gas-oracle"
Expand Down Expand Up @@ -90,6 +91,9 @@ var Upgrades = []upgrades.Upgrade{
pc20.NewUpgrade(),
// sdk-v0.53 — Cosmos SDK v0.50.10 -> v0.53.7; no-op (no module ConsensusVersion changes)
sdkv053.NewUpgrade(),
// evm-derived-gas-price — cosmos/evm bump for the derived-tx gas price fix;
// no-op (JSON-RPC reporting only, no module ConsensusVersion changes)
evmderivedgasprice.NewUpgrade(),
}

// RegisterUpgradeHandlers registers the chain upgrade handlers
Expand Down
45 changes: 45 additions & 0 deletions app/upgrades/evm-derived-gas-price/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package evmderivedgasprice

import (
"context"

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

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"

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

const UpgradeName = "evm-derived-gas-price"

// NewUpgrade constructs the upgrade definition.
//
// This upgrade only bumps the cosmos/evm dependency to pick up the derived-tx
// gas-price reporting fix (push-chain-evm#37). That change is confined to the
// JSON-RPC layer — no module state is touched — so there are no store upgrades
// and the handler just runs the standard module migrations.
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,
ak *upgrades.AppKeepers,
) upgradetypes.UpgradeHandler {
return func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.Logger().Info("🔧 Running upgrade:", "name", UpgradeName)

return mm.RunMigrations(ctx, configurator, fromVM)
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ replace (
cosmossdk.io/x/upgrade => cosmossdk.io/x/upgrade v0.2.0
github.com/CosmWasm/wasmd => github.com/CosmWasm/wasmd v0.55.0 // Keep v0.55.0
github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.53.7 // Use stable v0.53.7
github.com/cosmos/evm => github.com/pushchain/evm v1.0.0-rc2.0.20260627105801-6c22ba0b1e9e
github.com/cosmos/evm => github.com/pushchain/evm v1.0.0-rc2.0.20260803072921-2d248e1881bb
github.com/ethereum/go-ethereum => github.com/cosmos/go-ethereum v0.0.0-20250806193535-2fc7571efa91
github.com/spf13/viper => github.com/spf13/viper v1.17.0
github.com/strangelove-ventures/tokenfactory => github.com/strangelove-ventures/tokenfactory v0.50.7-wasmvm2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1769,8 +1769,8 @@ github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b h1
github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY=
github.com/prysmaticlabs/prysm/v5 v5.3.0 h1:7Lr8ndapBTZg00YE+MgujN6+yvJR6Bdfn28ZDSJ00II=
github.com/prysmaticlabs/prysm/v5 v5.3.0/go.mod h1:r1KhlduqDMIGZ1GhR5pjZ2Ko8Q89noTDYTRoPKwf1+c=
github.com/pushchain/evm v1.0.0-rc2.0.20260627105801-6c22ba0b1e9e h1:KL2DPaFKZ7t7SkXnFQ/V5QEjvPE45Il5AyQvoVWFoMI=
github.com/pushchain/evm v1.0.0-rc2.0.20260627105801-6c22ba0b1e9e/go.mod h1:QuenX5DgRhWeYdIg0J/p65cyS/ntpgnzpZOIajZ/SHk=
github.com/pushchain/evm v1.0.0-rc2.0.20260803072921-2d248e1881bb h1:Ny+O67qLEZpO2UxnPJCpElw5YXfAiWsUcIuJJ1PMayA=
github.com/pushchain/evm v1.0.0-rc2.0.20260803072921-2d248e1881bb/go.mod h1:QuenX5DgRhWeYdIg0J/p65cyS/ntpgnzpZOIajZ/SHk=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
Expand Down
66 changes: 52 additions & 14 deletions universalClient/core/client.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Package core provides the top-level orchestrator for the Push Universal Validator.
// It wires together all subsystems (pushcore, pushsigner, chains, tss, api)
// It wires together all subsystems (pushcore, pushsigner, externalchains, pushwatcher, tss, api)
// and manages their lifecycle.
package core

Expand All @@ -11,12 +11,13 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pushchain/push-chain-node/universalClient/api"
"github.com/pushchain/push-chain-node/universalClient/chains"
"github.com/pushchain/push-chain-node/universalClient/config"
"github.com/pushchain/push-chain-node/universalClient/db"
"github.com/pushchain/push-chain-node/universalClient/externalchains"
"github.com/pushchain/push-chain-node/universalClient/logger"
"github.com/pushchain/push-chain-node/universalClient/pushcore"
"github.com/pushchain/push-chain-node/universalClient/pushsigner"
"github.com/pushchain/push-chain-node/universalClient/pushwatcher"
"github.com/pushchain/push-chain-node/universalClient/tss"
"github.com/rs/zerolog"
)
Expand All @@ -29,7 +30,8 @@ type UniversalClient struct {
queryServer *api.Server
pushCore *pushcore.Client
pushSigner *pushsigner.Signer
chains *chains.Chains
chains *externalchains.Chains
pushChain *pushwatcher.Client
tssNode *tss.Node
}

Expand Down Expand Up @@ -66,9 +68,26 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie
return nil, fmt.Errorf("failed to create pushsigner: %w", err)
}

chainsManager := chains.NewChains(pushCore, pushSigner, cfg, log)
chainsManager := externalchains.NewChains(pushCore, pushSigner, cfg, log)

tssNode, err := initTSS(ctx, cfg, pushCore, chainsManager, pushSigner, log)
// Push chain DB is shared by the push chain client and the TSS node.
pushDB, err := openPushDB(cfg)
if err != nil {
return nil, err
}

pushChain, err := pushwatcher.NewClient(
pushDB,
cfg.GetChainConfig(cfg.PushChainID),
pushCore,
cfg.PushChainID,
log,
)
if err != nil {
return nil, fmt.Errorf("failed to create push chain client: %w", err)
}

tssNode, err := initTSS(ctx, cfg, pushCore, chainsManager, pushSigner, pushDB, log)
if err != nil {
return nil, err
}
Expand All @@ -83,10 +102,26 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie
pushCore: pushCore,
pushSigner: pushSigner,
chains: chainsManager,
pushChain: pushChain,
tssNode: tssNode,
}, nil
}

// openPushDB opens the Push chain database under NodeHome.
func openPushDB(cfg *config.Config) (*db.DB, error) {
if cfg.PushChainID == "" {
return nil, fmt.Errorf("push_chain_id is required")
}
// Sanitize chain ID for use as a database filename (e.g. "push_42101-1" → "push_42101-1.db")
dbFilename := sanitizeForFilename(cfg.PushChainID) + ".db"
baseDir := filepath.Join(cfg.NodeHome, config.DatabasesSubdir)
pushDB, err := db.OpenFileDB(baseDir, dbFilename, true)
if err != nil {
return nil, fmt.Errorf("failed to create push database: %w", err)
}
return pushDB, nil
}

// Start launches all subsystems, blocks until ctx is cancelled, then shuts down.
func (uc *UniversalClient) Start() error {
uc.log.Info().Msg("starting universal client")
Expand All @@ -95,6 +130,10 @@ func (uc *UniversalClient) Start() error {
return fmt.Errorf("failed to start chains manager: %w", err)
}

if err := uc.pushChain.Start(uc.ctx); err != nil {
return fmt.Errorf("failed to start push chain client: %w", err)
}

if uc.tssNode != nil {
if err := uc.tssNode.Start(uc.ctx); err != nil {
return fmt.Errorf("failed to start TSS node: %w", err)
Expand Down Expand Up @@ -127,6 +166,12 @@ func (uc *UniversalClient) shutdown() {
}
}

if uc.pushChain != nil {
if err := uc.pushChain.Stop(); err != nil {
uc.log.Error().Err(err).Str("subsystem", "push_chain").Msg("subsystem failed to stop")
}
}

if uc.chains != nil {
uc.chains.Stop()
}
Expand Down Expand Up @@ -158,22 +203,15 @@ func initTSS(
ctx context.Context,
cfg *config.Config,
pushCore *pushcore.Client,
chainsManager *chains.Chains,
chainsManager *externalchains.Chains,
pushSigner *pushsigner.Signer,
pushDB *db.DB,
log zerolog.Logger,
) (*tss.Node, error) {
if cfg.PushValoperAddress == "" || cfg.TSSP2PPrivateKeyHex == "" {
return nil, nil
}

// Sanitize chain ID for use as a database filename (e.g. "push_42101-1" → "push_42101-1.db")
dbFilename := sanitizeForFilename(cfg.PushChainID) + ".db"
baseDir := filepath.Join(cfg.NodeHome, config.DatabasesSubdir)
pushDB, err := db.OpenFileDB(baseDir, dbFilename, true)
if err != nil {
return nil, fmt.Errorf("failed to create push database: %w", err)
}

node, err := tss.NewNode(ctx, tss.Config{
ValidatorAddress: cfg.PushValoperAddress,
P2PPrivateKeyHex: cfg.TSSP2PPrivateKeyHex,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chains
package externalchains

import (
"context"
Expand All @@ -7,12 +7,11 @@ import (
"sync"
"time"

"github.com/pushchain/push-chain-node/universalClient/chains/common"
"github.com/pushchain/push-chain-node/universalClient/chains/evm"
"github.com/pushchain/push-chain-node/universalClient/chains/push"
"github.com/pushchain/push-chain-node/universalClient/chains/svm"
"github.com/pushchain/push-chain-node/universalClient/config"
"github.com/pushchain/push-chain-node/universalClient/db"
"github.com/pushchain/push-chain-node/universalClient/externalchains/common"
"github.com/pushchain/push-chain-node/universalClient/externalchains/evm"
"github.com/pushchain/push-chain-node/universalClient/externalchains/svm"
"github.com/pushchain/push-chain-node/universalClient/pushcore"
"github.com/pushchain/push-chain-node/universalClient/pushsigner"
uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types"
Expand Down Expand Up @@ -75,14 +74,6 @@ func (c *Chains) Start(ctx context.Context) error {
return fmt.Errorf("pushCore must be non-nil")
}

// Push chain client is a hard requirement: the universal client cannot
// do meaningful work (TSS coordination, signing, validator-set discovery)
// without it, so a startup failure here surfaces immediately rather than
// running degraded and relying on the periodic loop to recover.
if err := c.ensurePushChain(ctx); err != nil {
return fmt.Errorf("failed to attach push chain client: %w", err)
}

c.running = true
c.stopCh = make(chan struct{})
c.wg.Add(1)
Expand Down Expand Up @@ -158,13 +149,10 @@ func (c *Chains) fetchAndUpdate(parent context.Context) error {
return err
}

// Track seen chains (Push chain always marked as seen)
// Track seen chains
seenChains := make(map[string]bool)
if c.pushChainID != "" {
seenChains[c.pushChainID] = true
}

// Process each chain config
// Process each chain config (Push chain is managed by core, not here)
for _, cfg := range cfgs {
chainID := cfg.Chain
if chainID == "" || chainID == c.pushChainID {
Expand Down Expand Up @@ -199,10 +187,10 @@ func (c *Chains) fetchAndUpdate(parent context.Context) error {
}
}

// Remove stale chains (never remove Push chain)
// Remove stale chains
c.chainsMu.RLock()
for chainID := range c.chains {
if chainID != c.pushChainID && !seenChains[chainID] {
if !seenChains[chainID] {
c.logger.Info().Str("chain", chainID).Msg("removing chain no longer in config")
if err := c.removeChain(chainID); err != nil {
c.logger.Error().Err(err).Str("chain", chainID).Msg("failed to remove chain")
Expand Down Expand Up @@ -426,71 +414,6 @@ func (c *Chains) getChainDB(chainID string) (*db.DB, error) {
return database, nil
}

// ensurePushChain ensures the push chain client is always present
func (c *Chains) ensurePushChain(ctx context.Context) error {
if c.pushChainID == "" {
return fmt.Errorf("push chain ID not configured")
}

c.chainsMu.RLock()
_, exists := c.chains[c.pushChainID]
c.chainsMu.RUnlock()

if exists {
return nil // Already exists
}

// Get or create database for push chain
pushDB, err := c.getChainDB(c.pushChainID)
if err != nil {
return fmt.Errorf("failed to get database for push chain: %w", err)
}

// Create a minimal chain config for push chain
// Push chain doesn't need gateway or other configs
pushConfig := &uregistrytypes.ChainConfig{
Chain: c.pushChainID,
// VmType is not set for push chain as it's not a gateway chain
// GatewayAddress is empty for push chain
Enabled: &uregistrytypes.ChainEnabled{
IsInboundEnabled: true,
IsOutboundEnabled: true,
},
}

// Get chain-specific config for push chain
chainConfig := c.config.GetChainConfig(c.pushChainID)

// Create push chain client
client, err := push.NewClient(
pushDB,
chainConfig,
c.pushCore,
c.pushChainID,
c.logger,
)
if err != nil {
return fmt.Errorf("failed to create push chain client: %w", err)
}

// Start the push chain client
if err := client.Start(ctx); err != nil {
return fmt.Errorf("failed to start push chain client: %w", err)
}

// Store the client and config
c.chainsMu.Lock()
c.chains[c.pushChainID] = client
c.chainConfigs[c.pushChainID] = pushConfig
c.chainsMu.Unlock()

c.logger.Info().
Str("chain", c.pushChainID).
Msg("chain client added")

return nil
}

// Helper functions

// sanitizeChainID converts chain ID to filesystem-safe format
Expand Down
Loading
Loading