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
60 changes: 30 additions & 30 deletions spawn/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/rollchains/spawn/simapp"
"github.com/rollchains/spawn/spawn/types"
localictypes "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/types"
"github.com/strangelove-ventures/interchaintest/v8/chain/cosmos"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
Expand Down Expand Up @@ -59,7 +60,12 @@ type NewChainConfig struct {
IgnoreGitInit bool
DisabledModules []string
Logger *slog.Logger
isUsingICS bool
}

// NodeHome returns the full path to the node home directory
// ex: $HOME/.simapp
func (cfg NewChainConfig) NodeHome() string {
return path.Join("$HOME", cfg.HomeDir)
}

func (cfg NewChainConfig) ValidateAndRun(doAnnounce bool) error {
Expand All @@ -84,67 +90,49 @@ func (cfg NewChainConfig) ValidateAndRun(doAnnounce bool) error {
func (cfg *NewChainConfig) SetProperFeaturePairs() {
d := RemoveDuplicates(cfg.DisabledModules)

isUsingICS := true
for _, name := range d {
if AliasName(name) == InterchainSecurity {
isUsingICS = false
}
}
cfg.isUsingICS = isUsingICS

// remove POA if it is being used
if isUsingICS {
if cfg.IsFeatureEnabled(InterchainSecurity) {
d = append(d, POA)
}

cfg.DisabledModules = d
cfg.Logger.Debug("SetProperFeaturePairs Disabled features", "features", cfg.DisabledModules)
}

func (cfg *NewChainConfig) IsFeatureDisabled(featName string) bool {
for _, feat := range cfg.DisabledModules {
if AliasName(feat) == AliasName(featName) {
return true
}
}
return false
}

func (cfg *NewChainConfig) Validate() error {
if cfg.ProjectName == "" {
return ErrCfgEmptyProject
return types.ErrCfgEmptyProject
}

if strings.ContainsAny(cfg.ProjectName, `~!@#$%^&*()_+{}|:"<>?/.,;'[]\=-`) {
return ErrCfgProjSpecialChars
return types.ErrCfgProjSpecialChars
}

if cfg.GithubOrg == "" {
return ErrCfgEmptyOrg
return types.ErrCfgEmptyOrg
}

minDenomLen := 3
if len(cfg.Denom) < minDenomLen {
return ErrExpectedRange(ErrCfgDenomTooShort, minDenomLen, len(cfg.Denom))
return types.ErrExpectedRange(types.ErrCfgDenomTooShort, minDenomLen, len(cfg.Denom))
}

minBinLen := 2
if len(cfg.BinDaemon) < minBinLen {
return ErrExpectedRange(ErrCfgBinTooShort, minBinLen, len(cfg.BinDaemon))
return types.ErrExpectedRange(types.ErrCfgBinTooShort, minBinLen, len(cfg.BinDaemon))
}

if cfg.Bech32Prefix == "" {
return ErrCfgEmptyBech32
return types.ErrCfgEmptyBech32
}

cfg.Bech32Prefix = strings.ToLower(cfg.Bech32Prefix)
if !isAlphaFn(cfg.Bech32Prefix) {
return ErrCfgBech32Alpha
return types.ErrCfgBech32Alpha
}

minHomeLen := 2
if len(cfg.HomeDir) < minHomeLen {
return ErrExpectedRange(ErrCfgHomeDirTooShort, minHomeLen, len(cfg.HomeDir))
return types.ErrExpectedRange(types.ErrCfgHomeDirTooShort, minHomeLen, len(cfg.HomeDir))
}

if cfg.Logger == nil {
Expand Down Expand Up @@ -196,6 +184,8 @@ func (cfg *NewChainConfig) CreateNewChain() error {
}

cfg.MetadataFile().SaveJSON(fmt.Sprintf("%s/chain_metadata.json", NewDirName))
cfg.ChainRegistryFile().SaveJSON(fmt.Sprintf("%s/chain_registry.json", NewDirName))
cfg.ChainRegistryAssetsFile().SaveJSON(fmt.Sprintf("%s/chain_registry_assets.json", NewDirName))

// setup local-interchain testnets
// *testnet.json (chains/ directory)
Expand All @@ -207,7 +197,7 @@ func (cfg *NewChainConfig) CreateNewChain() error {
cfg.GitInitNewProjectRepo()
}

if !cfg.IsFeatureDisabled("block-explorer") {
if cfg.IsFeatureEnabled("block-explorer") {
cfg.NewPingPubExplorer()
}

Expand Down Expand Up @@ -322,7 +312,7 @@ func (cfg *NewChainConfig) SetupLocalInterchainJSON() {
cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", "1"),
}

if cfg.isUsingICS {
if cfg.IsFeatureEnabled(InterchainSecurity) {
c.SetICSConsumerLink("localcosmos-1")
} else {
// make this is an IBC testnet for POA/POS chains
Expand Down Expand Up @@ -393,6 +383,16 @@ func GetFileContent(logger *slog.Logger, newFilePath string, fs embed.FS, relPat
return fc, nil
}

func (cfg *NewChainConfig) IsFeatureEnabled(feat string) bool {
featAlias := AliasName(feat)
for _, disabledFeat := range cfg.DisabledModules {
if AliasName(disabledFeat) == featAlias {
return false
}
}
return true
}

// debugErrorFile saves the errored file to a debug directory for easier debugging.
// Returning the path to the file.
func debugErrorFile(logger *slog.Logger, newDirname string) string {
Expand Down
166 changes: 166 additions & 0 deletions spawn/cfg_registry_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package spawn

import (
"fmt"
"strings"

"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/rollchains/spawn/spawn/types"
)

var caser = cases.Title(language.English)

func (cfg NewChainConfig) ChainRegistryFile() types.ChainRegistryFormat {
// TODO: update as needed
DefaultSDKVersion := "0.50"
DefaultTendermintVersion := "0.38"
DefaultIBCGoVersion := "8"
DefaultCosmWasmVersion := ""
if cfg.IsFeatureEnabled(CosmWasm) {
DefaultCosmWasmVersion = "0.50"
}
DefaultConsensus := "tendermint" // TODO: gordian in the future on gen

return types.ChainRegistryFormat{
Schema: DefaultChainRegistrySchema,
ChainName: cfg.ProjectName,
ChainType: "cosmos",
Status: "live",
Website: DefaultWebsite,
NetworkType: DefaultNetworkType,
PrettyName: caser.String(cfg.ProjectName),
ChainID: DefaultChainID,
Bech32Prefix: cfg.Bech32Prefix,
DaemonName: cfg.BinDaemon,
NodeHome: cfg.NodeHome(),
KeyAlgos: []string{"secp256k1"},
Slip44: DefaultSlip44CoinType,
Fees: types.Fees{
FeeTokens: []types.FeeTokens{
{
Denom: cfg.Denom,
FixedMinGasPrice: 0,
LowGasPrice: 0,
AverageGasPrice: 0.025,
HighGasPrice: 0.04,
},
},
},
Codebase: types.Codebase{
GitRepo: "https://" + cfg.GithubPath(),
RecommendedVersion: "v1.0.0",
CompatibleVersions: []string{"v0.9.0"},
CosmosSdkVersion: DefaultSDKVersion,
Consensus: types.Consensus{
Type: DefaultConsensus,
Version: DefaultTendermintVersion,
},
CosmwasmVersion: DefaultCosmWasmVersion,
CosmwasmEnabled: cfg.IsFeatureEnabled(CosmWasm),
IbcGoVersion: DefaultIBCGoVersion,
IcsEnabled: []string{"ics20-1"},
Genesis: types.Genesis{
Name: "v1",
GenesisURL: fmt.Sprintf("https://%s/%s", cfg.GithubPath(), "networks/raw/main/genesis.json"),
},
Versions: []types.Versions{
{
Name: "v1.0.0",
Tag: "v1.0.0",
Height: 0,
NextVersionName: "v2",
},
},
},
Staking: types.Staking{
StakingTokens: []types.StakingTokens{
{
Denom: cfg.Denom,
},
},
LockDuration: types.LockDuration{
Time: "1814400s", // 21 days
},
},
Images: []types.Images{
{
Png: DefaultLogo,
Theme: types.Theme{
PrimaryColorHex: DefaultThemeHexColor,
},
},
},
Peers: types.Peers{},
Apis: types.Apis{
RPC: []types.RPC{
{
Address: "tcp://127.0.0.1:26657",
Provider: "localhost",
},
},
Rest: []types.Rest{
{
Address: "tcp://127.0.0.1:1317",
Provider: "localhost",
},
},
},
Explorers: []types.Explorers{
{
Kind: "cosmos",
URL: "https://example.com",
TxPage: "https://example.com/tx",
AccountPage: "https://example.com/account",
},
},
Keywords: []string{"cosmos", "spawn"},
}
}

// The ICS MetadataFile is similar to this.
func (cfg NewChainConfig) ChainRegistryAssetsFile() types.ChainRegistryAssetsList {
display := strings.TrimPrefix(strings.ToUpper(cfg.Denom), "U")

return types.ChainRegistryAssetsList{
Schema: DefaultChainRegistryAssetsSchema,
ChainName: cfg.ProjectName,
Assets: []types.Assets{
{
Description: "The native token of " + cfg.ProjectName,
DenomUnits: []types.DenomUnits{
{
Denom: cfg.Denom, // utoken
Exponent: 0,
},
{
Denom: display, // TOKEN
Exponent: 6,
},
},
Base: cfg.Denom, // utoken
Name: fmt.Sprintf("%s %s", cfg.ProjectName, display),
Display: strings.ToLower(display), // token
Symbol: display, // TOKEN
LogoURIs: types.LogoURIs{
Png: DefaultLogo,
Svg: DefaultLogoSVG,
},
Images: []types.ImagesAssetLists{
{
Png: DefaultLogo,
Svg: DefaultLogoSVG,
Theme: types.Theme{
PrimaryColorHex: DefaultThemeHexColor,
},
},
},
Socials: types.Socials{
Website: DefaultWebsite,
Twitter: "https://x.com/cosmoshub",
},
},
},
}
}
34 changes: 22 additions & 12 deletions spawn/cfg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/rollchains/spawn/spawn"
"github.com/rollchains/spawn/spawn/types"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -208,22 +209,22 @@ func goodCfg() spawn.NewChainConfig {
func TestBadConfigInputs(t *testing.T) {
chainCases := []cfgCase{
NewCfgCase("valid config", goodCfg(), nil),
NewCfgCase("no github org", goodCfg().WithOrg(""), spawn.ErrCfgEmptyOrg),
NewCfgCase("no project name", goodCfg().WithProjectName(""), spawn.ErrCfgEmptyProject),
NewCfgCase("project special chars -", goodCfg().WithProjectName("my-project"), spawn.ErrCfgProjSpecialChars),
NewCfgCase("project special chars /", goodCfg().WithProjectName("my/project"), spawn.ErrCfgProjSpecialChars),
NewCfgCase("binary name to short len 1", goodCfg().WithBinDaemon("a"), spawn.ErrCfgBinTooShort),
NewCfgCase("no github org", goodCfg().WithOrg(""), types.ErrCfgEmptyOrg),
NewCfgCase("no project name", goodCfg().WithProjectName(""), types.ErrCfgEmptyProject),
NewCfgCase("project special chars -", goodCfg().WithProjectName("my-project"), types.ErrCfgProjSpecialChars),
NewCfgCase("project special chars /", goodCfg().WithProjectName("my/project"), types.ErrCfgProjSpecialChars),
NewCfgCase("binary name to short len 1", goodCfg().WithBinDaemon("a"), types.ErrCfgBinTooShort),
NewCfgCase("success: binary name len 2", goodCfg().WithBinDaemon("ad"), nil),
NewCfgCase("token denom too short len 1", goodCfg().WithDenom("a"), spawn.ErrCfgDenomTooShort),
NewCfgCase("token denom too short len 2", goodCfg().WithDenom("ab"), spawn.ErrCfgDenomTooShort),
NewCfgCase("token denom too short len 1", goodCfg().WithDenom("a"), types.ErrCfgDenomTooShort),
NewCfgCase("token denom too short len 2", goodCfg().WithDenom("ab"), types.ErrCfgDenomTooShort),
NewCfgCase("success: token denom special chars", goodCfg().WithDenom("my-cool/token"), nil),
NewCfgCase("success: token denom 3", goodCfg().WithDenom("abc"), nil),
NewCfgCase("home dir too short", goodCfg().WithHomeDir("."), spawn.ErrCfgHomeDirTooShort),
NewCfgCase("home dir too short", goodCfg().WithHomeDir("."), types.ErrCfgHomeDirTooShort),
NewCfgCase("success: home dir valid", goodCfg().WithHomeDir(".a"), nil),
NewCfgCase("bech32 prefix to short", goodCfg().WithBech32Prefix(""), spawn.ErrCfgEmptyBech32),
NewCfgCase("bech32 not alpha", goodCfg().WithBech32Prefix("c919"), spawn.ErrCfgBech32Alpha),
NewCfgCase("bech32 not alpha", goodCfg().WithBech32Prefix("1"), spawn.ErrCfgBech32Alpha),
NewCfgCase("bech32 not alpha", goodCfg().WithBech32Prefix("---"), spawn.ErrCfgBech32Alpha),
NewCfgCase("bech32 prefix to short", goodCfg().WithBech32Prefix(""), types.ErrCfgEmptyBech32),
NewCfgCase("bech32 not alpha", goodCfg().WithBech32Prefix("c919"), types.ErrCfgBech32Alpha),
NewCfgCase("bech32 not alpha", goodCfg().WithBech32Prefix("1"), types.ErrCfgBech32Alpha),
NewCfgCase("bech32 not alpha", goodCfg().WithBech32Prefix("---"), types.ErrCfgBech32Alpha),
NewCfgCase("success: bech32 prefix", goodCfg().WithBech32Prefix("c"), nil),
}

Expand All @@ -242,3 +243,12 @@ func TestBadConfigInputs(t *testing.T) {
})
}
}

func TestChainRegistry(t *testing.T) {
cfg := goodCfg()
cr := cfg.ChainRegistryFile()
require.Equal(t, cfg.ProjectName, cr.ChainName)
require.Equal(t, bech, cr.Bech32Prefix)
require.Equal(t, bin, cr.DaemonName)
require.Equal(t, denom, cr.Fees.FeeTokens[0].Denom)
}
Loading