Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Add e2e tests for permissionless ICS #2223

Merged
merged 2 commits into from
Sep 9, 2024
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
83 changes: 52 additions & 31 deletions tests/e2e/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func (tr *Chain) startChain(
cometmockArg = "false"
}

chainHome := string(action.Chain)
startChainScript := tr.target.GetTestScriptPath(action.IsConsumer, "start-chain.sh")
cmd := tr.target.ExecCommand("/bin/bash",
startChainScript, chainConfig.BinaryName, string(vals),
Expand All @@ -183,6 +184,7 @@ func (tr *Chain) startChain(
// with short timeout_commit (eg. timeout_commit = 1s) some nodes may miss blocks causing the test run to fail
tr.testConfig.tendermintConfigOverride,
cometmockArg,
chainHome,
)

cmdReader, err := cmd.StdoutPipe()
Expand Down Expand Up @@ -311,7 +313,7 @@ func (tr Chain) updateConsumerChain(action UpdateConsumerChainAction, verbose bo
InitializationParameters: &initParams,
PowerShapingParameters: &powerShapingParams,
}
tr.UpdateConsumer(action.Chain, action.From, msg)
tr.UpdateConsumer(action.Chain, action.From, msg, verbose)
}

type CreateConsumerChainAction struct {
Expand All @@ -333,6 +335,13 @@ type CreateConsumerChainAction struct {
// createConsumerChain creates and initializes a consumer chain
func (tr Chain) createConsumerChain(action CreateConsumerChainAction, verbose bool) {
spawnTime := tr.testConfig.containerConfig.Now.Add(time.Duration(action.SpawnTime) * time.Millisecond)
consumerChainCfg := tr.testConfig.chainConfigs[action.ConsumerChain]
providerChainCfg := tr.testConfig.chainConfigs[action.Chain]

if consumerChainCfg.ConsumerId != "" {
log.Fatalf("consumer chain already created for '%s'", action.ConsumerChain)
}

params := ccvtypes.DefaultParams()
initParams := types.ConsumerInitializationParameters{
InitialHeight: action.InitialHeight,
Expand Down Expand Up @@ -360,16 +369,20 @@ func (tr Chain) createConsumerChain(action CreateConsumerChainAction, verbose bo
}

metadata := types.ConsumerMetadata{
Name: "chain name of " + string(action.Chain),
Name: "chain name of " + string(consumerChainCfg.ChainId),
Description: "no description",
Metadata: "no metadata",
}

// create consumer to get a consumer-id
consumerId := tr.CreateConsumer(action.Chain, action.ConsumerChain, action.From, metadata, &initParams, &powerShapingParams)
consumerId := tr.CreateConsumer(providerChainCfg.ChainId, consumerChainCfg.ChainId, action.From, metadata, &initParams, &powerShapingParams)
if verbose {
fmt.Println("Create consumer chain", string(action.ConsumerChain), " with consumer-id", string(consumerId))
fmt.Println("Created consumer chain", string(consumerChainCfg.ChainId), " with consumer-id", string(consumerId))
}

// Set the new created consumer-id on the chain's config
consumerChainCfg.ConsumerId = consumerId
tr.testConfig.chainConfigs[action.ConsumerChain] = consumerChainCfg
}

type SubmitConsumerAdditionProposalAction struct {
Expand All @@ -390,7 +403,7 @@ type SubmitConsumerAdditionProposalAction struct {
AllowInactiveVals bool
}

func (tr Chain) UpdateConsumer(providerChain ChainID, validator ValidatorID, update types.MsgUpdateConsumer) {
func (tr Chain) UpdateConsumer(providerChain ChainID, validator ValidatorID, update types.MsgUpdateConsumer, verbose bool) {
content, err := json.Marshal(update)
if err != nil {
log.Fatal("failed marshalling MsgUpdateConsumer: ", err.Error())
Expand Down Expand Up @@ -419,7 +432,8 @@ func (tr Chain) UpdateConsumer(providerChain ChainID, validator ValidatorID, upd

bz, err = cmd.CombinedOutput()
if err != nil {
log.Fatal("update consumer failed ", "error: ", err, "output: ", string(bz))
fmt.Println("command failed: ", cmd)
log.Fatal("update consumer failed error: %w, output: %s", err, string(bz))
}

// Check transaction
Expand All @@ -432,14 +446,19 @@ func (tr Chain) UpdateConsumer(providerChain ChainID, validator ValidatorID, upd
if txResponse.Code != 0 {
log.Fatalf("sending update-consumer transaction failed with error code %d, Log:'%s'", txResponse.Code, txResponse.RawLog)
}

if verbose {
fmt.Println("running 'update-consumer' returned: ", txResponse)
}

tr.waitBlocks(providerChain, 2, 10*time.Second)
}

// CreateConsumer creates a consumer chain and returns its consumer-id
func (tr Chain) CreateConsumer(providerChain, consumerChain ChainID, validator ValidatorID, metadata types.ConsumerMetadata, initParams *types.ConsumerInitializationParameters, powerShapingParams *types.PowerShapingParameters) ConsumerID {
chainID := string(tr.testConfig.chainConfigs[consumerChain].ChainId)

msg := types.MsgCreateConsumer{
ChainId: chainID,
ChainId: string(consumerChain),
Metadata: metadata,
InitializationParameters: initParams,
PowerShapingParameters: powerShapingParams,
Expand Down Expand Up @@ -522,19 +541,7 @@ func (tr Chain) CreateConsumer(providerChain, consumerChain ChainID, validator V
log.Fatalf("no consumer-id found in consumer creation transaction events for chain '%s'. events: %v", consumerChain, txResponse.Events)
}

cfg, exists := tr.testConfig.chainConfigs[e2e.ChainID(chainID)]
if !exists {
log.Fatal("no chain config found for consumer chain", chainID)
}
if cfg.ConsumerId != "" && cfg.ConsumerId != e2e.ConsumerID(consumerId) {
log.Fatalf("chain '%s'registered already with a different consumer ID '%s'", chainID, consumerId)
}

// Set the new created consumer-id on the chain's config
cfg.ConsumerId = e2e.ConsumerID(consumerId)
tr.testConfig.chainConfigs[e2e.ChainID(chainID)] = cfg

return e2e.ConsumerID(consumerId)
return ConsumerID(consumerId)
}

// submitConsumerAdditionProposal initializes a consumer chain and submits a governance proposal
Expand All @@ -544,6 +551,8 @@ func (tr Chain) submitConsumerAdditionProposal(
) {
params := ccvtypes.DefaultParams()
spawnTime := tr.testConfig.containerConfig.Now.Add(time.Duration(action.SpawnTime) * time.Millisecond)
consumerChainCfg := tr.testConfig.chainConfigs[action.ConsumerChain]
providerChainCfg := tr.testConfig.chainConfigs[action.Chain]

Metadata := types.ConsumerMetadata{
Name: "chain " + string(action.Chain),
Expand All @@ -566,8 +575,11 @@ func (tr Chain) submitConsumerAdditionProposal(
DistributionTransmissionChannel: action.DistributionChannel,
}

consumerId := tr.CreateConsumer(action.Chain, action.ConsumerChain, action.From, Metadata, nil, nil)
consumerId := tr.CreateConsumer(providerChainCfg.ChainId, consumerChainCfg.ChainId, action.From, Metadata, nil, nil)
authority := "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn"
// Set the new created consumer-id on the chain's config
consumerChainCfg.ConsumerId = consumerId
tr.testConfig.chainConfigs[action.ConsumerChain] = consumerChainCfg

// Update consumer to change owner to governance before submitting the proposal
update := &types.MsgUpdateConsumer{
Expand All @@ -585,7 +597,7 @@ func (tr Chain) submitConsumerAdditionProposal(
AllowInactiveVals: action.AllowInactiveVals,
}
update.PowerShapingParameters = &powerShapingParameters
tr.UpdateConsumer(action.Chain, action.From, *update)
tr.UpdateConsumer(action.Chain, action.From, *update, verbose)

// - set PowerShaping params TopN > 0 for consumer chain
update.PowerShapingParameters.Top_N = action.TopN
Expand Down Expand Up @@ -618,10 +630,10 @@ func (tr Chain) submitConsumerAdditionProposal(
tr.testConfig.chainConfigs[action.Chain].BinaryName,
"tx", "gov", "submit-proposal", proposalFile,
`--from`, `validator`+fmt.Sprint(action.From),
`--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId),
`--home`, tr.getValidatorHome(action.Chain, action.From),
`--chain-id`, string(providerChainCfg.ChainId),
`--home`, tr.getValidatorHome(providerChainCfg.ChainId, action.From),
`--gas`, `900000`,
`--node`, tr.getValidatorNode(action.Chain, action.From),
`--node`, tr.getValidatorNode(providerChainCfg.ChainId, action.From),
`--keyring-backend`, `test`,
`-o json`,
`-y`,
Expand Down Expand Up @@ -651,7 +663,7 @@ func (tr Chain) submitConsumerAdditionProposal(
}

// wait for inclusion in a block -> '--broadcast-mode block' is deprecated
tr.waitBlocks(action.Chain, 2, 10*time.Second)
tr.waitBlocks(providerChainCfg.ChainId, 2, 10*time.Second)
}

func (tr Chain) submitConsumerAdditionLegacyProposal(
Expand Down Expand Up @@ -2885,8 +2897,10 @@ func (tr Chain) startConsumerEvidenceDetector(
}

type OptInAction struct {
Chain ChainID
Validator ValidatorID
Chain ChainID
Validator ValidatorID
ExpectError bool
ExpectedError string
}

func (tr Chain) optIn(action OptInAction, verbose bool) {
Expand Down Expand Up @@ -2919,14 +2933,21 @@ func (tr Chain) optIn(action OptInAction, verbose bool) {
}

bz, err := cmd.CombinedOutput()
if err != nil {
if err != nil && !action.ExpectError {
log.Fatal(err, "\n", string(bz))
}

if !tr.testConfig.useCometmock { // error report only works with --gas auto, which does not work with CometMock, so ignore
if action.ExpectError && !tr.testConfig.useCometmock { // error report only works with --gas auto, which does not work with CometMock, so ignore
if err != nil && verbose {
fmt.Printf("got error during opt in | err: %s | output: %s \n", err, string(bz))
}
if err == nil || !strings.Contains(string(bz), action.ExpectedError) {
log.Fatalf("expected error not raised: expected: '%s', got '%s'", action.ExpectedError, (bz))
}

if verbose {
fmt.Printf("got expected error during key assignment | err: %s | output: %s \n", err, string(bz))
}
}

// wait for inclusion in a block -> '--broadcast-mode block' is deprecated
Expand Down
71 changes: 69 additions & 2 deletions tests/e2e/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const (
InactiveValsMintTestCfg TestConfigType = "inactive-vals-mint"
MintTestCfg TestConfigType = "mint"
InactiveValsExtraValsTestCfg TestConfigType = "inactive-vals-extra-vals"
PermissionlessTestCfg TestConfigType = "permissionless-ics"
)

type TestConfig struct {
Expand All @@ -109,6 +110,7 @@ type TestConfig struct {
containerConfig ContainerConfig
validatorConfigs map[ValidatorID]ValidatorConfig
chainConfigs map[ChainID]ChainConfig
consumerChains map[ConsumerID]ChainConfig
providerVersion string
consumerVersion string
// override config.toml parameters
Expand Down Expand Up @@ -210,6 +212,8 @@ func GetTestConfig(cfgType TestConfigType, providerVersion, consumerVersion stri
testCfg = MintTestConfig()
case InactiveValsExtraValsTestCfg:
testCfg = InactiveValsExtraValsTestConfig()
case PermissionlessTestCfg:
testCfg = PermissionlessTestConfig()
default:
panic(fmt.Sprintf("Invalid test config: %s", cfgType))
}
Expand Down Expand Up @@ -594,6 +598,68 @@ func DemocracyTestConfig(allowReward bool) TestConfig {
return tr
}

// PermissionlessTestConfig contains a provider chain and 2 cosumer chains with the same chain identifier
func PermissionlessTestConfig() TestConfig {
tr := TestConfig{
name: string(PermissionlessTestCfg),
containerConfig: e2e.ContainerConfig{
ContainerName: "interchain-security-container",
InstanceName: "interchain-security-instance",
CcvVersion: "1",
Now: time.Now(),
},
validatorConfigs: getDefaultValidators(),
chainConfigs: map[ChainID]e2e.ChainConfig{
"provi": {
ChainId: ChainID("provi"),
AccountPrefix: ProviderAccountPrefix,
BinaryName: "interchain-security-pd",
IpPrefix: "7.7.7",
VotingWaitTime: 20,
GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " +
".app_state.gov.params.expedited_voting_period = \"10s\" | " +
// Custom slashing parameters for testing validator downtime functionality
// See https://docs.cosmos.network/main/modules/slashing/04_begin_block.html#uptime-tracking
".app_state.slashing.params.signed_blocks_window = \"10\" | " +
".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " +
".app_state.slashing.params.downtime_jail_duration = \"60s\" | " +
".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " +
".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling
".app_state.provider.params.slash_meter_replenish_period = \"3s\" | " +
".app_state.provider.params.blocks_per_epoch = 3",
},
"cons1": {
ChainId: ChainID("consu"),
AccountPrefix: ConsumerAccountPrefix,
BinaryName: "interchain-security-cd",
IpPrefix: "7.7.8",
VotingWaitTime: 20,
GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " +
".app_state.slashing.params.signed_blocks_window = \"20\" | " +
".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " +
".app_state.slashing.params.downtime_jail_duration = \"60s\" | " +
".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"",
},
// ChainID needs to be "consu" as previous consumer chain
"cons2": {
ChainId: ChainID("consu"),
AccountPrefix: ConsumerAccountPrefix,
BinaryName: "interchain-security-cd",
IpPrefix: "7.7.9",
VotingWaitTime: 20,
GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " +
".app_state.slashing.params.signed_blocks_window = \"20\" | " +
".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " +
".app_state.slashing.params.downtime_jail_duration = \"60s\" | " +
".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"",
},
},
tendermintConfigOverride: `s/timeout_commit = "5s"/timeout_commit = "1s"/;` +
`s/peer_gossip_sleep_duration = "100ms"/peer_gossip_sleep_duration = "50ms"/;`,
}
tr.Initialize()
return tr
}
func InactiveProviderValsTestConfig() TestConfig {
tr := DefaultTestConfig()
tr.name = "InactiveValsConfig"
Expand Down Expand Up @@ -958,11 +1024,11 @@ func (s *TestConfig) validateStringLiterals() {

for chainID, chainConfig := range s.chainConfigs {
if len(chainID) > 5 {
panic("chain id string literal must be 5 char or less")
panic(fmt.Sprintf("chain id string literal must be 5 char or less: %s", chainID))
}

if chainID != chainConfig.ChainId {
panic("chain config is mapped to a chain id that is different than what's stored in the config")
log.Println("chain config is mapped to a chain id that is different than what's stored in the config")
}
}
}
Expand Down Expand Up @@ -1266,6 +1332,7 @@ func getValidatorConfigFromVersion(providerVersion, consumerVersion string) map[
// If provided version is before v1.6.0 then a configuration based on template for v1.4.x is returned
// otherwise the returned configuration is based on template v1.4.
func GetHermesConfig(hermesVersion, queryNodeIP string, chainCfg ChainConfig, isConsumer bool) string {

ChainId := chainCfg.ChainId
keyName := "query"
rpcAddr := "http://" + queryNodeIP + ":26658"
Expand Down
21 changes: 4 additions & 17 deletions tests/e2e/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,12 @@ var stepChoices = map[string]StepChoice{
description: "test minting without inactive validators as a sanity check",
testConfig: MintTestCfg,
},
// TODO PERMISSIONLESS: ADD NEW E2E TEST
/* "permissionless-ics": {
"permissionless-ics": {
name: "permissionless-ics",
steps: stepsPermissionlessICS(),
description: "test permissionless ics",
testConfig: DefaultTestCfg,
}, */
testConfig: PermissionlessTestCfg,
},
"inactive-vals-outside-max-validators": {
name: "inactive-vals-outside-max-validators",
steps: stepsInactiveValsTopNReproduce(),
Expand Down Expand Up @@ -540,6 +539,7 @@ func printReport(runners []TestRunner, duration time.Duration) {
}
numTotalTests := len(runners)
report := `

=================================================
TEST RESULTS
-------------------------------------------------
Expand Down Expand Up @@ -577,19 +577,6 @@ Summary:
len(remainingTests), numTotalTests,
)

report += fmt.Sprintln("\nFAILED TESTS:")
for _, t := range failedTests {
report += t.Report()
}
report += fmt.Sprintln("\n\nPASSED TESTS:")
for _, t := range passedTests {
report += t.Report()
}

report += fmt.Sprintln("\n\nREMAINING TESTS:")
for _, t := range remainingTests {
report += t.Report()
}
report += "=================================================="
fmt.Print(report)
}
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func (tr Chain) getValidatorIP(chain ChainID, validator ValidatorID) string {
}

func (tr Chain) getValidatorHome(chain ChainID, validator ValidatorID) string {
return `/` + string(tr.testConfig.chainConfigs[chain].ChainId) + `/validator` + fmt.Sprint(validator)
return `/` + string(chain) + `/validator` + fmt.Sprint(validator)
}

func (tr Chain) curlJsonRPCRequest(method, params, address string) {
Expand Down Expand Up @@ -926,7 +926,7 @@ func (tr Commands) GetHasToValidate(
log.Fatal(err, "\n", string(bz))
}

arr := gjson.Get(string(bz), "consumer_chain_ids").Array()
arr := gjson.Get(string(bz), "consumer_ids").Array()
chains := []ChainID{}
for _, c := range arr {
for _, chain := range tr.chainConfigs {
Expand Down
Loading
Loading