Skip to content

Commit

Permalink
Add e2e tests for permissionless ICS
Browse files Browse the repository at this point in the history
  • Loading branch information
bermuell committed Sep 9, 2024
1 parent 5b6252c commit 2d24a03
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 104 deletions.
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 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

0 comments on commit 2d24a03

Please sign in to comment.