Skip to content
Open
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
18 changes: 18 additions & 0 deletions inprocess/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"

"github.com/sei-protocol/sei-chain/sei-cosmos/client"
"github.com/sei-protocol/sei-chain/sei-cosmos/client/tx"
Expand All @@ -19,6 +20,7 @@ import (
banktypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types"
"github.com/sei-protocol/sei-chain/sei-cosmos/x/genutil"
genutiltypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/genutil/types"
govtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/gov/types"
stakingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/types"
tmtime "github.com/sei-protocol/sei-chain/sei-tendermint/libs/time"
tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types"
Expand All @@ -40,6 +42,9 @@ type genesisBuilder struct {
txConfig client.TxConfig
chainID string
bondDenom string
// govVotingPeriod, when > 0, shortens the gov voting + deposit periods in the base
// genesis so a proposal can pass within a test.
govVotingPeriod time.Duration

accounts []authtypes.GenesisAccount
balances []banktypes.Balance
Expand Down Expand Up @@ -152,6 +157,19 @@ func (b *genesisBuilder) writeBaseGenesis(baseState map[string]json.RawMessage,
bankGenState.Balances = append(bankGenState.Balances, b.balances...)
baseState[banktypes.ModuleName] = b.codec.MustMarshalJSON(&bankGenState)

if b.govVotingPeriod > 0 {
var govGenState govtypes.GenesisState
b.codec.MustUnmarshalJSON(baseState[govtypes.ModuleName], &govGenState)
// Deposit/quorum/threshold keep their defaults — node_admin funds the default
// usei deposit and all validators voting yes clears quorum + threshold. The
// expedited period must stay strictly below the normal one (gov Params
// ValidateBasic rejects >=), so halve it.
govGenState.VotingParams.VotingPeriod = b.govVotingPeriod
govGenState.VotingParams.ExpeditedVotingPeriod = b.govVotingPeriod / 2
govGenState.DepositParams.MaxDepositPeriod = b.govVotingPeriod
baseState[govtypes.ModuleName] = b.codec.MustMarshalJSON(&govGenState)
}

appStateJSON, err := json.MarshalIndent(baseState, "", " ")
if err != nil {
return err
Expand Down
83 changes: 56 additions & 27 deletions inprocess/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ type Options struct {
// dominant cadence lever — lower it (e.g. 500ms) for faster tests.
TimeoutCommit time.Duration

// SnapshotInterval, when > 0, makes each subprocess validator take a cosmos
// state-sync snapshot every N blocks into <home>/snapshots (the location the
// snapshot/statesync suites check and that a late-joining statesync node
// restores from). 0 disables snapshots. Subprocess backend only — the in-process
// backend does not run `seid start` and ignores it. Keep it small (e.g. 10) for
// a fast test so a snapshot lands inside the window.
SnapshotInterval uint64

// GovVotingPeriod, when > 0, shortens the gov voting + deposit periods in genesis
// so a governance proposal can pass within a test. 0 keeps the chain default (far
// too long for a test). Subprocess backend only — used by the upgrade suites,
// which pass a software-upgrade proposal and wait it out. It is coupled to the
// proposal's target height: the height must be far enough ahead that the proposal
// passes before the chain reaches it (see TestSubprocessUpgrade).
GovVotingPeriod time.Duration

// ExtraKeys are non-validator genesis accounts to create + fund. Each key is
// written into its target node's home `test` keyring (so a host `seid --home
// <home> --keyring-backend test` resolves it) and funded at genesis. This is
Expand Down Expand Up @@ -192,31 +208,58 @@ func Start(ctx context.Context, opts Options) (_ *Network, retErr error) {
}
}()

enc, err := net.provision()
if err != nil {
return nil, err
}

// One network per process (see networkStarted): claim the slot here, right
// before the first app.New (in startNode) — the first point that touches the
// process-global EVM singletons. Every step above (base dir, provisioning,
// genesis) can fail recoverably without burning the slot. Once claimed it is
// never released: app.New's singletons can't re-init.
if !networkStarted.CompareAndSwap(false, true) {
return nil, fmt.Errorf("inprocess: a network was already started in this process; only one is supported (EVM worker pool / metrics printer / Prometheus registries are process-global singletons)")
}
for _, n := range net.nodes {
if err := net.startNode(ctx, n, enc); err != nil {
return nil, fmt.Errorf("start %s: %w", n.moniker, err)
}
}
return net, nil
}

// provision lays every node's home down on disk — per-node keys, isolated config,
// keyring, the empty-valset genesis, and the gentx-derived peer mesh — the shared
// foundation both backends build on: the in-process backend then starts each node
// in-goroutine (startNode), the subprocess backend writes the remaining on-disk
// config and runs `seid start` (see subprocess.go). It returns the encoding config
// the caller needs to start the nodes.
func (net *Network) provision() (encoding, error) {
enc := app.MakeEncodingConfig()
gb := &genesisBuilder{
codec: enc.Marshaler,
txConfig: enc.TxConfig,
chainID: opts.ChainID,
bondDenom: sdk.DefaultBondDenom,
codec: enc.Marshaler,
txConfig: enc.TxConfig,
chainID: net.opts.ChainID,
bondDenom: sdk.DefaultBondDenom,
govVotingPeriod: net.opts.GovVotingPeriod,
}

if err := net.provisionNodes(enc, gb); err != nil {
return nil, err
return enc, err
}
if err := net.provisionExtraKeys(gb); err != nil {
return nil, err
return enc, err
}

baseState := app.ModuleBasics.DefaultGenesis(enc.Marshaler)
genFiles := make([]string, len(net.nodes))
for i, n := range net.nodes {
genFiles[i] = n.tmCfg.GenesisFile()
}
if err := gb.writeBaseGenesis(baseState, genFiles); err != nil {
return nil, fmt.Errorf("write base genesis: %w", err)
return enc, fmt.Errorf("write base genesis: %w", err)
}
if err := gb.collectGentxs(net.nodes, filepath.Join(baseDir, "gentxs")); err != nil {
return nil, fmt.Errorf("collect gentxs: %w", err)
if err := gb.collectGentxs(net.nodes, filepath.Join(net.baseDir, "gentxs")); err != nil {
return enc, fmt.Errorf("collect gentxs: %w", err)
}
// gentx-derived peer mesh guard: collectGentxs is what populates each node's
// PersistentPeers (in place, via GenAppStateFromConfig — see doc.go). For N>=2
Expand All @@ -225,28 +268,14 @@ func Start(ctx context.Context, opts Options) (_ *Network, retErr error) {
if len(net.nodes) >= 2 {
for _, n := range net.nodes {
if n.tmCfg.P2P.PersistentPeers == "" {
return nil, fmt.Errorf(
return enc, fmt.Errorf(
"inprocess: gentx-derived peer mesh not wired: collectGentxs did not populate PersistentPeers for %s — did a refactor clone or reorder the config?",
n.moniker,
)
}
}
}

// One network per process (see networkStarted): claim the slot here, right
// before the first app.New (in startNode) — the first point that touches the
// process-global EVM singletons. Every step above (base dir, provisioning,
// genesis) can fail recoverably without burning the slot. Once claimed it is
// never released: app.New's singletons can't re-init.
if !networkStarted.CompareAndSwap(false, true) {
return nil, fmt.Errorf("inprocess: a network was already started in this process; only one is supported (EVM worker pool / metrics printer / Prometheus registries are process-global singletons)")
}
for _, n := range net.nodes {
if err := net.startNode(ctx, n, enc); err != nil {
return nil, fmt.Errorf("start %s: %w", n.moniker, err)
}
}
return net, nil
return enc, nil
}

// provisionNodes runs the first pass: per-node keys, node IDs, gentxs, isolated
Expand Down
1 change: 1 addition & 0 deletions inprocess/readiness.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func latestHeight(ctx context.Context, hc *http.Client, tmRPC string) (int64, bo

type syncInfo struct {
LatestBlockHeight string `json:"latest_block_height"`
LatestBlockHash string `json:"latest_block_hash"`
}

// getJSON performs one request and returns the body on HTTP 200, else ok=false
Expand Down
Loading
Loading