From 81c8a9220e040e65f6efaa92acb6e24efc4aab4b Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:30:53 +0200 Subject: [PATCH] test: adapt e2e compatibility tests v5.x (#1828) * refactor e2e * e2e tests infra for compatibility testing * fix compatibility tests * adapt nightly runs * cleanup * addressed comments --- .github/workflows/nightly-e2e.yml | 4 +- tests/e2e/actions.go | 608 +++++++++---------- tests/e2e/actions_consumer_misbehaviour.go | 24 +- tests/e2e/actions_sovereign_chain.go | 37 +- tests/e2e/config.go | 144 ++--- tests/e2e/json_marshal_test.go | 3 +- tests/e2e/json_utils.go | 103 ---- tests/e2e/state.go | 626 +++++++++----------- tests/e2e/steps_compatibility.go | 4 +- tests/e2e/test_driver.go | 141 +++-- tests/e2e/test_target.go | 8 +- tests/e2e/testlib/types.go | 339 +++++++++++ tests/e2e/testlib/utils.go | 43 ++ tests/e2e/v4/state.go | 654 +++++++++++++++++++++ 14 files changed, 1782 insertions(+), 956 deletions(-) create mode 100644 tests/e2e/testlib/types.go create mode 100644 tests/e2e/testlib/utils.go create mode 100644 tests/e2e/v4/state.go diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 3e8cc7d614..ca560550ca 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -18,7 +18,7 @@ on: jobs: compatibility-test: runs-on: ubuntu-latest - timeout-minutes: 180 + timeout-minutes: 200 steps: - uses: actions/setup-go@v5 with: @@ -36,7 +36,7 @@ jobs: # Run compatibility tests for different consumer (-cv) and provider (-pv) versions. # Combination of all provider versions with consumer versions are tested. # For new versions to be tested add/modify -pc/-cv parameters. - run: go run ./tests/e2e/... --tc compatibility -pv latest -pv v4.0.0 -pv v3.3.3-lsm -cv latest -cv v4.0.0 -cv v3.3.0 -cv v3.2.0 + run: go run ./tests/e2e/... --tc compatibility -pv latest -pv v5.0.0-rc0 -pv v4.1.1-lsm -pv v3.3.3-lsm -cv latest -cv v5.0.0-rc0 -cv v4.1.1 -cv v3.3.0 -cv v3.2.0 happy-path-test: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index ca185aadea..8821aa40e5 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -19,6 +19,7 @@ import ( "github.com/tidwall/gjson" "golang.org/x/mod/semver" + e2e "github.com/cosmos/interchain-security/v5/tests/e2e/testlib" "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" @@ -40,13 +41,12 @@ type SendTokensAction struct { Amount uint } -func (tr TestConfig) sendTokens( +func (tr Chain) sendTokens( action SendTokensAction, - target ExecutionTarget, verbose bool, ) { - fromValCfg := tr.validatorConfigs[action.From] - toValCfg := tr.validatorConfigs[action.To] + fromValCfg := tr.testConfig.validatorConfigs[action.From] + toValCfg := tr.testConfig.validatorConfigs[action.To] fromAddress := fromValCfg.DelAddress toAddress := toValCfg.DelAddress if action.Chain != ChainID("provi") { @@ -65,15 +65,15 @@ func (tr TestConfig) sendTokens( } } - binaryName := tr.chainConfigs[action.Chain].BinaryName - cmd := target.ExecCommand(binaryName, + binaryName := tr.testConfig.chainConfigs[action.Chain].BinaryName + cmd := tr.target.ExecCommand(binaryName, "tx", "bank", "send", fromAddress, toAddress, fmt.Sprint(action.Amount)+`stake`, - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), `--node`, tr.getValidatorNode(action.Chain, action.From), `--keyring-backend`, `test`, @@ -105,12 +105,11 @@ type StartChainValidator struct { Stake uint } -func (tr *TestConfig) startChain( +func (tr *Chain) startChain( action StartChainAction, - target ExecutionTarget, verbose bool, ) { - chainConfig := tr.chainConfigs[action.Chain] + chainConfig := tr.testConfig.chainConfigs[action.Chain] type jsonValAttrs struct { Mnemonic string `json:"mnemonic"` Allocation string `json:"allocation"` @@ -128,18 +127,18 @@ func (tr *TestConfig) startChain( var validators []jsonValAttrs for _, val := range action.Validators { validators = append(validators, jsonValAttrs{ - Mnemonic: tr.validatorConfigs[val.Id].Mnemonic, - NodeKey: tr.validatorConfigs[val.Id].NodeKey, + Mnemonic: tr.testConfig.validatorConfigs[val.Id].Mnemonic, + NodeKey: tr.testConfig.validatorConfigs[val.Id].NodeKey, ValId: fmt.Sprint(val.Id), - PrivValidatorKey: tr.validatorConfigs[val.Id].PrivValidatorKey, + PrivValidatorKey: tr.testConfig.validatorConfigs[val.Id].PrivValidatorKey, Allocation: fmt.Sprint(val.Allocation) + "stake", Stake: fmt.Sprint(val.Stake) + "stake", - IpSuffix: tr.validatorConfigs[val.Id].IpSuffix, + IpSuffix: tr.testConfig.validatorConfigs[val.Id].IpSuffix, - ConsumerMnemonic: tr.validatorConfigs[val.Id].ConsumerMnemonic, - ConsumerPrivValidatorKey: tr.validatorConfigs[val.Id].ConsumerPrivValidatorKey, + ConsumerMnemonic: tr.testConfig.validatorConfigs[val.Id].ConsumerMnemonic, + ConsumerPrivValidatorKey: tr.testConfig.validatorConfigs[val.Id].ConsumerPrivValidatorKey, // if true node will be started with consumer key for each consumer chain - StartWithConsumerKey: tr.validatorConfigs[val.Id].UseConsumerKey, + StartWithConsumerKey: tr.testConfig.validatorConfigs[val.Id].UseConsumerKey, }) } @@ -157,14 +156,14 @@ func (tr *TestConfig) startChain( } var cometmockArg string - if tr.useCometmock { + if tr.testConfig.useCometmock { cometmockArg = "true" } else { cometmockArg = "false" } - startChainScript := target.GetTestScriptPath(action.IsConsumer, "start-chain.sh") - cmd := target.ExecCommand("/bin/bash", + startChainScript := tr.target.GetTestScriptPath(action.IsConsumer, "start-chain.sh") + cmd := tr.target.ExecCommand("/bin/bash", startChainScript, chainConfig.BinaryName, string(vals), string(chainConfig.ChainId), chainConfig.IpPrefix, genesisChanges, fmt.Sprint(action.IsConsumer), @@ -172,7 +171,7 @@ func (tr *TestConfig) startChain( // usually timeout_commit and peer_gossip_sleep_duration are changed to vary the test run duration // lower timeout_commit means the blocks are produced faster making the test run shorter // with short timeout_commit (eg. timeout_commit = 1s) some nodes may miss blocks causing the test run to fail - tr.tendermintConfigOverride, + tr.testConfig.tendermintConfigOverride, cometmockArg, ) @@ -205,14 +204,14 @@ func (tr *TestConfig) startChain( Chain: action.Chain, Validator: action.Validators[0].Id, IsConsumer: action.IsConsumer, - }, target, verbose) + }, verbose) // store the fact that we started the chain - tr.runningChains[action.Chain] = true + tr.testConfig.runningChains[action.Chain] = true fmt.Println("Started chain", action.Chain) - if tr.timeOffset != 0 { + if tr.testConfig.timeOffset != 0 { // advance time for this chain so that it is in sync with the rest of the network - tr.AdvanceTimeForChain(action.Chain, tr.timeOffset) + tr.AdvanceTimeForChain(action.Chain, tr.testConfig.timeOffset) } } @@ -224,20 +223,19 @@ type SubmitTextProposalAction struct { Description string } -func (tr TestConfig) submitTextProposal( +func (tr Chain) submitTextProposal( action SubmitTextProposalAction, - target ExecutionTarget, verbose bool, ) { // TEXT PROPOSAL - bz, err := target.ExecCommand( - tr.chainConfigs[action.Chain].BinaryName, + bz, err := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "gov", "submit-legacy-proposal", `--title`, action.Title, `--description`, action.Description, `--deposit`, fmt.Sprint(action.Deposit)+`stake`, `--from`, `validator`+fmt.Sprint(action.From), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), `--node`, tr.getValidatorNode(action.Chain, action.From), `--keyring-backend`, `test`, @@ -262,17 +260,16 @@ type SubmitConsumerAdditionProposalAction struct { DistributionChannel string } -func (tr TestConfig) submitConsumerAdditionProposal( +func (tr Chain) submitConsumerAdditionProposal( action SubmitConsumerAdditionProposalAction, - target ExecutionTarget, verbose bool, ) { - spawnTime := tr.containerConfig.Now.Add(time.Duration(action.SpawnTime) * time.Millisecond) + spawnTime := tr.testConfig.containerConfig.Now.Add(time.Duration(action.SpawnTime) * time.Millisecond) params := ccvtypes.DefaultParams() prop := client.ConsumerAdditionProposalJSON{ Title: "Propose the addition of a new chain", Summary: "Gonna be a great chain", - ChainId: string(tr.chainConfigs[action.ConsumerChain].ChainId), + ChainId: string(tr.testConfig.chainConfigs[action.ConsumerChain].ChainId), InitialHeight: action.InitialHeight, GenesisHash: []byte("gen_hash"), BinaryHash: []byte("bin_hash"), @@ -298,7 +295,7 @@ func (tr TestConfig) submitConsumerAdditionProposal( } //#nosec G204 -- bypass unsafe quoting warning (no production code) - bz, err = target.ExecCommand( + bz, err = tr.target.ExecCommand( "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/temp-proposal.json"), ).CombinedOutput() if err != nil { @@ -306,11 +303,11 @@ func (tr TestConfig) submitConsumerAdditionProposal( } // CONSUMER ADDITION PROPOSAL - cmd := target.ExecCommand( - tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "gov", "submit-legacy-proposal", "consumer-addition", "/temp-proposal.json", `--from`, `validator`+fmt.Sprint(action.From), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), `--gas`, `900000`, `--node`, tr.getValidatorNode(action.Chain, action.From), @@ -344,16 +341,15 @@ type SubmitConsumerRemovalProposalAction struct { StopTimeOffset time.Duration // offset from time.Now() } -func (tr TestConfig) submitConsumerRemovalProposal( +func (tr Chain) submitConsumerRemovalProposal( action SubmitConsumerRemovalProposalAction, - target ExecutionTarget, verbose bool, ) { - stopTime := tr.containerConfig.Now.Add(action.StopTimeOffset) + stopTime := tr.testConfig.containerConfig.Now.Add(action.StopTimeOffset) prop := client.ConsumerRemovalProposalJSON{ Title: fmt.Sprintf("Stop the %v chain", action.ConsumerChain), Summary: "It was a great chain", - ChainId: string(tr.chainConfigs[action.ConsumerChain].ChainId), + ChainId: string(tr.testConfig.chainConfigs[action.ConsumerChain].ChainId), StopTime: stopTime, Deposit: fmt.Sprint(action.Deposit) + `stake`, } @@ -368,18 +364,18 @@ func (tr TestConfig) submitConsumerRemovalProposal( log.Fatal("prop json contains single quote") } - bz, err = target.ExecCommand( + bz, err = tr.target.ExecCommand( "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/temp-proposal.json")).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } - bz, err = target.ExecCommand( - tr.chainConfigs[action.Chain].BinaryName, + bz, err = tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "gov", "submit-legacy-proposal", "consumer-removal", "/temp-proposal.json", `--from`, `validator`+fmt.Sprint(action.From), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), `--node`, tr.getValidatorNode(action.Chain, action.From), `--gas`, "900000", @@ -401,9 +397,8 @@ type SubmitEnableTransfersProposalAction struct { Deposit uint } -func (tr TestConfig) submitEnableTransfersProposalAction( +func (tr Chain) submitEnableTransfersProposalAction( action SubmitEnableTransfersProposalAction, - target ExecutionTarget, verbose bool, ) { // gov signed addres got by checking the gov module acc address in the test container @@ -430,18 +425,18 @@ func (tr TestConfig) submitEnableTransfersProposalAction( jsonStr := fmt.Sprintf(template, action.Deposit, action.Title) //#nosec G204 -- bypass unsafe quoting warning (no production code) - bz, err := target.ExecCommand( + bz, err := tr.target.ExecCommand( "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/params-proposal.json"), ).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } - cmd := target.ExecCommand( - tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "gov", "submit-proposal", "/params-proposal.json", `--from`, `validator`+fmt.Sprint(action.From), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), `--node`, tr.getValidatorNode(action.Chain, action.From), `--gas`, "900000", @@ -465,9 +460,8 @@ type VoteGovProposalAction struct { PropNumber uint } -func (tr *TestConfig) voteGovProposal( +func (tr *Chain) voteGovProposal( action VoteGovProposalAction, - target ExecutionTarget, verbose bool, ) { var wg sync.WaitGroup @@ -476,14 +470,14 @@ func (tr *TestConfig) voteGovProposal( vote := action.Vote[i] go func(val ValidatorID, vote string) { defer wg.Done() - bz, err := target.ExecCommand( - tr.chainConfigs[action.Chain].BinaryName, + bz, err := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "gov", "vote", fmt.Sprint(action.PropNumber), vote, `--from`, `validator`+fmt.Sprint(val), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, val), `--node`, tr.getValidatorNode(action.Chain, val), `--keyring-backend`, `test`, @@ -499,7 +493,7 @@ func (tr *TestConfig) voteGovProposal( wg.Wait() // wait for inclusion in a block -> '--broadcast-mode block' is deprecated tr.waitBlocks(action.Chain, 1, 10*time.Second) - tr.WaitTime(time.Duration(tr.chainConfigs[action.Chain].VotingWaitTime) * time.Second) + tr.WaitTime(time.Duration(tr.testConfig.chainConfigs[action.Chain].VotingWaitTime) * time.Second) } type StartConsumerChainAction struct { @@ -509,14 +503,13 @@ type StartConsumerChainAction struct { GenesisChanges string } -func (tr *TestConfig) startConsumerChain( +func (tr *Chain) startConsumerChain( action StartConsumerChainAction, - target ExecutionTarget, verbose bool, ) { fmt.Println("Starting consumer chain ", action.ConsumerChain) - consumerGenesis := ".app_state.ccvconsumer = " + tr.getConsumerGenesis(action.ProviderChain, action.ConsumerChain, target) - consumerGenesisChanges := tr.chainConfigs[action.ConsumerChain].GenesisChanges + consumerGenesis := ".app_state.ccvconsumer = " + tr.getConsumerGenesis(action.ProviderChain, action.ConsumerChain) + consumerGenesisChanges := tr.testConfig.chainConfigs[action.ConsumerChain].GenesisChanges if consumerGenesisChanges != "" { consumerGenesis = consumerGenesis + " | " + consumerGenesisChanges + " | " + action.GenesisChanges } @@ -526,21 +519,21 @@ func (tr *TestConfig) startConsumerChain( Validators: action.Validators, GenesisChanges: consumerGenesis, IsConsumer: true, - }, target, verbose) + }, verbose) } // Get consumer genesis from provider -func (tr *TestConfig) getConsumerGenesis(providerChain, consumerChain ChainID, target ExecutionTarget) string { +func (tr *Chain) getConsumerGenesis(providerChain, consumerChain ChainID) string { fmt.Println("Exporting consumer genesis from provider") - providerBinaryName := tr.chainConfigs[providerChain].BinaryName + providerBinaryName := tr.testConfig.chainConfigs[providerChain].BinaryName - cmd := target.ExecCommand( + cmd := tr.target.ExecCommand( providerBinaryName, "query", "provider", "consumer-genesis", - string(tr.chainConfigs[consumerChain].ChainId), + string(tr.testConfig.chainConfigs[consumerChain].ChainId), - `--node`, tr.getQueryNode(providerChain), + `--node`, tr.target.GetQueryNode(providerChain), `-o`, `json`, ) @@ -549,8 +542,8 @@ func (tr *TestConfig) getConsumerGenesis(providerChain, consumerChain ChainID, t log.Fatal(err, "\n", string(bz)) } - if tr.transformGenesis || needsGenesisTransform(target.GetTargetConfig()) { - return string(tr.transformConsumerGenesis(consumerChain, bz, target)) + if tr.testConfig.transformGenesis || needsGenesisTransform(tr.testConfig) { + return string(tr.transformConsumerGenesis(consumerChain, bz)) } else { fmt.Println("No genesis transformation performed") } @@ -558,7 +551,7 @@ func (tr *TestConfig) getConsumerGenesis(providerChain, consumerChain ChainID, t } // needsGenesisTransform tries to identify if a genesis transformation should be performed -func needsGenesisTransform(cfg TargetConfig) bool { +func needsGenesisTransform(cfg TestConfig) bool { // no genesis transformation needed for same versions if cfg.consumerVersion == cfg.providerVersion { return false @@ -663,7 +656,7 @@ func getTransformParameter(consumerVersion string) (string, error) { } // Transform consumer genesis content from older version -func (tr *TestConfig) transformConsumerGenesis(consumerChain ChainID, genesis []byte, target ExecutionTarget) []byte { +func (tr *Chain) transformConsumerGenesis(consumerChain ChainID, genesis []byte) []byte { fmt.Println("Transforming consumer genesis") fileName := "consumer_genesis.json" @@ -677,7 +670,7 @@ func (tr *TestConfig) transformConsumerGenesis(consumerChain ChainID, genesis [] log.Panicf("Failed writing consumer genesis to file: %v", err) } - containerInstance := tr.containerConfig.InstanceName + containerInstance := tr.testConfig.containerConfig.InstanceName targetFile := fmt.Sprintf("/tmp/%s", fileName) sourceFile := file.Name() //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. @@ -689,20 +682,19 @@ func (tr *TestConfig) transformConsumerGenesis(consumerChain ChainID, genesis [] } // check if genesis transform supports --to target - bz, err := target.ExecCommand( + bz, err := tr.target.ExecCommand( "interchain-security-transformer", "genesis", "transform", "--to").CombinedOutput() if err != nil && !strings.Contains(string(bz), "unknown flag: --to") { - cfg := target.GetTargetConfig() - targetVersion, err := getTransformParameter(cfg.consumerVersion) + targetVersion, err := getTransformParameter(tr.testConfig.consumerVersion) if err != nil { log.Panic("Failed getting genesis transformation parameter: ", err) } - cmd = target.ExecCommand( + cmd = tr.target.ExecCommand( "interchain-security-transformer", "genesis", "transform", targetVersion, targetFile) } else { - cmd = target.ExecCommand( + cmd = tr.target.ExecCommand( "interchain-security-transformer", "genesis", "transform", targetFile) } @@ -721,20 +713,19 @@ type ChangeoverChainAction struct { GenesisChanges string } -func (tr TestConfig) changeoverChain( +func (tr Chain) changeoverChain( action ChangeoverChainAction, - target ExecutionTarget, verbose bool, ) { // sleep until the consumer chain genesis is ready on consumer time.Sleep(5 * time.Second) - cmd := target.ExecCommand( - tr.chainConfigs[action.ProviderChain].BinaryName, + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.ProviderChain].BinaryName, "query", "provider", "consumer-genesis", - string(tr.chainConfigs[action.SovereignChain].ChainId), + string(tr.testConfig.chainConfigs[action.SovereignChain].ChainId), - `--node`, tr.getQueryNode(action.ProviderChain), + `--node`, tr.target.GetQueryNode(action.ProviderChain), `-o`, `json`, ) @@ -748,7 +739,7 @@ func (tr TestConfig) changeoverChain( } consumerGenesis := ".app_state.ccvconsumer = " + string(bz) - consumerGenesisChanges := tr.chainConfigs[action.SovereignChain].GenesisChanges + consumerGenesisChanges := tr.testConfig.chainConfigs[action.SovereignChain].GenesisChanges if consumerGenesisChanges != "" { consumerGenesis = consumerGenesis + " | " + consumerGenesisChanges + " | " + action.GenesisChanges } @@ -756,15 +747,14 @@ func (tr TestConfig) changeoverChain( tr.startChangeover(ChangeoverChainAction{ Validators: action.Validators, GenesisChanges: consumerGenesis, - }, target, verbose) + }, verbose) } -func (tr TestConfig) startChangeover( +func (tr Chain) startChangeover( action ChangeoverChainAction, - target ExecutionTarget, verbose bool, ) { - chainConfig := tr.chainConfigs[ChainID("sover")] + chainConfig := tr.testConfig.chainConfigs[ChainID("sover")] type jsonValAttrs struct { Mnemonic string `json:"mnemonic"` Allocation string `json:"allocation"` @@ -782,18 +772,18 @@ func (tr TestConfig) startChangeover( var validators []jsonValAttrs for _, val := range action.Validators { validators = append(validators, jsonValAttrs{ - Mnemonic: tr.validatorConfigs[val.Id].Mnemonic, - NodeKey: tr.validatorConfigs[val.Id].NodeKey, + Mnemonic: tr.testConfig.validatorConfigs[val.Id].Mnemonic, + NodeKey: tr.testConfig.validatorConfigs[val.Id].NodeKey, ValId: fmt.Sprint(val.Id), - PrivValidatorKey: tr.validatorConfigs[val.Id].PrivValidatorKey, + PrivValidatorKey: tr.testConfig.validatorConfigs[val.Id].PrivValidatorKey, Allocation: fmt.Sprint(val.Allocation) + "stake", Stake: fmt.Sprint(val.Stake) + "stake", - IpSuffix: tr.validatorConfigs[val.Id].IpSuffix, + IpSuffix: tr.testConfig.validatorConfigs[val.Id].IpSuffix, - ConsumerMnemonic: tr.validatorConfigs[val.Id].ConsumerMnemonic, - ConsumerPrivValidatorKey: tr.validatorConfigs[val.Id].ConsumerPrivValidatorKey, + ConsumerMnemonic: tr.testConfig.validatorConfigs[val.Id].ConsumerMnemonic, + ConsumerPrivValidatorKey: tr.testConfig.validatorConfigs[val.Id].ConsumerPrivValidatorKey, // if true node will be started with consumer key for each consumer chain - StartWithConsumerKey: tr.validatorConfigs[val.Id].UseConsumerKey, + StartWithConsumerKey: tr.testConfig.validatorConfigs[val.Id].UseConsumerKey, }) } @@ -811,12 +801,12 @@ func (tr TestConfig) startChangeover( } isConsumer := true - changeoverScript := target.GetTestScriptPath(isConsumer, "start-changeover.sh") - cmd := target.ExecCommand( + changeoverScript := tr.target.GetTestScriptPath(isConsumer, "start-changeover.sh") + cmd := tr.target.ExecCommand( "/bin/bash", changeoverScript, chainConfig.UpgradeBinary, string(vals), "sover", chainConfig.IpPrefix, genesisChanges, - tr.tendermintConfigOverride, + tr.testConfig.tendermintConfigOverride, ) cmdReader, err := cmd.StdoutPipe() @@ -873,34 +863,32 @@ const gorelayerChainConfigTemplate = ` } }` -func (tr TestConfig) addChainToRelayer( +func (tr Chain) addChainToRelayer( action AddChainToRelayerAction, - target ExecutionTarget, verbose bool, ) { - if !tr.useGorelayer { - tr.addChainToHermes(action, target, verbose) + if !tr.testConfig.useGorelayer { + tr.addChainToHermes(action, verbose) } else { - tr.addChainToGorelayer(action, target, verbose) + tr.addChainToGorelayer(action, verbose) } } -func (tr TestConfig) addChainToGorelayer( +func (tr Chain) addChainToGorelayer( action AddChainToRelayerAction, - target ExecutionTarget, verbose bool, ) { - queryNodeIP := tr.getQueryNodeIP(action.Chain) - ChainId := tr.chainConfigs[action.Chain].ChainId + queryNodeIP := tr.target.GetQueryNodeIP(action.Chain) + ChainId := tr.testConfig.chainConfigs[action.Chain].ChainId rpcAddr := "http://" + queryNodeIP + ":26658" chainConfig := fmt.Sprintf(gorelayerChainConfigTemplate, ChainId, rpcAddr, - tr.chainConfigs[action.Chain].AccountPrefix, + tr.testConfig.chainConfigs[action.Chain].AccountPrefix, ) - bz, err := target.ExecCommand( + bz, err := tr.target.ExecCommand( "rly", "config", "init").CombinedOutput() if err != nil && !strings.Contains(string(bz), "config already exists") { log.Fatal(err, "\n", string(bz)) @@ -909,25 +897,25 @@ func (tr TestConfig) addChainToGorelayer( chainConfigFileName := fmt.Sprintf("/root/%s_config.json", ChainId) bashCommand := fmt.Sprintf(`echo '%s' >> %s`, chainConfig, chainConfigFileName) - bz, err = target.ExecCommand("bash", "-c", + bz, err = tr.target.ExecCommand("bash", "-c", bashCommand).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } - addChainCommand := target.ExecCommand("rly", "chains", "add", "--file", chainConfigFileName, string(ChainId)) - executeCommand(addChainCommand, "add chain") + addChainCommand := tr.target.ExecCommand("rly", "chains", "add", "--file", chainConfigFileName, string(ChainId)) + e2e.ExecuteCommand(addChainCommand, "add chain") - keyRestoreCommand := target.ExecCommand("rly", "keys", "restore", string(ChainId), "default", tr.validatorConfigs[action.Validator].Mnemonic) - executeCommand(keyRestoreCommand, "restore keys") + keyRestoreCommand := tr.target.ExecCommand("rly", "keys", "restore", string(ChainId), "default", tr.testConfig.validatorConfigs[action.Validator].Mnemonic) + e2e.ExecuteCommand(keyRestoreCommand, "restore keys") } -func (tr TestConfig) addChainToHermes( +func (tr Chain) addChainToHermes( action AddChainToRelayerAction, - target ExecutionTarget, verbose bool, ) { - bz, err := target.ExecCommand("bash", "-c", "hermes", "version").CombinedOutput() + + bz, err := tr.target.ExecCommand("bash", "-c", "hermes", "version").CombinedOutput() if err != nil { log.Fatal(err, "\n error getting hermes version", string(bz)) } @@ -938,34 +926,34 @@ func (tr TestConfig) addChainToHermes( } hermesVersion := match[1] - queryNodeIP := tr.getQueryNodeIP(action.Chain) - hermesConfig := GetHermesConfig(hermesVersion, queryNodeIP, tr.chainConfigs[action.Chain], action.IsConsumer) + queryNodeIP := tr.target.GetQueryNodeIP(action.Chain) + hermesConfig := GetHermesConfig(hermesVersion, queryNodeIP, tr.testConfig.chainConfigs[action.Chain], action.IsConsumer) bashCommand := fmt.Sprintf(`echo '%s' >> %s`, hermesConfig, "/root/.hermes/config.toml") - bz, err = target.ExecCommand("bash", "-c", bashCommand).CombinedOutput() + bz, err = tr.target.ExecCommand("bash", "-c", bashCommand).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } // Save mnemonic to file within container var mnemonic string - if tr.validatorConfigs[action.Validator].UseConsumerKey && action.IsConsumer { - mnemonic = tr.validatorConfigs[action.Validator].ConsumerMnemonic + if tr.testConfig.validatorConfigs[action.Validator].UseConsumerKey && action.IsConsumer { + mnemonic = tr.testConfig.validatorConfigs[action.Validator].ConsumerMnemonic } else { - mnemonic = tr.validatorConfigs[action.Validator].Mnemonic + mnemonic = tr.testConfig.validatorConfigs[action.Validator].Mnemonic } saveMnemonicCommand := fmt.Sprintf(`echo '%s' > %s`, mnemonic, "/root/.hermes/mnemonic.txt") fmt.Println("Add to hermes", action.Validator) - bz, err = target.ExecCommand("bash", "-c", saveMnemonicCommand).CombinedOutput() + bz, err = tr.target.ExecCommand("bash", "-c", saveMnemonicCommand).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } - bz, err = target.ExecCommand("hermes", + bz, err = tr.target.ExecCommand("hermes", "keys", "add", - "--chain", string(tr.chainConfigs[action.Chain].ChainId), + "--chain", string(tr.testConfig.chainConfigs[action.Chain].ChainId), "--mnemonic-file", "/root/.hermes/mnemonic.txt", ).CombinedOutput() if err != nil { @@ -1000,21 +988,19 @@ type AddIbcConnectionAction struct { ClientB uint } -func (tr TestConfig) addIbcConnection( +func (tr Chain) addIbcConnection( action AddIbcConnectionAction, - target ExecutionTarget, verbose bool, ) { - if !tr.useGorelayer { - tr.addIbcConnectionHermes(action, target, verbose) + if !tr.testConfig.useGorelayer { + tr.addIbcConnectionHermes(action, verbose) } else { - tr.addIbcConnectionGorelayer(action, target, verbose) + tr.addIbcConnectionGorelayer(action, verbose) } } -func (tr TestConfig) addIbcConnectionGorelayer( +func (tr Chain) addIbcConnectionGorelayer( action AddIbcConnectionAction, - target ExecutionTarget, verbose bool, ) { pathName := tr.GetPathNameForGorelayer(action.ChainA, action.ChainB) @@ -1026,32 +1012,32 @@ func (tr TestConfig) addIbcConnectionGorelayer( bashCommand := fmt.Sprintf(`echo '%s' >> %s`, pathConfig, pathConfigFileName) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - pathConfigCommand := target.ExecCommand("bash", "-c", bashCommand) - executeCommand(pathConfigCommand, "add path config") + pathConfigCommand := tr.target.ExecCommand("bash", "-c", bashCommand) + e2e.ExecuteCommand(pathConfigCommand, "add path config") //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - newPathCommand := target.ExecCommand("rly", + newPathCommand := tr.target.ExecCommand("rly", "paths", "add", - string(tr.chainConfigs[action.ChainA].ChainId), - string(tr.chainConfigs[action.ChainB].ChainId), + string(tr.testConfig.chainConfigs[action.ChainA].ChainId), + string(tr.testConfig.chainConfigs[action.ChainB].ChainId), pathName, "--file", pathConfigFileName, ) - executeCommand(newPathCommand, "new path") + e2e.ExecuteCommand(newPathCommand, "new path") //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - newClientsCommand := target.ExecCommand("rly", "transact", "clients", pathName) + newClientsCommand := tr.target.ExecCommand("rly", "transact", "clients", pathName) - executeCommand(newClientsCommand, "new clients") + e2e.ExecuteCommand(newClientsCommand, "new clients") tr.waitBlocks(action.ChainA, 1, 10*time.Second) tr.waitBlocks(action.ChainB, 1, 10*time.Second) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - newConnectionCommand := target.ExecCommand("rly", "transact", "connection", pathName) + newConnectionCommand := tr.target.ExecCommand("rly", "transact", "connection", pathName) - executeCommand(newConnectionCommand, "new connection") + e2e.ExecuteCommand(newConnectionCommand, "new connection") tr.waitBlocks(action.ChainA, 1, 10*time.Second) tr.waitBlocks(action.ChainB, 1, 10*time.Second) @@ -1065,15 +1051,14 @@ type CreateIbcClientsAction struct { // if clients are not provided hermes will first // create new clients and then a new connection // otherwise, it would use client provided as CLI argument (-a-client) -func (tr TestConfig) createIbcClientsHermes( +func (tr Chain) createIbcClientsHermes( action CreateIbcClientsAction, - target ExecutionTarget, verbose bool, ) { - cmd := target.ExecCommand("hermes", + cmd := tr.target.ExecCommand("hermes", "create", "connection", - "--a-chain", string(tr.chainConfigs[action.ChainA].ChainId), - "--b-chain", string(tr.chainConfigs[action.ChainB].ChainId), + "--a-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), + "--b-chain", string(tr.testConfig.chainConfigs[action.ChainB].ChainId), ) cmdReader, err := cmd.StdoutPipe() @@ -1102,14 +1087,13 @@ func (tr TestConfig) createIbcClientsHermes( } } -func (tr TestConfig) addIbcConnectionHermes( +func (tr Chain) addIbcConnectionHermes( action AddIbcConnectionAction, - target ExecutionTarget, verbose bool, ) { - cmd := target.ExecCommand("hermes", + cmd := tr.target.ExecCommand("hermes", "create", "connection", - "--a-chain", string(tr.chainConfigs[action.ChainA].ChainId), + "--a-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), "--a-client", "07-tendermint-"+fmt.Sprint(action.ClientA), "--b-client", "07-tendermint-"+fmt.Sprint(action.ClientB), ) @@ -1152,25 +1136,23 @@ type AddIbcChannelAction struct { type StartRelayerAction struct{} -func (tr TestConfig) startRelayer( +func (tr Chain) startRelayer( action StartRelayerAction, - target ExecutionTarget, verbose bool, ) { - if tr.useGorelayer { - tr.startGorelayer(action, target, verbose) + if tr.testConfig.useGorelayer { + tr.startGorelayer(action, verbose) } else { - tr.startHermes(action, target, verbose) + tr.startHermes(action, verbose) } } -func (tr TestConfig) startGorelayer( +func (tr Chain) startGorelayer( action StartRelayerAction, - target ExecutionTarget, verbose bool, ) { // gorelayer start is running in detached mode - cmd := target.ExecDetachedCommand("rly", "start") + cmd := tr.target.ExecDetachedCommand("rly", "start") if err := cmd.Start(); err != nil { log.Fatal(err) @@ -1181,13 +1163,12 @@ func (tr TestConfig) startGorelayer( } } -func (tr TestConfig) startHermes( +func (tr Chain) startHermes( action StartRelayerAction, - target ExecutionTarget, verbose bool, ) { // hermes start is running in detached mode - cmd := target.ExecDetachedCommand("hermes", "start") + cmd := tr.target.ExecDetachedCommand("hermes", "start") if err := cmd.Start(); err != nil { log.Fatal(err) @@ -1198,51 +1179,48 @@ func (tr TestConfig) startHermes( } } -func (tr TestConfig) addIbcChannel( +func (tr Chain) addIbcChannel( action AddIbcChannelAction, - target ExecutionTarget, verbose bool, ) { - if tr.useGorelayer { - tr.addIbcChannelGorelayer(action, target, verbose) + if tr.testConfig.useGorelayer { + tr.addIbcChannelGorelayer(action, verbose) } else { - tr.addIbcChannelHermes(action, target, verbose) + tr.addIbcChannelHermes(action, verbose) } } -func (tr TestConfig) addIbcChannelGorelayer( +func (tr Chain) addIbcChannelGorelayer( action AddIbcChannelAction, - target ExecutionTarget, verbose bool, ) { pathName := tr.GetPathNameForGorelayer(action.ChainA, action.ChainB) - cmd := target.ExecCommand("rly", + cmd := tr.target.ExecCommand("rly", "transact", "channel", pathName, "--src-port", action.PortA, "--dst-port", action.PortB, - "--version", tr.containerConfig.CcvVersion, + "--version", tr.testConfig.containerConfig.CcvVersion, "--order", action.Order, "--debug", ) - executeCommand(cmd, "addChannel") + e2e.ExecuteCommand(cmd, "addChannel") } -func (tr TestConfig) addIbcChannelHermes( +func (tr Chain) addIbcChannelHermes( action AddIbcChannelAction, - target ExecutionTarget, verbose bool, ) { // if version is not specified, use the default version when creating ccv connections // otherwise, use the provided version schema (usually it is ICS20-1 for IBC transfer) chanVersion := action.Version if chanVersion == "" { - chanVersion = tr.containerConfig.CcvVersion + chanVersion = tr.testConfig.containerConfig.CcvVersion } - cmd := target.ExecCommand("hermes", + cmd := tr.target.ExecCommand("hermes", "create", "channel", - "--a-chain", string(tr.chainConfigs[action.ChainA].ChainId), + "--a-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), "--a-connection", "connection-"+fmt.Sprint(action.ConnectionA), "--a-port", action.PortA, "--b-port", action.PortB, @@ -1291,30 +1269,29 @@ type TransferChannelCompleteAction struct { ChannelB uint } -func (tr TestConfig) transferChannelComplete( +func (tr Chain) transferChannelComplete( action TransferChannelCompleteAction, - target ExecutionTarget, verbose bool, ) { - if tr.useGorelayer { + if tr.testConfig.useGorelayer { log.Fatal("transferChannelComplete is not implemented for rly") } - chanOpenTryCmd := target.ExecCommand("hermes", + chanOpenTryCmd := tr.target.ExecCommand("hermes", "tx", "chan-open-try", - "--dst-chain", string(tr.chainConfigs[action.ChainB].ChainId), - "--src-chain", string(tr.chainConfigs[action.ChainA].ChainId), + "--dst-chain", string(tr.testConfig.chainConfigs[action.ChainB].ChainId), + "--src-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), "--dst-connection", "connection-"+fmt.Sprint(action.ConnectionA), "--dst-port", action.PortB, "--src-port", action.PortA, "--src-channel", "channel-"+fmt.Sprint(action.ChannelA), ) - executeCommand(chanOpenTryCmd, "transferChanOpenTry") + e2e.ExecuteCommand(chanOpenTryCmd, "transferChanOpenTry") - chanOpenAckCmd := target.ExecCommand("hermes", + chanOpenAckCmd := tr.target.ExecCommand("hermes", "tx", "chan-open-ack", - "--dst-chain", string(tr.chainConfigs[action.ChainA].ChainId), - "--src-chain", string(tr.chainConfigs[action.ChainB].ChainId), + "--dst-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), + "--src-chain", string(tr.testConfig.chainConfigs[action.ChainB].ChainId), "--dst-connection", "connection-"+fmt.Sprint(action.ConnectionA), "--dst-port", action.PortA, "--src-port", action.PortB, @@ -1322,52 +1299,19 @@ func (tr TestConfig) transferChannelComplete( "--src-channel", "channel-"+fmt.Sprint(action.ChannelB), ) - executeCommand(chanOpenAckCmd, "transferChanOpenAck") + e2e.ExecuteCommand(chanOpenAckCmd, "transferChanOpenAck") - chanOpenConfirmCmd := target.ExecCommand("hermes", + chanOpenConfirmCmd := tr.target.ExecCommand("hermes", "tx", "chan-open-confirm", - "--dst-chain", string(tr.chainConfigs[action.ChainB].ChainId), - "--src-chain", string(tr.chainConfigs[action.ChainA].ChainId), + "--dst-chain", string(tr.testConfig.chainConfigs[action.ChainB].ChainId), + "--src-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), "--dst-connection", "connection-"+fmt.Sprint(action.ConnectionA), "--dst-port", action.PortB, "--src-port", action.PortA, "--dst-channel", "channel-"+fmt.Sprint(action.ChannelB), "--src-channel", "channel-"+fmt.Sprint(action.ChannelA), ) - executeCommand(chanOpenConfirmCmd, "transferChanOpenConfirm") -} - -func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) { - if verbose { - fmt.Println(cmdName+" cmd:", cmd.String()) - } - - cmdReader, err := cmd.StdoutPipe() - if err != nil { - log.Fatal(err) - } - cmd.Stderr = cmd.Stdout - - if err := cmd.Start(); err != nil { - log.Fatal(err) - } - - scanner := bufio.NewScanner(cmdReader) - - for scanner.Scan() { - out := scanner.Text() - if verbose { - fmt.Println(cmdName + ": " + out) - } - } - if err := scanner.Err(); err != nil { - log.Fatal(err) - } -} - -// Executes a command with verbosity specified by CLI flag -func executeCommand(cmd *exec.Cmd, cmdName string) { - executeCommandWithVerbosity(cmd, cmdName, *verbose) + e2e.ExecuteCommand(chanOpenConfirmCmd, "transferChanOpenConfirm") } type RelayPacketsAction struct { @@ -1377,21 +1321,19 @@ type RelayPacketsAction struct { Channel uint } -func (tr TestConfig) relayPackets( +func (tr Chain) relayPackets( action RelayPacketsAction, - target ExecutionTarget, verbose bool, ) { - if tr.useGorelayer { - tr.relayPacketsGorelayer(action, target, verbose) + if tr.testConfig.useGorelayer { + tr.relayPacketsGorelayer(action, verbose) } else { - tr.relayPacketsHermes(action, target, verbose) + tr.relayPacketsHermes(action, verbose) } } -func (tr TestConfig) relayPacketsGorelayer( +func (tr Chain) relayPacketsGorelayer( action RelayPacketsAction, - target ExecutionTarget, verbose bool, ) { // Because `.app_state.provider.params.blocks_per_epoch` is set to 3 in the E2E tests, we wait 3 blocks @@ -1403,7 +1345,7 @@ func (tr TestConfig) relayPacketsGorelayer( pathName := tr.GetPathNameForGorelayer(action.ChainA, action.ChainB) // rly transact relay-packets [path-name] --channel [channel-id] - cmd := target.ExecCommand("rly", "transact", "flush", + cmd := tr.target.ExecCommand("rly", "transact", "flush", pathName, "channel-"+fmt.Sprint(action.Channel), ) @@ -1419,9 +1361,8 @@ func (tr TestConfig) relayPacketsGorelayer( tr.waitBlocks(action.ChainB, 1, 30*time.Second) } -func (tr TestConfig) relayPacketsHermes( +func (tr Chain) relayPacketsHermes( action RelayPacketsAction, - target ExecutionTarget, verbose bool, ) { // Because `.app_state.provider.params.blocks_per_epoch` is set to 3 in the E2E tests, we wait 3 blocks @@ -1431,8 +1372,8 @@ func (tr TestConfig) relayPacketsHermes( tr.waitBlocks(action.ChainB, 3, 90*time.Second) // hermes clear packets ibc0 transfer channel-13 - cmd := target.ExecCommand("hermes", "clear", "packets", - "--chain", string(tr.chainConfigs[action.ChainA].ChainId), + cmd := tr.target.ExecCommand("hermes", "clear", "packets", + "--chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), "--port", action.Port, "--channel", "channel-"+fmt.Sprint(action.Channel), ) @@ -1456,18 +1397,17 @@ type RelayRewardPacketsToProviderAction struct { Channel uint } -func (tr TestConfig) relayRewardPacketsToProvider( +func (tr Chain) relayRewardPacketsToProvider( action RelayRewardPacketsToProviderAction, - target ExecutionTarget, verbose bool, ) { - blockPerDistribution, _ := strconv.ParseUint(strings.Trim(tr.getParam(action.ConsumerChain, Param{Subspace: "ccvconsumer", Key: "BlocksPerDistributionTransmission"}), "\""), 10, 64) - currentBlock := uint64(tr.getBlockHeight(action.ConsumerChain)) + blockPerDistribution, _ := strconv.ParseUint(strings.Trim(tr.target.GetParam(action.ConsumerChain, Param{Subspace: "ccvconsumer", Key: "BlocksPerDistributionTransmission"}), "\""), 10, 64) + currentBlock := uint64(tr.target.GetBlockHeight(action.ConsumerChain)) if currentBlock <= blockPerDistribution { tr.waitBlocks(action.ConsumerChain, uint(blockPerDistribution-currentBlock+1), 60*time.Second) } - tr.relayPackets(RelayPacketsAction{ChainA: action.ConsumerChain, ChainB: action.ProviderChain, Port: action.Port, Channel: action.Channel}, target, verbose) + tr.relayPackets(RelayPacketsAction{ChainA: action.ConsumerChain, ChainB: action.ProviderChain, Port: action.Port, Channel: action.Channel}, verbose) tr.waitBlocks(action.ProviderChain, 1, 10*time.Second) } @@ -1478,12 +1418,11 @@ type DelegateTokensAction struct { Amount uint } -func (tr TestConfig) delegateTokens( +func (tr Chain) delegateTokens( action DelegateTokensAction, - target ExecutionTarget, verbose bool, ) { - toValCfg := tr.validatorConfigs[action.To] + toValCfg := tr.testConfig.validatorConfigs[action.To] validatorAddress := toValCfg.ValoperAddress if action.Chain != ChainID("provi") { // use binary with Bech32Prefix set to ConsumerAccountPrefix @@ -1495,14 +1434,14 @@ func (tr TestConfig) delegateTokens( } } - cmd := target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand(tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "staking", "delegate", validatorAddress, fmt.Sprint(action.Amount)+`stake`, `--from`, `validator`+fmt.Sprint(action.From), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), `--node`, tr.getValidatorNode(action.Chain, action.From), `--keyring-backend`, `test`, @@ -1529,12 +1468,11 @@ type UnbondTokensAction struct { Amount uint } -func (tr TestConfig) unbondTokens( +func (tr Chain) unbondTokens( action UnbondTokensAction, - target ExecutionTarget, verbose bool, ) { - unbondFromValCfg := tr.validatorConfigs[action.UnbondFrom] + unbondFromValCfg := tr.testConfig.validatorConfigs[action.UnbondFrom] validatorAddress := unbondFromValCfg.ValoperAddress if action.Chain != ChainID("provi") { // use binary with Bech32Prefix set to ConsumerAccountPrefix @@ -1546,14 +1484,14 @@ func (tr TestConfig) unbondTokens( } } - cmd := target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand(tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "staking", "unbond", validatorAddress, fmt.Sprint(action.Amount)+`stake`, `--from`, `validator`+fmt.Sprint(action.Sender), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.Sender), `--node`, tr.getValidatorNode(action.Chain, action.Sender), `--gas`, "900000", @@ -1581,13 +1519,12 @@ type CancelUnbondTokensAction struct { Amount uint } -func (tr TestConfig) cancelUnbondTokens( +func (tr Chain) cancelUnbondTokens( action CancelUnbondTokensAction, - target ExecutionTarget, verbose bool, ) { - valCfg := tr.validatorConfigs[action.Validator] - delCfg := tr.validatorConfigs[action.Delegator] + valCfg := tr.testConfig.validatorConfigs[action.Validator] + delCfg := tr.testConfig.validatorConfigs[action.Delegator] validatorAddress := valCfg.ValoperAddress delegatorAddress := delCfg.DelAddress if action.Chain != ChainID("provi") { @@ -1607,7 +1544,7 @@ func (tr TestConfig) cancelUnbondTokens( } // get creation-height from state - cmd := target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand(tr.testConfig.chainConfigs[action.Chain].BinaryName, "q", "staking", "unbonding-delegation", delegatorAddress, validatorAddress, @@ -1628,13 +1565,13 @@ func (tr TestConfig) cancelUnbondTokens( log.Fatal("invalid creation height") } - cmd = target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, + cmd = tr.target.ExecCommand(tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "staking", "cancel-unbond", validatorAddress, fmt.Sprint(action.Amount)+`stake`, fmt.Sprint(creationHeight), `--from`, `validator`+fmt.Sprint(action.Delegator), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.Delegator), `--node`, tr.getValidatorNode(action.Chain, action.Delegator), `--gas`, "900000", @@ -1664,9 +1601,9 @@ type RedelegateTokensAction struct { Amount uint } -func (tr TestConfig) redelegateTokens(action RedelegateTokensAction, target ExecutionTarget, verbose bool) { - srcCfg := tr.validatorConfigs[action.Src] - dstCfg := tr.validatorConfigs[action.Dst] +func (tr Chain) redelegateTokens(action RedelegateTokensAction, verbose bool) { + srcCfg := tr.testConfig.validatorConfigs[action.Src] + dstCfg := tr.testConfig.validatorConfigs[action.Dst] redelegateSrc := srcCfg.ValoperAddress redelegateDst := dstCfg.ValoperAddress if action.Chain != ChainID("provi") { @@ -1685,15 +1622,15 @@ func (tr TestConfig) redelegateTokens(action RedelegateTokensAction, target Exec } } - cmd := target.ExecCommand( - tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "staking", "redelegate", redelegateSrc, redelegateDst, fmt.Sprint(action.Amount)+`stake`, `--from`, `validator`+fmt.Sprint(action.TxSender), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.TxSender), `--node`, tr.getValidatorNode(action.Chain, action.TxSender), // Need to manually set gas limit past default (200000), since redelegate has a lot of operations @@ -1723,7 +1660,7 @@ type DowntimeSlashAction struct { // takes a string representation of the private key like // `{"address":"DF090A4880B54CD57B2A79E64D9E969BD7514B09","pub_key":{"type":"tendermint/PubKeyEd25519","value":"ujY14AgopV907IYgPAk/5x8c9267S4fQf89nyeCPTes="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"TRJgf7lkTjs/sj43pyweEOanyV7H7fhnVivOi0A4yjW6NjXgCCilX3TshiA8CT/nHxz3brtLh9B/z2fJ4I9N6w=="}}` // and returns the value of the "address" field -func (tr TestConfig) getValidatorKeyAddressFromString(keystring string) string { +func (tr Chain) getValidatorKeyAddressFromString(keystring string) string { var key struct { Address string `json:"address"` } @@ -1734,17 +1671,17 @@ func (tr TestConfig) getValidatorKeyAddressFromString(keystring string) string { return key.Address } -func (tr TestConfig) invokeDowntimeSlash(action DowntimeSlashAction, target ExecutionTarget, verbose bool) { +func (tr Chain) invokeDowntimeSlash(action DowntimeSlashAction, verbose bool) { // Bring validator down - tr.setValidatorDowntime(action.Chain, action.Validator, true, target, verbose) + tr.setValidatorDowntime(action.Chain, action.Validator, true, verbose) // Wait appropriate amount of blocks for validator to be slashed tr.waitBlocks(action.Chain, 11, 3*time.Minute) // Bring validator back up - tr.setValidatorDowntime(action.Chain, action.Validator, false, target, verbose) + tr.setValidatorDowntime(action.Chain, action.Validator, false, verbose) } // Sets validator downtime by setting the virtual ethernet interface of a node to "up" or "down" -func (tr TestConfig) setValidatorDowntime(chain ChainID, validator ValidatorID, down bool, target ExecutionTarget, verbose bool) { +func (tr Chain) setValidatorDowntime(chain ChainID, validator ValidatorID, down bool, verbose bool) { var lastArg string if down { lastArg = "down" @@ -1752,20 +1689,20 @@ func (tr TestConfig) setValidatorDowntime(chain ChainID, validator ValidatorID, lastArg = "up" } - if tr.useCometmock { + if tr.testConfig.useCometmock { // send set_signing_status either to down or up for validator validatorPrivateKeyAddress := tr.GetValidatorPrivateKeyAddress(chain, validator) method := "set_signing_status" params := fmt.Sprintf(`{"private_key_address":"%s","status":"%s"}`, validatorPrivateKeyAddress, lastArg) - address := tr.getQueryNodeRPCAddress(chain) + address := tr.target.GetQueryNodeRPCAddress(chain) tr.curlJsonRPCRequest(method, params, address) tr.waitBlocks(chain, 1, 10*time.Second) return } - cmd := target.ExecCommand( + cmd := tr.target.ExecCommand( "ip", "link", "set", @@ -1783,16 +1720,16 @@ func (tr TestConfig) setValidatorDowntime(chain ChainID, validator ValidatorID, } } -func (tr TestConfig) GetValidatorPrivateKeyAddress(chain ChainID, validator ValidatorID) string { +func (tr Chain) GetValidatorPrivateKeyAddress(chain ChainID, validator ValidatorID) string { var validatorPrivateKeyAddress string if chain == ChainID("provi") { - validatorPrivateKeyAddress = tr.getValidatorKeyAddressFromString(tr.validatorConfigs[validator].PrivValidatorKey) + validatorPrivateKeyAddress = tr.getValidatorKeyAddressFromString(tr.testConfig.validatorConfigs[validator].PrivValidatorKey) } else { var valAddressString string - if tr.validatorConfigs[validator].UseConsumerKey { - valAddressString = tr.validatorConfigs[validator].ConsumerPrivValidatorKey + if tr.testConfig.validatorConfigs[validator].UseConsumerKey { + valAddressString = tr.testConfig.validatorConfigs[validator].ConsumerPrivValidatorKey } else { - valAddressString = tr.validatorConfigs[validator].PrivValidatorKey + valAddressString = tr.testConfig.validatorConfigs[validator].PrivValidatorKey } validatorPrivateKeyAddress = tr.getValidatorKeyAddressFromString(valAddressString) } @@ -1805,16 +1742,16 @@ type UnjailValidatorAction struct { } // Sends an unjail transaction to the provider chain -func (tr TestConfig) unjailValidator(action UnjailValidatorAction, target ExecutionTarget, verbose bool) { +func (tr Chain) unjailValidator(action UnjailValidatorAction, verbose bool) { // wait until downtime_jail_duration has elapsed, to make sure the validator can be unjailed tr.WaitTime(61 * time.Second) - cmd := target.ExecCommand( - tr.chainConfigs[action.Provider].BinaryName, + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Provider].BinaryName, "tx", "slashing", "unjail", // Validator is sender here `--from`, `validator`+fmt.Sprint(action.Validator), - `--chain-id`, string(tr.chainConfigs[action.Provider].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Provider].ChainId), `--home`, tr.getValidatorHome(action.Provider, action.Validator), `--node`, tr.getValidatorNode(action.Provider, action.Validator), `--gas`, "900000", @@ -1843,9 +1780,8 @@ type RegisterRepresentativeAction struct { Stakes []uint } -func (tr TestConfig) registerRepresentative( +func (tr Chain) registerRepresentative( action RegisterRepresentativeAction, - target ExecutionTarget, verbose bool, ) { fileTempl := `{ @@ -1868,7 +1804,7 @@ func (tr TestConfig) registerRepresentative( go func(val ValidatorID, stake uint) { defer wg.Done() - pubKeycmd := target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, + pubKeycmd := tr.target.ExecCommand(tr.testConfig.chainConfigs[action.Chain].BinaryName, "tendermint", "show-validator", `--home`, tr.getValidatorHome(action.Chain, val), ) @@ -1890,7 +1826,7 @@ func (tr TestConfig) registerRepresentative( log.Fatalf("Failed writing consumer genesis to file: %v", err) } - containerInstance := tr.containerConfig.InstanceName + containerInstance := tr.testConfig.containerConfig.InstanceName targetFile := fmt.Sprintf("/tmp/%s", fileName) sourceFile := file.Name() //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. @@ -1901,11 +1837,11 @@ func (tr TestConfig) registerRepresentative( log.Fatal(err, "\n", string(writeResult)) } - cmd := target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, + cmd := tr.target.ExecCommand(tr.testConfig.chainConfigs[action.Chain].BinaryName, "tx", "staking", "create-validator", targetFile, `--from`, `validator`+fmt.Sprint(val), - `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--chain-id`, string(tr.testConfig.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, val), `--node`, tr.getValidatorNode(action.Chain, val), `--keyring-backend`, `test`, @@ -1936,8 +1872,8 @@ type SubmitChangeRewardDenomsProposalAction struct { From ValidatorID } -func (tr TestConfig) submitChangeRewardDenomsProposal(action SubmitChangeRewardDenomsProposalAction, target ExecutionTarget, verbose bool) { - providerChain := tr.chainConfigs[ChainID("provi")] +func (tr Chain) submitChangeRewardDenomsProposal(action SubmitChangeRewardDenomsProposalAction, verbose bool) { + providerChain := tr.testConfig.chainConfigs[ChainID("provi")] prop := client.ChangeRewardDenomsProposalJSON{ Summary: "Change reward denoms", @@ -1960,14 +1896,14 @@ func (tr TestConfig) submitChangeRewardDenomsProposal(action SubmitChangeRewardD log.Fatal("prop json contains single quote") } - bz, err = target.ExecCommand( + bz, err = tr.target.ExecCommand( "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/change-reward-denoms-proposal.json")).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } // CHANGE REWARDS DENOM PROPOSAL - bz, err = target.ExecCommand(providerChain.BinaryName, + bz, err = tr.target.ExecCommand(providerChain.BinaryName, "tx", "gov", "submit-legacy-proposal", "change-reward-denoms", "/change-reward-denoms-proposal.json", `--from`, `validator`+fmt.Sprint(action.From), `--chain-id`, string(providerChain.ChainId), @@ -2001,15 +1937,14 @@ type DoublesignSlashAction struct { Chain ChainID } -func (tr TestConfig) invokeDoublesignSlash( +func (tr Chain) invokeDoublesignSlash( action DoublesignSlashAction, - target ExecutionTarget, verbose bool, ) { - if !tr.useCometmock { - chainConfig := tr.chainConfigs[action.Chain] - doubleSignScript := target.GetTestScriptPath(false, "cause-doublesign.sh") - bz, err := target.ExecCommand("/bin/bash", + if !tr.testConfig.useCometmock { + chainConfig := tr.testConfig.chainConfigs[action.Chain] + doubleSignScript := tr.target.GetTestScriptPath(false, "cause-doublesign.sh") + bz, err := tr.target.ExecCommand("/bin/bash", doubleSignScript, chainConfig.BinaryName, string(action.Validator), string(chainConfig.ChainId), chainConfig.IpPrefix).CombinedOutput() if err != nil { @@ -2022,7 +1957,7 @@ func (tr TestConfig) invokeDoublesignSlash( method := "cause_double_sign" params := fmt.Sprintf(`{"private_key_address":"%s"}`, validatorPrivateKeyAddress) - address := tr.getQueryNodeRPCAddress(action.Chain) + address := tr.target.GetQueryNodeRPCAddress(action.Chain) tr.curlJsonRPCRequest(method, params, address) tr.waitBlocks(action.Chain, 1, 10*time.Second) @@ -2039,7 +1974,7 @@ type LightClientEquivocationAttackAction struct { Chain ChainID } -func (tr TestConfig) lightClientEquivocationAttack( +func (tr Chain) lightClientEquivocationAttack( action LightClientEquivocationAttackAction, verbose bool, ) { @@ -2055,7 +1990,7 @@ type LightClientAmnesiaAttackAction struct { Chain ChainID } -func (tr TestConfig) lightClientAmnesiaAttack( +func (tr Chain) lightClientAmnesiaAttack( action LightClientAmnesiaAttackAction, verbose bool, ) { @@ -2071,7 +2006,7 @@ type LightClientLunaticAttackAction struct { Chain ChainID } -func (tr TestConfig) lightClientLunaticAttack( +func (tr Chain) lightClientLunaticAttack( action LightClientLunaticAttackAction, verbose bool, ) { @@ -2086,12 +2021,12 @@ const ( LightClientLunaticAttack LightClientAttackType = "Lunatic" ) -func (tr TestConfig) lightClientAttack( +func (tr Chain) lightClientAttack( validator ValidatorID, chain ChainID, attackType LightClientAttackType, ) { - if !tr.useCometmock { + if !tr.testConfig.useCometmock { log.Fatal("light client attack is only supported with CometMock") } validatorPrivateKeyAddress := tr.GetValidatorPrivateKeyAddress(chain, validator) @@ -2099,7 +2034,7 @@ func (tr TestConfig) lightClientAttack( method := "cause_light_client_attack" params := fmt.Sprintf(`{"private_key_address":"%s", "misbehaviour_type": "%s"}`, validatorPrivateKeyAddress, attackType) - address := tr.getQueryNodeRPCAddress(chain) + address := tr.target.GetQueryNodeRPCAddress(chain) tr.curlJsonRPCRequest(method, params, address) tr.waitBlocks(chain, 1, 10*time.Second) @@ -2116,28 +2051,28 @@ type AssignConsumerPubKeyAction struct { ExpectedError string } -func (tr TestConfig) assignConsumerPubKey(action AssignConsumerPubKeyAction, target ExecutionTarget, verbose bool) { - valCfg := tr.validatorConfigs[action.Validator] +func (tr Chain) assignConsumerPubKey(action AssignConsumerPubKeyAction, verbose bool) { + valCfg := tr.testConfig.validatorConfigs[action.Validator] // Note: to get error response reported back from this command '--gas auto' needs to be set. gas := "auto" // Unfortunately, --gas auto does not work with CometMock. so when using CometMock, just use --gas 9000000 then - if tr.useCometmock { + if tr.testConfig.useCometmock { gas = "9000000" } assignKey := fmt.Sprintf( `%s tx provider assign-consensus-key %s '%s' --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y -o json`, - tr.chainConfigs[ChainID("provi")].BinaryName, - string(tr.chainConfigs[action.Chain].ChainId), + tr.testConfig.chainConfigs[ChainID("provi")].BinaryName, + string(tr.testConfig.chainConfigs[action.Chain].ChainId), action.ConsumerPubkey, action.Validator, - tr.chainConfigs[ChainID("provi")].ChainId, + tr.testConfig.chainConfigs[ChainID("provi")].ChainId, tr.getValidatorHome(ChainID("provi"), action.Validator), tr.getValidatorNode(ChainID("provi"), action.Validator), gas, ) - cmd := target.ExecCommand( + cmd := tr.target.ExecCommand( "/bin/bash", "-c", assignKey, ) @@ -2151,7 +2086,7 @@ func (tr TestConfig) assignConsumerPubKey(action AssignConsumerPubKeyAction, tar log.Fatalf("unexpected error during key assignment - output: %s, err: %s", string(bz), err) } - if action.ExpectError && !tr.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 || !strings.Contains(string(bz), action.ExpectedError) { log.Fatalf("expected error not raised: expected: '%s', got '%s'", action.ExpectedError, (bz)) } @@ -2164,12 +2099,12 @@ func (tr TestConfig) assignConsumerPubKey(action AssignConsumerPubKeyAction, tar // node was started with provider key // we swap the nodes's keys for consumer keys and restart it if action.ReconfigureNode { - isConsumer := tr.chainConfigs[action.Chain].BinaryName != "interchain-security-pd" - reconfigureScript := target.GetTestScriptPath(isConsumer, "reconfigure-node.sh") - configureNodeCmd := target.ExecCommand("/bin/bash", - reconfigureScript, tr.chainConfigs[action.Chain].BinaryName, + isConsumer := tr.testConfig.chainConfigs[action.Chain].BinaryName != "interchain-security-pd" + reconfigureScript := tr.target.GetTestScriptPath(isConsumer, "reconfigure-node.sh") + configureNodeCmd := tr.target.ExecCommand("/bin/bash", + reconfigureScript, tr.testConfig.chainConfigs[action.Chain].BinaryName, string(action.Validator), string(action.Chain), - tr.chainConfigs[action.Chain].IpPrefix, valCfg.IpSuffix, + tr.testConfig.chainConfigs[action.Chain].IpPrefix, valCfg.IpSuffix, valCfg.ConsumerMnemonic, valCfg.ConsumerPrivValidatorKey, valCfg.ConsumerNodeKey, ) @@ -2208,7 +2143,7 @@ func (tr TestConfig) assignConsumerPubKey(action AssignConsumerPubKeyAction, tar // @POfftermatt I am currently using this for downtime slashing with cometmock // (I need to find the currently used validator key address)Í valCfg.UseConsumerKey = true - tr.validatorConfigs[action.Validator] = valCfg + tr.testConfig.validatorConfigs[action.Validator] = valCfg } // wait for inclusion in a block -> '--broadcast-mode block' is deprecated @@ -2222,19 +2157,19 @@ type SlashMeterReplenishmentAction struct { Timeout time.Duration } -func (tr TestConfig) waitForSlashMeterReplenishment( +func (tr Chain) waitForSlashMeterReplenishment( action SlashMeterReplenishmentAction, verbose bool, ) { timeout := time.Now().Add(action.Timeout) - initialSlashMeter := tr.getSlashMeter() + initialSlashMeter := tr.target.GetSlashMeter() if initialSlashMeter >= 0 { panic(fmt.Sprintf("No need to wait for slash meter replenishment, current value: %d", initialSlashMeter)) } for { - slashMeter := tr.getSlashMeter() + slashMeter := tr.target.GetSlashMeter() if verbose { fmt.Printf("waiting for slash meter to be replenished, current value: %d\n", slashMeter) } @@ -2256,7 +2191,7 @@ type WaitTimeAction struct { WaitTime time.Duration } -func (tr TestConfig) waitForTime( +func (tr Chain) waitForTime( action WaitTimeAction, verbose bool, ) { @@ -2266,7 +2201,7 @@ func (tr TestConfig) waitForTime( // GetPathNameForGorelayer returns the name of the path between two given chains used by Gorelayer. // Since paths are bidirectional, we need either chain to be able to be provided as first or second argument // and still return the same name, so we sort the chain names alphabetically. -func (tr TestConfig) GetPathNameForGorelayer(chainA, chainB ChainID) string { +func (tr Chain) GetPathNameForGorelayer(chainA, chainB ChainID) string { var pathName string if string(chainA) < string(chainB) { pathName = string(chainA) + "-" + string(chainB) @@ -2285,19 +2220,18 @@ type StartConsumerEvidenceDetectorAction struct { Chain ChainID } -func (tc TestConfig) startConsumerEvidenceDetector( +func (tr Chain) startConsumerEvidenceDetector( action StartConsumerEvidenceDetectorAction, - target ExecutionTarget, verbose bool, ) { - chainConfig := tc.chainConfigs[action.Chain] + chainConfig := tr.testConfig.chainConfigs[action.Chain] // run in detached mode so it will keep running in the background - bz, err := target.ExecDetachedCommand( + bz, err := tr.target.ExecDetachedCommand( "hermes", "evidence", "--chain", string(chainConfig.ChainId)).CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } - tc.waitBlocks("provi", 10, 2*time.Minute) + tr.waitBlocks("provi", 10, 2*time.Minute) } // WaitTime waits for the given duration. @@ -2306,12 +2240,12 @@ func (tc TestConfig) startConsumerEvidenceDetector( // The CometMock version of this takes a pointer to the TestConfig as it needs to manipulate // information in the testrun that stores how much each chain has waited, to keep times in sync. // Be careful that all functions calling WaitTime should therefore also take a pointer to the TestConfig. -func (tr *TestConfig) WaitTime(duration time.Duration) { - if !tr.useCometmock { +func (tr *Chain) WaitTime(duration time.Duration) { + if !tr.testConfig.useCometmock { time.Sleep(duration) } else { - tr.timeOffset += duration - for chain, running := range tr.runningChains { + tr.testConfig.timeOffset += duration + for chain, running := range tr.testConfig.runningChains { if !running { continue } @@ -2321,12 +2255,12 @@ func (tr *TestConfig) WaitTime(duration time.Duration) { } } -func (tr TestConfig) AdvanceTimeForChain(chain ChainID, duration time.Duration) { +func (tr Chain) AdvanceTimeForChain(chain ChainID, duration time.Duration) { // cometmock avoids sleeping, and instead advances time for all chains method := "advance_time" params := fmt.Sprintf(`{"duration_in_seconds": "%d"}`, int(math.Ceil(duration.Seconds()))) - address := tr.getQueryNodeRPCAddress(chain) + address := tr.target.GetQueryNodeRPCAddress(chain) tr.curlJsonRPCRequest(method, params, address) diff --git a/tests/e2e/actions_consumer_misbehaviour.go b/tests/e2e/actions_consumer_misbehaviour.go index 10cd4ea5c6..6c2fc74304 100644 --- a/tests/e2e/actions_consumer_misbehaviour.go +++ b/tests/e2e/actions_consumer_misbehaviour.go @@ -3,7 +3,6 @@ package main import ( "bufio" "log" - "os/exec" "strconv" "time" ) @@ -17,15 +16,13 @@ type ForkConsumerChainAction struct { RelayerConfig string } -func (tc TestConfig) forkConsumerChain(action ForkConsumerChainAction, verbose bool) { - valCfg := tc.validatorConfigs[action.Validator] - - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - configureNodeCmd := exec.Command("docker", "exec", tc.containerConfig.InstanceName, "/bin/bash", - "/testnet-scripts/fork-consumer.sh", tc.chainConfigs[action.ConsumerChain].BinaryName, +func (tc Chain) forkConsumerChain(action ForkConsumerChainAction, verbose bool) { + valCfg := tc.testConfig.validatorConfigs[action.Validator] + configureNodeCmd := tc.target.ExecCommand("/bin/bash", + "/testnet-scripts/fork-consumer.sh", tc.testConfig.chainConfigs[action.ConsumerChain].BinaryName, string(action.Validator), string(action.ConsumerChain), - tc.chainConfigs[action.ConsumerChain].IpPrefix, - tc.chainConfigs[action.ProviderChain].IpPrefix, + tc.testConfig.chainConfigs[action.ConsumerChain].IpPrefix, + tc.testConfig.chainConfigs[action.ProviderChain].IpPrefix, valCfg.Mnemonic, action.RelayerConfig, ) @@ -69,21 +66,20 @@ type UpdateLightClientAction struct { ClientID string } -func (tc TestConfig) updateLightClient( +func (tc Chain) updateLightClient( action UpdateLightClientAction, verbose bool, ) { // retrieve a trusted height of the consumer light client - trustedHeight := tc.getTrustedHeight(action.HostChain, action.ClientID, 2) + revHeight, _ := tc.target.GetTrustedHeight(action.HostChain, action.ClientID, 2) - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tc.containerConfig.InstanceName, "hermes", + cmd := tc.target.ExecCommand("hermes", "--config", action.RelayerConfig, "update", "client", "--client", action.ClientID, "--host-chain", string(action.HostChain), - "--trusted-height", strconv.Itoa(int(trustedHeight.RevisionHeight)), + "--trusted-height", strconv.Itoa(int(revHeight)), ) if verbose { log.Println("UpdateLightClientAction cmd:", cmd.String()) diff --git a/tests/e2e/actions_sovereign_chain.go b/tests/e2e/actions_sovereign_chain.go index 3e5035aa5c..96d308ba57 100644 --- a/tests/e2e/actions_sovereign_chain.go +++ b/tests/e2e/actions_sovereign_chain.go @@ -17,12 +17,11 @@ type StartSovereignChainAction struct { // calls a simplified startup script (start-sovereign.sh) and runs a validator node // upgrades are simpler with a single validator node since only one node needs to be upgraded -func (tr TestConfig) startSovereignChain( +func (tr Chain) startSovereignChain( action StartSovereignChainAction, - target ExecutionTarget, verbose bool, ) { - chainConfig := tr.chainConfigs["sover"] + chainConfig := tr.testConfig.chainConfigs["sover"] type jsonValAttrs struct { Mnemonic string `json:"mnemonic"` Allocation string `json:"allocation"` @@ -40,18 +39,18 @@ func (tr TestConfig) startSovereignChain( var validators []jsonValAttrs for _, val := range action.Validators { validators = append(validators, jsonValAttrs{ - Mnemonic: tr.validatorConfigs[val.Id].Mnemonic, - NodeKey: tr.validatorConfigs[val.Id].NodeKey, + Mnemonic: tr.testConfig.validatorConfigs[val.Id].Mnemonic, + NodeKey: tr.testConfig.validatorConfigs[val.Id].NodeKey, ValId: fmt.Sprint(val.Id), - PrivValidatorKey: tr.validatorConfigs[val.Id].PrivValidatorKey, + PrivValidatorKey: tr.testConfig.validatorConfigs[val.Id].PrivValidatorKey, Allocation: fmt.Sprint(val.Allocation) + "stake", Stake: fmt.Sprint(val.Stake) + "stake", - IpSuffix: tr.validatorConfigs[val.Id].IpSuffix, + IpSuffix: tr.testConfig.validatorConfigs[val.Id].IpSuffix, - ConsumerMnemonic: tr.validatorConfigs[val.Id].ConsumerMnemonic, - ConsumerPrivValidatorKey: tr.validatorConfigs[val.Id].ConsumerPrivValidatorKey, + ConsumerMnemonic: tr.testConfig.validatorConfigs[val.Id].ConsumerMnemonic, + ConsumerPrivValidatorKey: tr.testConfig.validatorConfigs[val.Id].ConsumerPrivValidatorKey, // if true node will be started with consumer key for each consumer chain - StartWithConsumerKey: tr.validatorConfigs[val.Id].UseConsumerKey, + StartWithConsumerKey: tr.testConfig.validatorConfigs[val.Id].UseConsumerKey, }) } @@ -69,10 +68,10 @@ func (tr TestConfig) startSovereignChain( } isConsumer := chainConfig.BinaryName != "interchain-security-pd" - testScriptPath := target.GetTestScriptPath(isConsumer, "start-sovereign.sh") - cmd := target.ExecCommand("/bin/bash", testScriptPath, chainConfig.BinaryName, string(vals), + testScriptPath := tr.target.GetTestScriptPath(isConsumer, "start-sovereign.sh") + cmd := tr.target.ExecCommand("/bin/bash", testScriptPath, chainConfig.BinaryName, string(vals), string(chainConfig.ChainId), chainConfig.IpPrefix, genesisChanges, - tr.tendermintConfigOverride) + tr.testConfig.tendermintConfigOverride) cmdReader, err := cmd.StdoutPipe() if err != nil { @@ -101,7 +100,7 @@ func (tr TestConfig) startSovereignChain( tr.addChainToRelayer(AddChainToRelayerAction{ Chain: action.Chain, Validator: action.Validators[0].Id, - }, target, verbose) + }, verbose) } type LegacyUpgradeProposalAction struct { @@ -111,7 +110,7 @@ type LegacyUpgradeProposalAction struct { UpgradeHeight uint64 } -func (tr *TestConfig) submitLegacyUpgradeProposal(action LegacyUpgradeProposalAction, target ExecutionTarget, verbose bool) { +func (tr *Chain) submitLegacyUpgradeProposal(action LegacyUpgradeProposalAction, verbose bool) { submit := fmt.Sprintf( `%s tx gov submit-legacy-proposal software-upgrade %s \ --title %s \ @@ -127,16 +126,16 @@ func (tr *TestConfig) submitLegacyUpgradeProposal(action LegacyUpgradeProposalAc --node %s \ --no-validate \ -y`, - tr.chainConfigs[ChainID("sover")].BinaryName, + tr.testConfig.chainConfigs[ChainID("sover")].BinaryName, action.UpgradeTitle, action.UpgradeTitle, fmt.Sprint(action.UpgradeHeight), action.Proposer, - tr.chainConfigs[ChainID("sover")].ChainId, + tr.testConfig.chainConfigs[ChainID("sover")].ChainId, tr.getValidatorHome(ChainID("sover"), action.Proposer), tr.getValidatorNode(ChainID("sover"), action.Proposer), ) - cmd := target.ExecCommand("/bin/bash", "-c", submit) + cmd := tr.target.ExecCommand("/bin/bash", "-c", submit) if verbose { fmt.Println("submitUpgradeProposal cmd:", cmd.String()) @@ -155,7 +154,7 @@ type WaitUntilBlockAction struct { Chain ChainID } -func (tr *TestConfig) waitUntilBlockOnChain(action WaitUntilBlockAction) { +func (tr *Chain) waitUntilBlockOnChain(action WaitUntilBlockAction) { fmt.Println("waitUntilBlockOnChain is waiting for block:", action.Block) tr.waitUntilBlock(action.Chain, action.Block, 120*time.Second) fmt.Println("waitUntilBlockOnChain done waiting for block:", action.Block) diff --git a/tests/e2e/config.go b/tests/e2e/config.go index c48c397a51..7f6062b65c 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -7,6 +7,7 @@ import ( "strconv" "time" + e2e "github.com/cosmos/interchain-security/v5/tests/e2e/testlib" "golang.org/x/mod/semver" ) @@ -69,10 +70,13 @@ var hermesTemplates = map[string]string{ `, } -// TODO: Determine if user defined type (wrapping a primitive string) is desired in long run +// type aliases for shared types from e2e package type ( - ChainID string - ValidatorID string + ChainID = e2e.ChainID + ValidatorID = e2e.ValidatorID + ValidatorConfig = e2e.ValidatorConfig + ChainConfig = e2e.ChainConfig + ContainerConfig = e2e.ContainerConfig // will be moved back ) // Supported Test configurations to be used with GetTestConfig @@ -89,98 +93,6 @@ const ( CompatibilityTestCfg TestConfigType = "compatibility" ) -// Attributes that are unique to a validator. Allows us to map (part of) -// the set of strings defined above to a set of viable validators -type ValidatorConfig struct { - // Seed phrase to generate a secp256k1 key used by the validator on the provider - Mnemonic string - // Validator account address on provider marshaled to string using Bech32 - // with Bech32Prefix = ProviderAccountPrefix - DelAddress string - // Validator account address on provider marshaled to string using Bech32 - // with Bech32Prefix = ConsumerAccountPrefix - DelAddressOnConsumer string - // Validator operator address on provider marshaled to string using Bech32 - // with Bech32Prefix = ProviderAccountPrefix - ValoperAddress string - // Validator operator address on provider marshaled to string using Bech32 - // with Bech32Prefix = ConsumerAccountPrefix - ValoperAddressOnConsumer string - // Validator consensus address on provider marshaled to string using Bech32 - // with Bech32Prefix = ProviderAccountPrefix. It matches the PrivValidatorKey below. - ValconsAddress string - // Validator consensus address on provider marshaled to string using Bech32 - // with Bech32Prefix = ConsumerAccountPrefix. - ValconsAddressOnConsumer string - // Key used for consensus on provider - PrivValidatorKey string - NodeKey string - // Must be an integer greater than 0 and less than 253 - IpSuffix string - - // consumer chain key assignment data - // keys are used on a new node - - // Seed phrase to generate a secp256k1 key used by the validator on the consumer - ConsumerMnemonic string - // Validator account address on consumer marshaled to string using Bech32 - // with Bech32Prefix = ConsumerAccountPrefix - ConsumerDelAddress string - // Validator account address on consumer marshaled to string using Bech32 - // with Bech32Prefix = ProviderAccountPrefix - ConsumerDelAddressOnProvider string - // Validator operator address on consumer marshaled to string using Bech32 - // with Bech32Prefix = ConsumerAccountPrefix - ConsumerValoperAddress string - // Validator operator address on consumer marshaled to string using Bech32 - // with Bech32Prefix = ProviderAccountPrefix - ConsumerValoperAddressOnProvider string - // Validator consensus address on consumer marshaled to string using Bech32 - // with Bech32Prefix = ConsumerAccountPrefix. It matches the PrivValidatorKey below. - ConsumerValconsAddress string - // Validator consensus address on consumer marshaled to string using Bech32 - // with Bech32Prefix = ProviderAccountPrefix. - ConsumerValconsAddressOnProvider string - ConsumerValPubKey string - // Key used for consensus on consumer - ConsumerPrivValidatorKey string - ConsumerNodeKey string - UseConsumerKey bool // if true the validator node will start with consumer key -} - -// Attributes that are unique to a chain. Allows us to map (part of) -// the set of strings defined above to a set of viable chains -type ChainConfig struct { - ChainId ChainID - // The account prefix configured on the chain. For example, on the Hub, this is "cosmos" - AccountPrefix string - // Must be unique per chain - IpPrefix string - VotingWaitTime uint - // Any transformations to apply to the genesis file of all chains instantiated with this chain config, as a jq string. - // Example: ".app_state.gov.params.voting_period = \"5s\" | .app_state.slashing.params.signed_blocks_window = \"2\" | .app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\"" - GenesisChanges string - BinaryName string - - // binary to use after upgrade height - UpgradeBinary string -} - -type ContainerConfig struct { - ContainerName string - InstanceName string - CcvVersion string - Now time.Time -} - -type TargetConfig struct { - gaiaTag string - localSdkPath string - useGaia bool - providerVersion string - consumerVersion string -} - type TestConfig struct { // These are the non altered values during a typical test run, where multiple test runs can exist // to validate different action sequences and corresponding state checks. @@ -221,7 +133,8 @@ func getIcsVersion(reference string) string { // remove build suffix return semver.Canonical(reference) } - for _, tag := range []string{"v2.0.0", "v2.4.0", "v2.4.0-lsm", "v3.1.0", "v3.2.0", "v3.3.0", "v4.0.0"} { + + for _, tag := range []string{"v2.0.0", "v2.4.0", "v2.4.0-lsm", "v3.1.0", "v3.2.0", "v3.3.0", "v4.0.0", "v4.1.1", "v4.1.1-lsm"} { //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments cmd := exec.Command("git", "merge-base", "--is-ancestor", reference, tag) out, err := cmd.CombinedOutput() @@ -412,7 +325,7 @@ func CompatibilityTestConfig(providerVersion, consumerVersion string) TestConfig var providerConfig, consumerConfig ChainConfig if !semver.IsValid(consumerVersion) { - fmt.Println("Using default provider chain config") + fmt.Printf("Invalid sem-version '%s' for provider.Using default provider chain config\n", consumerVersion) consumerConfig = testCfg.chainConfigs[ChainID("consu")] } else if semver.Compare(consumerVersion, "v3.0.0") < 0 { fmt.Println("Using consumer chain config for v2.0.0") @@ -442,6 +355,20 @@ func CompatibilityTestConfig(providerVersion, consumerVersion string) TestConfig ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", } + } else if semver.Compare(consumerVersion, "v5.0.0-alpha1") < 0 { // TODO: change this to first published v5 release - once it's out + fmt.Println("Using consumer chain config for v4.x.x") + consumerConfig = ChainConfig{ + 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\"", + } } else { fmt.Println("Using default consumer chain config") consumerConfig = testCfg.chainConfigs[ChainID("consu")] @@ -449,7 +376,7 @@ func CompatibilityTestConfig(providerVersion, consumerVersion string) TestConfig // Get the provider chain config for a specific version if !semver.IsValid(providerVersion) { - fmt.Println("Using default provider chain config") + fmt.Printf("Invalid sem-version '%s' for provider. Using default provider chain config\n", providerVersion) providerConfig = testCfg.chainConfigs[ChainID("provi")] } else if semver.Compare(providerVersion, "v3.0.0") < 0 { fmt.Println("Using provider chain config for v2.x.x") @@ -487,6 +414,25 @@ func CompatibilityTestConfig(providerVersion, consumerVersion string) TestConfig ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling ".app_state.provider.params.slash_meter_replenish_period = \"3s\"", } + } else if semver.Compare(providerVersion, "v5.0.0-alpha1") < 0 { //TODO: MOV THIS BACK TO "v5.0.0" + fmt.Println("Using provider chain config for v4.1.x") + providerConfig = ChainConfig{ + ChainId: ChainID("provi"), + AccountPrefix: ProviderAccountPrefix, + BinaryName: "interchain-security-pd", + IpPrefix: "7.7.7", + VotingWaitTime: 20, + GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + + // 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", + } } else { fmt.Println("Using default provider chain config") providerConfig = testCfg.chainConfigs[ChainID("provi")] @@ -944,7 +890,7 @@ func getValidatorConfigFromVersion(providerVersion, consumerVersion string) map[ }, } case "v4.0.0": - fmt.Println("Using current default validator configs: ", providerVersion) + fmt.Println("Using current validator configs v4.0.0: ", providerVersion) validatorCfg = map[ValidatorID]ValidatorConfig{ ValidatorID("alice"): { Mnemonic: "pave immune ethics wrap gain ceiling always holiday employ earth tumble real ice engage false unable carbon equal fresh sick tattoo nature pupil nuclear", diff --git a/tests/e2e/json_marshal_test.go b/tests/e2e/json_marshal_test.go index 9a7694587f..1265063e69 100644 --- a/tests/e2e/json_marshal_test.go +++ b/tests/e2e/json_marshal_test.go @@ -9,6 +9,7 @@ import ( gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + e2e "github.com/cosmos/interchain-security/v5/tests/e2e/testlib" "github.com/davecgh/go-spew/spew" ) @@ -45,7 +46,7 @@ func TestProposalUnmarshal(t *testing.T) { t.Errorf("Unexpected error while unmarshalling: %v", err) } - actualProposal, err := UnmarshalProposalWithType(propAndType.RawProposal, propAndType.Type) + actualProposal, err := e2e.UnmarshalProposalWithType(propAndType.RawProposal, propAndType.Type) if err != nil { t.Errorf("Unexpected error while unmarshalling\n error: %v\n Raw proposal: %v\n Type: %v", err, spew.Sdump(propAndType.RawProposal), propAndType.Type) } diff --git a/tests/e2e/json_utils.go b/tests/e2e/json_utils.go index d94f599588..e1e0b67fc6 100644 --- a/tests/e2e/json_utils.go +++ b/tests/e2e/json_utils.go @@ -6,23 +6,6 @@ import ( "reflect" ) -// stores a proposal as a raw json, together with its type -type ProposalAndType struct { - RawProposal json.RawMessage - Type string -} - -type ( - // to have a ChainState object that does not have the overridden Marshal/Unmarshal method - ChainStateCopy ChainState - - // duplicated from the ChainState with a minor change to the Proposals field - ChainStateWithProposalTypes struct { - ChainStateCopy - Proposals *map[uint]ProposalAndType // the only thing changed from the real ChainState - } -) - // MarshalJSON marshals a step into JSON while including the type of the action. func (step Step) MarshalJSON() ([]byte, error) { actionType := reflect.TypeOf(step.Action) @@ -298,89 +281,3 @@ func UnmarshalMapToActionType(rawAction json.RawMessage, actionTypeString string } return nil, err } - -// custom marshal and unmarshal functions for the chainstate that convert proposals to/from the auxiliary type with type info - -// MarshalJSON transforms the ChainState into a ChainStateWithProposalTypes by adding type info to the proposals -func (c ChainState) MarshalJSON() ([]byte, error) { - chainStateCopy := ChainStateCopy(c) - chainStateWithProposalTypes := ChainStateWithProposalTypes{chainStateCopy, nil} - if c.Proposals != nil { - proposalsWithTypes := make(map[uint]ProposalAndType) - for k, v := range *c.Proposals { - rawMessage, err := json.Marshal(v) - if err != nil { - return nil, err - } - proposalsWithTypes[k] = ProposalAndType{rawMessage, reflect.TypeOf(v).String()} - } - chainStateWithProposalTypes.Proposals = &proposalsWithTypes - } - return json.Marshal(chainStateWithProposalTypes) -} - -// UnmarshalJSON unmarshals the ChainStateWithProposalTypes into a ChainState by removing the type info from the proposals and getting back standard proposals -func (c *ChainState) UnmarshalJSON(data []byte) error { - chainStateWithProposalTypes := ChainStateWithProposalTypes{} - err := json.Unmarshal(data, &chainStateWithProposalTypes) - if err != nil { - return err - } - - chainState := ChainState(chainStateWithProposalTypes.ChainStateCopy) - *c = chainState - - if chainStateWithProposalTypes.Proposals != nil { - proposals := make(map[uint]Proposal) - for k, v := range *chainStateWithProposalTypes.Proposals { - proposal, err := UnmarshalProposalWithType(v.RawProposal, v.Type) - if err != nil { - return err - } - proposals[k] = proposal - } - c.Proposals = &proposals - } - return nil -} - -// UnmarshalProposalWithType takes a JSON object and a proposal type and marshals into an object of the corresponding proposal. -func UnmarshalProposalWithType(inputMap json.RawMessage, proposalType string) (Proposal, error) { - var err error - switch proposalType { - case "main.TextProposal": - prop := TextProposal{} - err := json.Unmarshal(inputMap, &prop) - if err == nil { - return prop, nil - } - case "main.ConsumerAdditionProposal": - prop := ConsumerAdditionProposal{} - err := json.Unmarshal(inputMap, &prop) - if err == nil { - return prop, nil - } - case "main.UpgradeProposal": - prop := UpgradeProposal{} - err := json.Unmarshal(inputMap, &prop) - if err == nil { - return prop, nil - } - case "main.ConsumerRemovalProposal": - prop := ConsumerRemovalProposal{} - err := json.Unmarshal(inputMap, &prop) - if err == nil { - return prop, nil - } - case "main.IBCTransferParamsProposal": - prop := IBCTransferParamsProposal{} - err := json.Unmarshal(inputMap, &prop) - if err == nil { - return prop, nil - } - default: - return nil, fmt.Errorf("%s is not a known proposal type", proposalType) - } - - return nil, err -} diff --git a/tests/e2e/state.go b/tests/e2e/state.go index e4cb225392..86882ddc56 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -11,192 +11,105 @@ import ( "time" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + e2e "github.com/cosmos/interchain-security/v5/tests/e2e/testlib" "github.com/kylelemons/godebug/pretty" "github.com/tidwall/gjson" "gopkg.in/yaml.v2" ) -type State map[ChainID]ChainState - -type ChainState struct { - ValBalances *map[ValidatorID]uint - Proposals *map[uint]Proposal - ProposedConsumerChains *[]string - ValPowers *map[ValidatorID]uint - StakedTokens *map[ValidatorID]uint - IBCTransferParams *IBCTransferParams - Rewards *Rewards - ConsumerChains *map[ChainID]bool - AssignedKeys *map[ValidatorID]string - ProviderKeys *map[ValidatorID]string // validatorID: validator provider key - ConsumerPendingPacketQueueSize *uint // Only relevant to consumer chains - RegisteredConsumerRewardDenoms *[]string - ClientsFrozenHeights *map[string]clienttypes.Height -} - -type Proposal interface { - isProposal() -} -type TextProposal struct { - Title string - Description string - Deposit uint - Status string -} - -func (p TextProposal) isProposal() {} - -type IBCTransferParamsProposal struct { - Title string - Deposit uint - Status string - Params IBCTransferParams -} - -func (ibct IBCTransferParamsProposal) isProposal() {} - -type ConsumerAdditionProposal struct { - Deposit uint - Chain ChainID - SpawnTime int - InitialHeight clienttypes.Height - Status string -} - -type UpgradeProposal struct { - Title string - Description string - UpgradeHeight uint64 - Type string - Deposit uint - Status string -} - -func (p UpgradeProposal) isProposal() {} - -func (p ConsumerAdditionProposal) isProposal() {} - -type ConsumerRemovalProposal struct { - Deposit uint - Chain ChainID - StopTime int - Status string -} - -func (p ConsumerRemovalProposal) isProposal() {} - -type Rewards struct { - IsRewarded map[ValidatorID]bool - // if true it will calculate if the validator/delegator is rewarded between 2 successive blocks, - // otherwise it will calculate if it received any rewards since the 1st block - IsIncrementalReward bool - // if true checks rewards for "stake" token, otherwise checks rewards from - // other chains (e.g. false is used to check if provider received rewards from a consumer chain) - IsNativeDenom bool -} - -type ParamsProposal struct { - Deposit uint - Status string - Subspace string - Key string - Value string -} - -func (p ParamsProposal) isProposal() {} +// type aliases +type ( + ChainState = e2e.ChainState + Proposal = e2e.Proposal + Rewards = e2e.Rewards + TextProposal = e2e.TextProposal + UpgradeProposal = e2e.UpgradeProposal + ConsumerAdditionProposal = e2e.ConsumerAdditionProposal + ConsumerRemovalProposal = e2e.ConsumerRemovalProposal + IBCTransferParams = e2e.IBCTransferParams + IBCTransferParamsProposal = e2e.IBCTransferParamsProposal + Param = e2e.Param + ParamsProposal = e2e.ParamsProposal + TargetDriver = e2e.TargetDriver +) -type Param struct { - Subspace string - Key string - Value string -} +type State map[ChainID]ChainState -type IBCTransferParams struct { - SendEnabled bool `json:"send_enabled"` - ReceiveEnabled bool `json:"receive_enabled"` +type Chain struct { + target e2e.TargetDriver + testConfig TestConfig } -func (tr TestConfig) getState(modelState State, verbose bool) State { - systemState := State{} - for k, modelState := range modelState { - if verbose { - fmt.Println("Getting model state for chain: ", k) - } - systemState[k] = tr.getChainState(k, modelState) - } - - return systemState -} +func (tr Chain) GetChainState(chain ChainID, modelState ChainState) ChainState { -func (tr TestConfig) getChainState(chain ChainID, modelState ChainState) ChainState { chainState := ChainState{} if modelState.ValBalances != nil { - valBalances := tr.getBalances(chain, *modelState.ValBalances) + valBalances := tr.GetBalances(chain, *modelState.ValBalances) chainState.ValBalances = &valBalances } if modelState.Proposals != nil { - proposals := tr.getProposals(chain, *modelState.Proposals) + proposals := tr.GetProposals(chain, *modelState.Proposals) chainState.Proposals = &proposals } if modelState.ProposedConsumerChains != nil { - proposedConsumerChains := tr.getProposedConsumerChains(chain) + proposedConsumerChains := tr.GetProposedConsumerChains(chain) chainState.ProposedConsumerChains = &proposedConsumerChains } if modelState.ValPowers != nil { tr.waitBlocks(chain, 1, 10*time.Second) - powers := tr.getValPowers(chain, *modelState.ValPowers) + powers := tr.GetValPowers(chain, *modelState.ValPowers) chainState.ValPowers = &powers } if modelState.StakedTokens != nil { - representPowers := tr.getStakedTokens(chain, *modelState.StakedTokens) + representPowers := tr.GetStakedTokens(chain, *modelState.StakedTokens) chainState.StakedTokens = &representPowers } if modelState.IBCTransferParams != nil { - params := tr.getIBCTransferParams(chain) + params := tr.target.GetIBCTransferParams(chain) chainState.IBCTransferParams = ¶ms } if modelState.Rewards != nil { - rewards := tr.getRewards(chain, *modelState.Rewards) + rewards := tr.GetRewards(chain, *modelState.Rewards) chainState.Rewards = &rewards } if modelState.ConsumerChains != nil { - chains := tr.getConsumerChains(chain) + chains := tr.target.GetConsumerChains(chain) chainState.ConsumerChains = &chains } if modelState.AssignedKeys != nil { - assignedKeys := tr.getConsumerAddresses(chain, *modelState.AssignedKeys) + assignedKeys := tr.GetConsumerAddresses(chain, *modelState.AssignedKeys) chainState.AssignedKeys = &assignedKeys } if modelState.ProviderKeys != nil { - providerKeys := tr.getProviderAddresses(chain, *modelState.ProviderKeys) + providerKeys := tr.GetProviderAddresses(chain, *modelState.ProviderKeys) chainState.ProviderKeys = &providerKeys } if modelState.RegisteredConsumerRewardDenoms != nil { - registeredConsumerRewardDenoms := tr.getRegisteredConsumerRewardDenoms(chain) + registeredConsumerRewardDenoms := tr.target.GetRegisteredConsumerRewardDenoms(chain) chainState.RegisteredConsumerRewardDenoms = ®isteredConsumerRewardDenoms } if modelState.ClientsFrozenHeights != nil { chainClientsFrozenHeights := map[string]clienttypes.Height{} for id := range *modelState.ClientsFrozenHeights { - chainClientsFrozenHeights[id] = tr.getClientFrozenHeight(chain, id) + chainClientsFrozenHeights[id] = tr.GetClientFrozenHeight(chain, id) } chainState.ClientsFrozenHeights = &chainClientsFrozenHeights } if modelState.ConsumerPendingPacketQueueSize != nil { - pendingPacketQueueSize := tr.getPendingPacketQueueSize(chain) + pendingPacketQueueSize := tr.target.GetPendingPacketQueueSize(chain) chainState.ConsumerPendingPacketQueueSize = &pendingPacketQueueSize } @@ -207,44 +120,22 @@ func (tr TestConfig) getChainState(chain ChainID, modelState ChainState) ChainSt return chainState } -var blockHeightRegex = regexp.MustCompile(`block_height: "(\d+)"`) - -func (tr TestConfig) getBlockHeight(chain ChainID) uint { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, - - "query", "tendermint-validator-set", - - `--node`, tr.getQueryNode(chain), - ).CombinedOutput() - if err != nil { - log.Fatal(err, "\n", string(bz)) - } - - blockHeight, err := strconv.Atoi(blockHeightRegex.FindStringSubmatch(string(bz))[1]) - if err != nil { - log.Fatal(err) - } - - return uint(blockHeight) -} - -func (tr TestConfig) waitBlocks(chain ChainID, blocks uint, timeout time.Duration) { - if tr.useCometmock { +func (tr Chain) waitBlocks(chain ChainID, blocks uint, timeout time.Duration) { + if tr.testConfig.useCometmock { // call advance_blocks method on cometmock // curl -H 'Content-Type: application/json' -H 'Accept:application/json' --data '{"jsonrpc":"2.0","method":"advance_blocks","params":{"num_blocks": "36000000"},"id":1}' 127.0.0.1:22331 - tcpAddress := tr.getQueryNodeRPCAddress(chain) + tcpAddress := tr.target.GetQueryNodeRPCAddress(chain) method := "advance_blocks" params := fmt.Sprintf(`{"num_blocks": "%d"}`, blocks) tr.curlJsonRPCRequest(method, params, tcpAddress) return } - startBlock := tr.getBlockHeight(chain) + startBlock := tr.target.GetBlockHeight(chain) start := time.Now() for { - thisBlock := tr.getBlockHeight(chain) + thisBlock := tr.target.GetBlockHeight(chain) if thisBlock >= startBlock+blocks { return } @@ -255,10 +146,10 @@ func (tr TestConfig) waitBlocks(chain ChainID, blocks uint, timeout time.Duratio } } -func (tr TestConfig) waitUntilBlock(chain ChainID, block uint, timeout time.Duration) { +func (tr Chain) waitUntilBlock(chain ChainID, block uint, timeout time.Duration) { start := time.Now() for { - thisBlock := tr.getBlockHeight(chain) + thisBlock := tr.target.GetBlockHeight(chain) if thisBlock >= block { return } @@ -269,61 +160,181 @@ func (tr TestConfig) waitUntilBlock(chain ChainID, block uint, timeout time.Dura } } -func (tr TestConfig) getBalances(chain ChainID, modelState map[ValidatorID]uint) map[ValidatorID]uint { +func (tr Chain) GetBalances(chain ChainID, modelState map[ValidatorID]uint) map[ValidatorID]uint { actualState := map[ValidatorID]uint{} for k := range modelState { - actualState[k] = tr.getBalance(chain, k) + actualState[k] = tr.target.GetBalance(chain, k) } return actualState } -func (tr TestConfig) getProposals(chain ChainID, modelState map[uint]Proposal) map[uint]Proposal { +func (tr Chain) GetClientFrozenHeight(chain ChainID, clientID string) clienttypes.Height { + revNumber, revHeight := tr.target.GetClientFrozenHeight(chain, clientID) + return clienttypes.Height{RevisionHeight: uint64(revHeight), RevisionNumber: uint64(revNumber)} +} + +func (tr Chain) GetProposedConsumerChains(chain ChainID) []string { + tr.waitBlocks(chain, 1, 10*time.Second) + return tr.target.GetProposedConsumerChains(chain) +} + +func (tr Chain) GetProposals(chain ChainID, modelState map[uint]Proposal) map[uint]Proposal { actualState := map[uint]Proposal{} for k := range modelState { - actualState[k] = tr.getProposal(chain, k) + actualState[k] = tr.target.GetProposal(chain, k) } return actualState } -func (tr TestConfig) getValPowers(chain ChainID, modelState map[ValidatorID]uint) map[ValidatorID]uint { +func (tr Chain) GetValPowers(chain ChainID, modelState map[ValidatorID]uint) map[ValidatorID]uint { actualState := map[ValidatorID]uint{} + validatorConfigs := tr.testConfig.validatorConfigs for k := range modelState { - actualState[k] = tr.getValPower(chain, k) + valAddresses := map[string]bool{} + if chain == ChainID("provi") { + // use binary with Bech32Prefix set to ProviderAccountPrefix + valAddresses[validatorConfigs[k].ValconsAddress] = true + } else { + // use binary with Bech32Prefix set to ConsumerAccountPrefix + valAddresses[validatorConfigs[k].ValconsAddressOnConsumer] = true + valAddresses[validatorConfigs[k].ConsumerValconsAddress] = true + } + actualState[k] = tr.target.GetValPower(chain, k) } return actualState } -func (tr TestConfig) getStakedTokens(chain ChainID, modelState map[ValidatorID]uint) map[ValidatorID]uint { +func (tr Chain) GetStakedTokens(chain ChainID, modelState map[ValidatorID]uint) map[ValidatorID]uint { actualState := map[ValidatorID]uint{} - for k := range modelState { - actualState[k] = tr.getValStakedTokens(chain, k) + for validator := range modelState { + validatorConfigs := tr.testConfig.validatorConfigs + valoperAddress := validatorConfigs[validator].ValoperAddress + if chain != ChainID("provi") { + // use binary with Bech32Prefix set to ConsumerAccountPrefix + if validatorConfigs[validator].UseConsumerKey { + valoperAddress = validatorConfigs[validator].ConsumerValoperAddress + } else { + // use the same address as on the provider but with different prefix + valoperAddress = validatorConfigs[validator].ValoperAddressOnConsumer + } + } + + actualState[validator] = tr.target.GetValStakedTokens(chain, valoperAddress) } return actualState } -func (tr TestConfig) getRewards(chain ChainID, modelState Rewards) Rewards { +func (tr Chain) GetRewards(chain ChainID, modelState Rewards) Rewards { receivedRewards := map[ValidatorID]bool{} - currentBlock := tr.getBlockHeight(chain) + currentBlock := tr.target.GetBlockHeight(chain) tr.waitBlocks(chain, 1, 10*time.Second) - nextBlock := tr.getBlockHeight(chain) + nextBlock := tr.target.GetBlockHeight(chain) tr.waitBlocks(chain, 1, 10*time.Second) if !modelState.IsIncrementalReward { currentBlock = 1 } for k := range modelState.IsRewarded { - receivedRewards[k] = tr.getReward(chain, k, nextBlock, modelState.IsNativeDenom) > tr.getReward(chain, k, currentBlock, modelState.IsNativeDenom) + receivedRewards[k] = tr.target.GetReward(chain, k, nextBlock, modelState.IsNativeDenom) > tr.target.GetReward(chain, k, currentBlock, modelState.IsNativeDenom) } return Rewards{IsRewarded: receivedRewards, IsIncrementalReward: modelState.IsIncrementalReward, IsNativeDenom: modelState.IsNativeDenom} } -func (tr TestConfig) getReward(chain ChainID, validator ValidatorID, blockHeight uint, isNativeDenom bool) float64 { +func (tr Chain) GetConsumerAddresses(chain ChainID, modelState map[ValidatorID]string) map[ValidatorID]string { + actualState := map[ValidatorID]string{} + for k := range modelState { + actualState[k] = tr.target.GetConsumerAddress(chain, k) + } + + return actualState +} + +func (tr Chain) GetProviderAddresses(chain ChainID, modelState map[ValidatorID]string) map[ValidatorID]string { + actualState := map[ValidatorID]string{} + for k := range modelState { + actualState[k] = tr.target.GetProviderAddressFromConsumer(chain, k) + } + + return actualState +} + +func (tr Chain) getValidatorNode(chain ChainID, validator ValidatorID) string { + // for CometMock, validatorNodes are all the same address as the query node (which is CometMocks address) + if tr.testConfig.useCometmock { + return tr.target.GetQueryNode(chain) + } + + return "tcp://" + tr.getValidatorIP(chain, validator) + ":26658" +} + +func (tr Chain) getValidatorIP(chain ChainID, validator ValidatorID) string { + return tr.testConfig.chainConfigs[chain].IpPrefix + "." + tr.testConfig.validatorConfigs[validator].IpSuffix +} + +func (tr Chain) getValidatorHome(chain ChainID, validator ValidatorID) string { + return `/` + string(tr.testConfig.chainConfigs[chain].ChainId) + `/validator` + fmt.Sprint(validator) +} + +func (tr Chain) curlJsonRPCRequest(method, params, address string) { + cmd_template := `curl -H 'Content-Type: application/json' -H 'Accept:application/json' --data '{"jsonrpc":"2.0","method":"%s","params":%s,"id":1}' %s` + + cmd := tr.target.ExecCommand("bash", "-c", fmt.Sprintf(cmd_template, method, params, address)) + + verbosity := false + e2e.ExecuteCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) +} + +func uintPtr(i uint) *uint { + return &i +} + +type Commands struct { + containerConfig ContainerConfig // FIXME only needed for 'Now' time tracking + validatorConfigs map[ValidatorID]ValidatorConfig + chainConfigs map[ChainID]ChainConfig + target e2e.PlatformDriver +} + +func (tr Commands) ExecCommand(name string, arg ...string) *exec.Cmd { + return tr.target.ExecCommand(name, arg...) +} + +func (tr Commands) ExecDetachedCommand(name string, args ...string) *exec.Cmd { + return tr.target.ExecDetachedCommand(name, args...) +} + +func (tr Commands) GetTestScriptPath(isConsumer bool, script string) string { + return tr.target.GetTestScriptPath(isConsumer, script) +} + +func (tr Commands) GetBlockHeight(chain ChainID) uint { + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, + + "query", "tendermint-validator-set", + + `--node`, tr.GetQueryNode(chain), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + blockHeightRegex := regexp.MustCompile(`block_height: "(\d+)"`) + blockHeight, err := strconv.Atoi(blockHeightRegex.FindStringSubmatch(string(bz))[1]) + if err != nil { + log.Fatal(err) + } + + return uint(blockHeight) +} + +func (tr Commands) GetReward(chain ChainID, validator ValidatorID, blockHeight uint, isNativeDenom bool) float64 { valCfg := tr.validatorConfigs[validator] delAddresss := valCfg.DelAddress if chain != ChainID("provi") { @@ -336,12 +347,12 @@ func (tr TestConfig) getReward(chain ChainID, validator ValidatorID, blockHeight } } - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, + binaryName := tr.chainConfigs[chain].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "distribution", "delegation-total-rewards", "--delegator-address", delAddresss, `--height`, fmt.Sprint(blockHeight), - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ) @@ -359,47 +370,15 @@ func (tr TestConfig) getReward(chain ChainID, validator ValidatorID, blockHeight return gjson.Get(string(bz), denomCondition).Float() } -func (tr TestConfig) getBalance(chain ChainID, validator ValidatorID) uint { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - valCfg := tr.validatorConfigs[validator] - valDelAddress := valCfg.DelAddress - if chain != ChainID("provi") { - // use binary with Bech32Prefix set to ConsumerAccountPrefix - if valCfg.UseConsumerKey { - valDelAddress = valCfg.ConsumerDelAddress - } else { - // use the same address as on the provider but with different prefix - valDelAddress = valCfg.DelAddressOnConsumer - } - } - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, - - "query", "bank", "balances", - valDelAddress, - - `--node`, tr.getQueryNode(chain), - `-o`, `json`, - ) - bz, err := cmd.CombinedOutput() - if err != nil { - log.Fatal("getBalance() failed: ", cmd, ": ", err, "\n", string(bz)) - } - - amount := gjson.Get(string(bz), `balances.#(denom=="stake").amount`) - - return uint(amount.Uint()) -} - -var noProposalRegex = regexp.MustCompile(`doesn't exist: key not found`) - // interchain-securityd query gov proposals -func (tr TestConfig) getProposal(chain ChainID, proposal uint) Proposal { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, +func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { + var noProposalRegex = regexp.MustCompile(`doesn't exist: key not found`) + + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, "query", "gov", "proposal", fmt.Sprint(proposal), - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ).CombinedOutput() @@ -502,7 +481,7 @@ func (tr TestConfig) getProposal(chain ChainID, proposal uint) Proposal { } } - log.Fatal("received unknosn proposal type: ", propType, "proposal JSON:", string(bz)) + log.Fatal("received unknown proposal type: ", propType, "proposal JSON:", string(bz)) return nil } @@ -525,16 +504,16 @@ type ValPubKey struct { } // TODO (mpoke) Return powers for multiple validators -func (tr TestConfig) getValPower(chain ChainID, validator ValidatorID) uint { +func (tr Commands) GetValPower(chain ChainID, validator ValidatorID) uint { if *verbose { log.Println("getting validator power for chain: ", chain, " validator: ", validator) } - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - command := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, + binaryName := tr.chainConfigs[chain].BinaryName + command := tr.target.ExecCommand(binaryName, "query", "tendermint-validator-set", - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), ) bz, err := command.CombinedOutput() if err != nil { @@ -550,7 +529,8 @@ func (tr TestConfig) getValPower(chain ChainID, validator ValidatorID) uint { total, err := strconv.Atoi(valset.Pagination.Total) if err != nil { - log.Fatalf("strconv.Atoi returned an error while coonverting total for validator set: %v, input: %s, validator set: %s", err, valset.Pagination.Total, pretty.Sprint(valset)) + log.Fatalf("strconv.Atoi returned an error while converting total for validator set: %v, input: %s, validator set: %s, src:%s", + err, valset.Pagination.Total, pretty.Sprint(valset), string(bz)) } if total != len(valset.Validators) { @@ -584,25 +564,46 @@ func (tr TestConfig) getValPower(chain ChainID, validator ValidatorID) uint { return 0 } -func (tr TestConfig) getValStakedTokens(chain ChainID, validator ValidatorID) uint { - valoperAddress := tr.validatorConfigs[validator].ValoperAddress +func (tr Commands) GetBalance(chain ChainID, validator ValidatorID) uint { + valCfg := tr.validatorConfigs[validator] + valDelAddress := valCfg.DelAddress if chain != ChainID("provi") { // use binary with Bech32Prefix set to ConsumerAccountPrefix - if tr.validatorConfigs[validator].UseConsumerKey { - valoperAddress = tr.validatorConfigs[validator].ConsumerValoperAddress + if valCfg.UseConsumerKey { + valDelAddress = valCfg.ConsumerDelAddress } else { // use the same address as on the provider but with different prefix - valoperAddress = tr.validatorConfigs[validator].ValoperAddressOnConsumer + valDelAddress = valCfg.DelAddressOnConsumer } } - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, + binaryName := tr.chainConfigs[chain].BinaryName + cmd := tr.target.ExecCommand(binaryName, + + "query", "bank", "balances", + valDelAddress, + + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal("getBalance() failed: ", cmd, ": ", err, "\n", string(bz)) + } + + amount := gjson.Get(string(bz), `balances.#(denom=="stake").amount`) + + return uint(amount.Uint()) +} + +func (tr Commands) GetValStakedTokens(chain ChainID, valoperAddress string) uint { + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, "query", "staking", "validator", valoperAddress, - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ).CombinedOutput() if err != nil { @@ -614,15 +615,14 @@ func (tr TestConfig) getValStakedTokens(chain ChainID, validator ValidatorID) ui return uint(amount.Uint()) } -func (tr TestConfig) getParam(chain ChainID, param Param) string { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, - +func (tr Commands) GetParam(chain ChainID, param Param) string { + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, "query", "params", "subspace", param.Subspace, param.Key, - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ).CombinedOutput() if err != nil { @@ -634,11 +634,11 @@ func (tr TestConfig) getParam(chain ChainID, param Param) string { return value.String() } -func (tr TestConfig) getIBCTransferParams(chain ChainID) IBCTransferParams { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, +func (tr Commands) GetIBCTransferParams(chain ChainID) IBCTransferParams { + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, "query", "ibc-transfer", "params", - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ).CombinedOutput() if err != nil { @@ -653,14 +653,14 @@ func (tr TestConfig) getIBCTransferParams(chain ChainID) IBCTransferParams { return params } -// getConsumerChains returns a list of consumer chains that're being secured by the provider chain, +// GetConsumerChains returns a list of consumer chains that're being secured by the provider chain, // determined by querying the provider chain. -func (tr TestConfig) getConsumerChains(chain ChainID) map[ChainID]bool { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, +func (tr Commands) GetConsumerChains(chain ChainID) map[ChainID]bool { + binaryName := tr.chainConfigs[chain].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "provider", "list-consumer-chains", - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ) @@ -679,31 +679,13 @@ func (tr TestConfig) getConsumerChains(chain ChainID) map[ChainID]bool { return chains } -func (tr TestConfig) getConsumerAddresses(chain ChainID, modelState map[ValidatorID]string) map[ValidatorID]string { - actualState := map[ValidatorID]string{} - for k := range modelState { - actualState[k] = tr.getConsumerAddress(chain, k) - } - - return actualState -} - -func (tr TestConfig) getProviderAddresses(chain ChainID, modelState map[ValidatorID]string) map[ValidatorID]string { - actualState := map[ValidatorID]string{} - for k := range modelState { - actualState[k] = tr.getProviderAddressFromConsumer(chain, k) - } - - return actualState -} - -func (tr TestConfig) getConsumerAddress(consumerChain ChainID, validator ValidatorID) string { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[ChainID("provi")].BinaryName, +func (tr Commands) GetConsumerAddress(consumerChain ChainID, validator ValidatorID) string { + binaryName := tr.chainConfigs[ChainID("provi")].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "provider", "validator-consumer-key", string(consumerChain), tr.validatorConfigs[validator].ValconsAddress, - `--node`, tr.getQueryNode(ChainID("provi")), + `--node`, tr.GetQueryNode(ChainID("provi")), `-o`, `json`, ) bz, err := cmd.CombinedOutput() @@ -715,13 +697,13 @@ func (tr TestConfig) getConsumerAddress(consumerChain ChainID, validator Validat return addr } -func (tr TestConfig) getProviderAddressFromConsumer(consumerChain ChainID, validator ValidatorID) string { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[ChainID("provi")].BinaryName, +func (tr Commands) GetProviderAddressFromConsumer(consumerChain ChainID, validator ValidatorID) string { + binaryName := tr.chainConfigs[ChainID("provi")].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "provider", "validator-provider-key", string(consumerChain), tr.validatorConfigs[validator].ConsumerValconsAddressOnProvider, - `--node`, tr.getQueryNode(ChainID("provi")), + `--node`, tr.GetQueryNode(ChainID("provi")), `-o`, `json`, ) @@ -735,13 +717,12 @@ func (tr TestConfig) getProviderAddressFromConsumer(consumerChain ChainID, valid return addr } -func (tr TestConfig) getSlashMeter() int64 { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", - tr.containerConfig.InstanceName, tr.chainConfigs[ChainID("provi")].BinaryName, +func (tr Commands) GetSlashMeter() int64 { + binaryName := tr.chainConfigs[ChainID("provi")].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "provider", "throttle-state", - `--node`, tr.getQueryNode(ChainID("provi")), + `--node`, tr.GetQueryNode(ChainID("provi")), `-o`, `json`, ) bz, err := cmd.CombinedOutput() @@ -753,12 +734,12 @@ func (tr TestConfig) getSlashMeter() int64 { return slashMeter.Int() } -func (tr TestConfig) getRegisteredConsumerRewardDenoms(chain ChainID) []string { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, +func (tr Commands) GetRegisteredConsumerRewardDenoms(chain ChainID) []string { + binaryName := tr.chainConfigs[chain].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "provider", "registered-consumer-reward-denoms", - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ) bz, err := cmd.CombinedOutput() @@ -775,12 +756,12 @@ func (tr TestConfig) getRegisteredConsumerRewardDenoms(chain ChainID) []string { return rewardDenoms } -func (tr TestConfig) getPendingPacketQueueSize(chain ChainID) uint { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, +func (tr Commands) GetPendingPacketQueueSize(chain ChainID) uint { + binaryName := tr.chainConfigs[chain].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "ccvconsumer", "throttle-state", - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ) bz, err := cmd.CombinedOutput() @@ -796,62 +777,13 @@ func (tr TestConfig) getPendingPacketQueueSize(chain ChainID) uint { return uint(len(packetData)) } -func (tr TestConfig) getValidatorNode(chain ChainID, validator ValidatorID) string { - // for CometMock, validatorNodes are all the same address as the query node (which is CometMocks address) - if tr.useCometmock { - return tr.getQueryNode(chain) - } - - return "tcp://" + tr.getValidatorIP(chain, validator) + ":26658" -} - -func (tr TestConfig) getValidatorIP(chain ChainID, validator ValidatorID) string { - return tr.chainConfigs[chain].IpPrefix + "." + tr.validatorConfigs[validator].IpSuffix -} - -func (tr TestConfig) getValidatorHome(chain ChainID, validator ValidatorID) string { - return `/` + string(tr.chainConfigs[chain].ChainId) + `/validator` + fmt.Sprint(validator) -} - -// getQueryNode returns query node tcp address on chain. -func (tr TestConfig) getQueryNode(chain ChainID) string { - return fmt.Sprintf("tcp://%s", tr.getQueryNodeRPCAddress(chain)) -} - -func (tr TestConfig) getQueryNodeRPCAddress(chain ChainID) string { - return fmt.Sprintf("%s:26658", tr.getQueryNodeIP(chain)) -} - -// getQueryNodeIP returns query node IP for chain, -// ipSuffix is hardcoded to be 253 on all query nodes -// except for "sover" chain where there's only one node -func (tr TestConfig) getQueryNodeIP(chain ChainID) string { - if chain == ChainID("sover") { - // return address of first and only validator - return fmt.Sprintf("%s.%s", - tr.chainConfigs[chain].IpPrefix, - tr.validatorConfigs[ValidatorID("alice")].IpSuffix) - } - return fmt.Sprintf("%s.253", tr.chainConfigs[chain].IpPrefix) -} - -func (tr TestConfig) curlJsonRPCRequest(method, params, address string) { - cmd_template := `curl -H 'Content-Type: application/json' -H 'Accept:application/json' --data '{"jsonrpc":"2.0","method":"%s","params":%s,"id":1}' %s` - - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, "bash", "-c", fmt.Sprintf(cmd_template, method, params, address)) - - verbosity := false - executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) -} - -// getClientFrozenHeight returns the frozen height for a client with the given client ID +// GetClientFrozenHeight returns the frozen height for a client with the given client ID // by querying the hosting chain with the given chainID -func (tc TestConfig) getClientFrozenHeight(chain ChainID, clientID string) clienttypes.Height { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - cmd := exec.Command("docker", "exec", tc.containerConfig.InstanceName, tc.chainConfigs[ChainID("provi")].BinaryName, +func (tr Commands) GetClientFrozenHeight(chain ChainID, clientID string) (uint64, uint64) { + binaryName := tr.chainConfigs[ChainID("provi")].BinaryName + cmd := tr.target.ExecCommand(binaryName, "query", "ibc", "client", "state", clientID, - `--node`, tc.getQueryNode(ChainID("provi")), + `--node`, tr.GetQueryNode(ChainID("provi")), `-o`, `json`, ) @@ -872,16 +804,15 @@ func (tc TestConfig) getClientFrozenHeight(chain ChainID, clientID string) clien log.Fatal(err, "\n", string(bz)) } - return clienttypes.Height{RevisionHeight: uint64(revHeight), RevisionNumber: uint64(revNumber)} + return uint64(revHeight), uint64(revNumber) } -func (tc TestConfig) getTrustedHeight( +func (tr Commands) GetTrustedHeight( chain ChainID, clientID string, index int, -) clienttypes.Height { - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - configureNodeCmd := exec.Command("docker", "exec", tc.containerConfig.InstanceName, "hermes", +) (uint64, uint64) { + configureNodeCmd := tr.target.ExecCommand("hermes", "--json", "query", "client", "consensus", "--chain", string(chain), `--client`, clientID, ) @@ -919,15 +850,14 @@ func (tc TestConfig) getTrustedHeight( if err != nil { log.Fatal(err) } - return clienttypes.Height{RevisionHeight: uint64(revHeight), RevisionNumber: uint64(revNumber)} + return uint64(revHeight), uint64(revNumber) } -func (tr TestConfig) getProposedConsumerChains(chain ChainID) []string { - tr.waitBlocks(chain, 1, 10*time.Second) - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, +func (tr Commands) GetProposedConsumerChains(chain ChainID) []string { + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, "query", "provider", "list-proposed-consumer-chains", - `--node`, tr.getQueryNode(chain), + `--node`, tr.GetQueryNode(chain), `-o`, `json`, ).CombinedOutput() if err != nil { @@ -944,6 +874,24 @@ func (tr TestConfig) getProposedConsumerChains(chain ChainID) []string { return chains } -func uintPtr(i uint) *uint { - return &i +// getQueryNode returns query node tcp address on chain. +func (tr Commands) GetQueryNode(chain ChainID) string { + return fmt.Sprintf("tcp://%s", tr.GetQueryNodeRPCAddress(chain)) +} + +func (tr Commands) GetQueryNodeRPCAddress(chain ChainID) string { + return fmt.Sprintf("%s:26658", tr.GetQueryNodeIP(chain)) +} + +// getQueryNodeIP returns query node IP for chain, +// ipSuffix is hardcoded to be 253 on all query nodes +// except for "sover" chain where there's only one node +func (tr Commands) GetQueryNodeIP(chain ChainID) string { + if chain == ChainID("sover") { + // return address of first and only validator + return fmt.Sprintf("%s.%s", + tr.chainConfigs[chain].IpPrefix, + tr.validatorConfigs[ValidatorID("alice")].IpSuffix) + } + return fmt.Sprintf("%s.253", tr.chainConfigs[chain].IpPrefix) } diff --git a/tests/e2e/steps_compatibility.go b/tests/e2e/steps_compatibility.go index a3d639267e..3eaf1eae9c 100644 --- a/tests/e2e/steps_compatibility.go +++ b/tests/e2e/steps_compatibility.go @@ -57,7 +57,7 @@ func compstepsStartConsumerChain(consumerName string, proposalIndex, chainIndex Chain: ChainID(consumerName), SpawnTime: 0, InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, - Status: "PROPOSAL_STATUS_VOTING_PERIOD", + Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), // breaking change in SDK: gov.ProposalStatus(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD).String(), }, }, // not supported across major versions @@ -138,7 +138,7 @@ func compstepsStartConsumerChain(consumerName string, proposalIndex, chainIndex Chain: ChainID(consumerName), SpawnTime: 0, InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, - Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)), + Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)), //TODO: CHECK if this is bug on SDK SIDE!!!: should be as before gov.ProposalStatus(gov.ProposalStatus_PROPOSAL_STATUS_PASSED).String(), }, }, ValBalances: &map[ValidatorID]uint{ diff --git a/tests/e2e/test_driver.go b/tests/e2e/test_driver.go index 0ca3bc29ed..a4c2424ff5 100644 --- a/tests/e2e/test_driver.go +++ b/tests/e2e/test_driver.go @@ -5,7 +5,9 @@ import ( "log" "reflect" + v4 "github.com/cosmos/interchain-security/v5/tests/e2e/v4" "github.com/kylelemons/godebug/pretty" + "golang.org/x/mod/semver" ) // TestCaseDriver knows how different TC can be executed @@ -47,7 +49,7 @@ func (td *DefaultDriver) runStep(step Step) error { return err } modelState := step.State - actualState := td.testCfg.getState(modelState, td.verbose) + actualState := td.getState(modelState) // Check state if !reflect.DeepEqual(actualState, modelState) { @@ -60,84 +62,145 @@ func (td *DefaultDriver) runStep(step Step) error { return nil } +func (td *DefaultDriver) getIcsVersion(chainID ChainID) string { + version := "" + if td.testCfg.chainConfigs[chainID].BinaryName == "interchain-security-pd" { + version = td.testCfg.providerVersion + } else { + version = td.testCfg.consumerVersion + } + ics := getIcsVersion(version) + if !semver.IsValid(ics) { + return "" + } else { + return semver.Major(ics) + } +} + +func (td *DefaultDriver) getTargetDriver(chainID ChainID) Chain { + target := Chain{ + testConfig: td.testCfg, + } + icsVersion := td.getIcsVersion(chainID) + switch icsVersion { + case "v4": + if td.verbose { + fmt.Println("Using 'v4' driver for chain ", chainID) + } + target.target = v4.Commands{ + ContainerConfig: td.testCfg.containerConfig, + ValidatorConfigs: td.testCfg.validatorConfigs, + ChainConfigs: td.testCfg.chainConfigs, + Target: td.target, + } + default: + target.target = Commands{ + containerConfig: td.testCfg.containerConfig, + validatorConfigs: td.testCfg.validatorConfigs, + chainConfigs: td.testCfg.chainConfigs, + target: td.target, + } + if td.verbose { + fmt.Println("Using default driver ", icsVersion, " for chain ", chainID) + } + } + + return target +} + +func (td *DefaultDriver) getState(modelState State) State { + systemState := State{} + for chainID, modelState := range modelState { + target := td.getTargetDriver(chainID) + + if td.verbose { + fmt.Println("Getting model state for chain: ", chainID) + } + systemState[chainID] = target.GetChainState(chainID, modelState) + } + + return systemState +} + func (td *DefaultDriver) runAction(action interface{}) error { + target := td.getTargetDriver("") switch action := action.(type) { case StartChainAction: - td.testCfg.startChain(action, td.target, td.verbose) + target.startChain(action, td.verbose) case StartSovereignChainAction: - td.testCfg.startSovereignChain(action, td.target, td.verbose) + target.startSovereignChain(action, td.verbose) case LegacyUpgradeProposalAction: - td.testCfg.submitLegacyUpgradeProposal(action, td.target, td.verbose) + target.submitLegacyUpgradeProposal(action, td.verbose) case WaitUntilBlockAction: - td.testCfg.waitUntilBlockOnChain(action) + target.waitUntilBlockOnChain(action) case ChangeoverChainAction: - td.testCfg.changeoverChain(action, td.target, td.verbose) + target.changeoverChain(action, td.verbose) case SendTokensAction: - td.testCfg.sendTokens(action, td.target, td.verbose) + target.sendTokens(action, td.verbose) case SubmitTextProposalAction: - td.testCfg.submitTextProposal(action, td.target, td.verbose) + target.submitTextProposal(action, td.verbose) case SubmitConsumerAdditionProposalAction: - td.testCfg.submitConsumerAdditionProposal(action, td.target, td.verbose) + target.submitConsumerAdditionProposal(action, td.verbose) case SubmitConsumerRemovalProposalAction: - td.testCfg.submitConsumerRemovalProposal(action, td.target, td.verbose) + target.submitConsumerRemovalProposal(action, td.verbose) case SubmitEnableTransfersProposalAction: - td.testCfg.submitEnableTransfersProposalAction(action, td.target, td.verbose) + target.submitEnableTransfersProposalAction(action, td.verbose) case VoteGovProposalAction: - td.testCfg.voteGovProposal(action, td.target, td.verbose) + target.voteGovProposal(action, td.verbose) case StartConsumerChainAction: - td.testCfg.startConsumerChain(action, td.target, td.verbose) + target.startConsumerChain(action, td.verbose) case AddChainToRelayerAction: - td.testCfg.addChainToRelayer(action, td.target, td.verbose) + target.addChainToRelayer(action, td.verbose) case CreateIbcClientsAction: - td.testCfg.createIbcClientsHermes(action, td.target, td.verbose) + target.createIbcClientsHermes(action, td.verbose) case AddIbcConnectionAction: - td.testCfg.addIbcConnection(action, td.target, td.verbose) + target.addIbcConnection(action, td.verbose) case AddIbcChannelAction: - td.testCfg.addIbcChannel(action, td.target, td.verbose) + target.addIbcChannel(action, td.verbose) case TransferChannelCompleteAction: - td.testCfg.transferChannelComplete(action, td.target, td.verbose) + target.transferChannelComplete(action, td.verbose) case RelayPacketsAction: - td.testCfg.relayPackets(action, td.target, td.verbose) + target.relayPackets(action, td.verbose) case RelayRewardPacketsToProviderAction: - td.testCfg.relayRewardPacketsToProvider(action, td.target, td.verbose) + target.relayRewardPacketsToProvider(action, td.verbose) case DelegateTokensAction: - td.testCfg.delegateTokens(action, td.target, td.verbose) + target.delegateTokens(action, td.verbose) case UnbondTokensAction: - td.testCfg.unbondTokens(action, td.target, td.verbose) + target.unbondTokens(action, td.verbose) case CancelUnbondTokensAction: - td.testCfg.cancelUnbondTokens(action, td.target, td.verbose) + target.cancelUnbondTokens(action, td.verbose) case RedelegateTokensAction: - td.testCfg.redelegateTokens(action, td.target, td.verbose) + target.redelegateTokens(action, td.verbose) case DowntimeSlashAction: - td.testCfg.invokeDowntimeSlash(action, td.target, td.verbose) + target.invokeDowntimeSlash(action, td.verbose) case UnjailValidatorAction: - td.testCfg.unjailValidator(action, td.target, td.verbose) + target.unjailValidator(action, td.verbose) case DoublesignSlashAction: - td.testCfg.invokeDoublesignSlash(action, td.target, td.verbose) + target.invokeDoublesignSlash(action, td.verbose) case LightClientAmnesiaAttackAction: - td.testCfg.lightClientAmnesiaAttack(action, td.verbose) + target.lightClientAmnesiaAttack(action, td.verbose) case LightClientEquivocationAttackAction: - td.testCfg.lightClientEquivocationAttack(action, td.verbose) + target.lightClientEquivocationAttack(action, td.verbose) case LightClientLunaticAttackAction: - td.testCfg.lightClientLunaticAttack(action, td.verbose) + target.lightClientLunaticAttack(action, td.verbose) case RegisterRepresentativeAction: - td.testCfg.registerRepresentative(action, td.target, td.verbose) + target.registerRepresentative(action, td.verbose) case AssignConsumerPubKeyAction: - td.testCfg.assignConsumerPubKey(action, td.target, td.verbose) + target.assignConsumerPubKey(action, td.verbose) case SlashMeterReplenishmentAction: - td.testCfg.waitForSlashMeterReplenishment(action, td.verbose) + target.waitForSlashMeterReplenishment(action, td.verbose) case WaitTimeAction: - td.testCfg.waitForTime(action, td.verbose) + target.waitForTime(action, td.verbose) case StartRelayerAction: - td.testCfg.startRelayer(action, td.target, td.verbose) + target.startRelayer(action, td.verbose) case ForkConsumerChainAction: - td.testCfg.forkConsumerChain(action, td.verbose) + target.forkConsumerChain(action, td.verbose) case UpdateLightClientAction: - td.testCfg.updateLightClient(action, td.verbose) + target.updateLightClient(action, td.verbose) case StartConsumerEvidenceDetectorAction: - td.testCfg.startConsumerEvidenceDetector(action, td.target, td.verbose) + target.startConsumerEvidenceDetector(action, td.verbose) case SubmitChangeRewardDenomsProposalAction: - td.testCfg.submitChangeRewardDenomsProposal(action, td.target, td.verbose) + target.submitChangeRewardDenomsProposal(action, td.verbose) default: log.Fatalf("unknown action in testRun %s: %#v", td.testCfg.name, action) } diff --git a/tests/e2e/test_target.go b/tests/e2e/test_target.go index 421be1d1fa..140ed565e6 100644 --- a/tests/e2e/test_target.go +++ b/tests/e2e/test_target.go @@ -24,7 +24,13 @@ type ExecutionTarget interface { Delete() error Info() string } - +type TargetConfig struct { + gaiaTag string + localSdkPath string + useGaia bool + providerVersion string + consumerVersion string +} type DockerContainer struct { targetConfig TargetConfig containerCfg ContainerConfig diff --git a/tests/e2e/testlib/types.go b/tests/e2e/testlib/types.go new file mode 100644 index 0000000000..dd600bb624 --- /dev/null +++ b/tests/e2e/testlib/types.go @@ -0,0 +1,339 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "os/exec" + "reflect" + "time" + + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" +) + +type ( + ChainID string + ValidatorID string +) + +type ChainCommands interface { + GetBlockHeight(chain ChainID) uint + GetBalance(chain ChainID, validator ValidatorID) uint + GetConsumerChains(chain ChainID) map[ChainID]bool + GetConsumerAddress(consumerChain ChainID, validator ValidatorID) string + GetClientFrozenHeight(chain ChainID, clientID string) (RevisionNumber, RevisionHeight uint64) + GetIBCTransferParams(chain ChainID) IBCTransferParams + GetProposal(chain ChainID, proposal uint) Proposal + GetParam(chain ChainID, param Param) string + GetProviderAddressFromConsumer(consumerChain ChainID, validator ValidatorID) string + GetReward(chain ChainID, validator ValidatorID, blockHeight uint, isNativeDenom bool) float64 + GetRegisteredConsumerRewardDenoms(chain ChainID) []string + GetSlashMeter() int64 + GetPendingPacketQueueSize(chain ChainID) uint + GetProposedConsumerChains(chain ChainID) []string + GetQueryNode(chain ChainID) string + GetQueryNodeRPCAddress(chain ChainID) string + GetTrustedHeight(chain ChainID, clientID string, index int) (uint64, uint64) + GetValPower(chain ChainID, validator ValidatorID) uint + GetValStakedTokens(chain ChainID, validatorAddress string) uint + GetQueryNodeIP(chain ChainID) string +} + +// TODO: replace ExecutionTarget with new TargetDriver interface +type PlatformDriver interface { + ExecCommand(name string, arg ...string) *exec.Cmd + // ExecDetachedCommand: when executed the command will be run in the background and call will return immediately + ExecDetachedCommand(name string, args ...string) *exec.Cmd + GetTestScriptPath(isConsumer bool, script string) string +} +type TargetDriver interface { + // ChainCommands + ChainCommands + PlatformDriver +} + +// TODO: this should not be here. mv 'Now' to a better suited type here and then move ContainerConfig back +type ContainerConfig struct { + ContainerName string + InstanceName string + CcvVersion string + Now time.Time +} + +// Attributes that are unique to a validator. Allows us to map (part of) +// the set of strings defined above to a set of viable validators +type ValidatorConfig struct { + // Seed phrase to generate a secp256k1 key used by the validator on the provider + Mnemonic string + // Validator account address on provider marshaled to string using Bech32 + // with Bech32Prefix = ProviderAccountPrefix + DelAddress string + // Validator account address on provider marshaled to string using Bech32 + // with Bech32Prefix = ConsumerAccountPrefix + DelAddressOnConsumer string + // Validator operator address on provider marshaled to string using Bech32 + // with Bech32Prefix = ProviderAccountPrefix + ValoperAddress string + // Validator operator address on provider marshaled to string using Bech32 + // with Bech32Prefix = ConsumerAccountPrefix + ValoperAddressOnConsumer string + // Validator consensus address on provider marshaled to string using Bech32 + // with Bech32Prefix = ProviderAccountPrefix. It matches the PrivValidatorKey below. + ValconsAddress string + // Validator consensus address on provider marshaled to string using Bech32 + // with Bech32Prefix = ConsumerAccountPrefix. + ValconsAddressOnConsumer string + // Key used for consensus on provider + PrivValidatorKey string + NodeKey string + // Must be an integer greater than 0 and less than 253 + IpSuffix string + + // consumer chain key assignment data + // keys are used on a new node + + // Seed phrase to generate a secp256k1 key used by the validator on the consumer + ConsumerMnemonic string + // Validator account address on consumer marshaled to string using Bech32 + // with Bech32Prefix = ConsumerAccountPrefix + ConsumerDelAddress string + // Validator account address on consumer marshaled to string using Bech32 + // with Bech32Prefix = ProviderAccountPrefix + ConsumerDelAddressOnProvider string + // Validator operator address on consumer marshaled to string using Bech32 + // with Bech32Prefix = ConsumerAccountPrefix + ConsumerValoperAddress string + // Validator operator address on consumer marshaled to string using Bech32 + // with Bech32Prefix = ProviderAccountPrefix + ConsumerValoperAddressOnProvider string + // Validator consensus address on consumer marshaled to string using Bech32 + // with Bech32Prefix = ConsumerAccountPrefix. It matches the PrivValidatorKey below. + ConsumerValconsAddress string + // Validator consensus address on consumer marshaled to string using Bech32 + // with Bech32Prefix = ProviderAccountPrefix. + ConsumerValconsAddressOnProvider string + ConsumerValPubKey string + // Key used for consensus on consumer + ConsumerPrivValidatorKey string + ConsumerNodeKey string + UseConsumerKey bool // if true the validator node will start with consumer key +} + +// Attributes that are unique to a chain. Allows us to map (part of) +// the set of strings defined above to a set of viable chains +type ChainConfig struct { + ChainId ChainID + // The account prefix configured on the chain. For example, on the Hub, this is "cosmos" + AccountPrefix string + // Must be unique per chain + IpPrefix string + VotingWaitTime uint + // Any transformations to apply to the genesis file of all chains instantiated with this chain config, as a jq string. + // Example: ".app_state.gov.params.voting_period = \"5s\" | .app_state.slashing.params.signed_blocks_window = \"2\" | .app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\"" + GenesisChanges string + BinaryName string + + // binary to use after upgrade height + UpgradeBinary string +} + +type ( + // to have a ChainState object that does not have the overridden Marshal/Unmarshal method + ChainStateCopy ChainState + + // duplicated from the ChainState with a minor change to the Proposals field + ChainStateWithProposalTypes struct { + ChainStateCopy + Proposals *map[uint]ProposalAndType // the only thing changed from the real ChainState + } +) + +// stores a proposal as a raw json, together with its type +type ProposalAndType struct { + RawProposal json.RawMessage + Type string +} + +type ChainState struct { + ValBalances *map[ValidatorID]uint + Proposals *map[uint]Proposal + ProposedConsumerChains *[]string + ValPowers *map[ValidatorID]uint + StakedTokens *map[ValidatorID]uint + IBCTransferParams *IBCTransferParams + Params *[]Param + Rewards *Rewards + ConsumerChains *map[ChainID]bool + AssignedKeys *map[ValidatorID]string + ProviderKeys *map[ValidatorID]string // validatorID: validator provider key + ConsumerPendingPacketQueueSize *uint // Only relevant to consumer chains + RegisteredConsumerRewardDenoms *[]string + ClientsFrozenHeights *map[string]clienttypes.Height +} + +// custom marshal and unmarshal functions for the chainstate that convert proposals to/from the auxiliary type with type info + +// MarshalJSON transforms the ChainState into a ChainStateWithProposalTypes by adding type info to the proposals +func (c ChainState) MarshalJSON() ([]byte, error) { + chainStateCopy := ChainStateCopy(c) + chainStateWithProposalTypes := ChainStateWithProposalTypes{chainStateCopy, nil} + if c.Proposals != nil { + proposalsWithTypes := make(map[uint]ProposalAndType) + for k, v := range *c.Proposals { + rawMessage, err := json.Marshal(v) + if err != nil { + return nil, err + } + proposalsWithTypes[k] = ProposalAndType{rawMessage, reflect.TypeOf(v).String()} + } + chainStateWithProposalTypes.Proposals = &proposalsWithTypes + } + return json.Marshal(chainStateWithProposalTypes) +} + +// UnmarshalJSON unmarshals the ChainStateWithProposalTypes into a ChainState by removing the type info from the proposals and getting back standard proposals +func (c *ChainState) UnmarshalJSON(data []byte) error { + chainStateWithProposalTypes := ChainStateWithProposalTypes{} + err := json.Unmarshal(data, &chainStateWithProposalTypes) + if err != nil { + return err + } + + chainState := ChainState(chainStateWithProposalTypes.ChainStateCopy) + *c = chainState + + if chainStateWithProposalTypes.Proposals != nil { + proposals := make(map[uint]Proposal) + for k, v := range *chainStateWithProposalTypes.Proposals { + proposal, err := UnmarshalProposalWithType(v.RawProposal, v.Type) + if err != nil { + return err + } + proposals[k] = proposal + } + c.Proposals = &proposals + } + return nil +} + +// UnmarshalProposalWithType takes a JSON object and a proposal type and marshals into an object of the corresponding proposal. +func UnmarshalProposalWithType(inputMap json.RawMessage, proposalType string) (Proposal, error) { + var err error + switch proposalType { + case "main.TextProposal": + prop := TextProposal{} + err := json.Unmarshal(inputMap, &prop) + if err == nil { + return prop, nil + } + case "main.ConsumerAdditionProposal": + prop := ConsumerAdditionProposal{} + err := json.Unmarshal(inputMap, &prop) + if err == nil { + return prop, nil + } + case "main.UpgradeProposal": + prop := UpgradeProposal{} + err := json.Unmarshal(inputMap, &prop) + if err == nil { + return prop, nil + } + case "main.ConsumerRemovalProposal": + prop := ConsumerRemovalProposal{} + err := json.Unmarshal(inputMap, &prop) + if err == nil { + return prop, nil + } + case "main.IBCTransferParamsProposal": + prop := IBCTransferParamsProposal{} + err := json.Unmarshal(inputMap, &prop) + if err == nil { + return prop, nil + } + default: + return nil, fmt.Errorf("%s is not a known proposal type", proposalType) + } + + return nil, err +} + +type Proposal interface { + isProposal() +} +type TextProposal struct { + Title string + Description string + Deposit uint + Status string +} + +func (p TextProposal) isProposal() {} + +type IBCTransferParamsProposal struct { + Title string + Deposit uint + Status string + Params IBCTransferParams +} + +func (ibct IBCTransferParamsProposal) isProposal() {} + +type ConsumerAdditionProposal struct { + Deposit uint + Chain ChainID + SpawnTime int + InitialHeight clienttypes.Height + Status string +} + +type UpgradeProposal struct { + Title string + Description string + UpgradeHeight uint64 + Type string + Deposit uint + Status string +} + +func (p UpgradeProposal) isProposal() {} + +func (p ConsumerAdditionProposal) isProposal() {} + +type ConsumerRemovalProposal struct { + Deposit uint + Chain ChainID + StopTime int + Status string +} + +func (p ConsumerRemovalProposal) isProposal() {} + +type Rewards struct { + IsRewarded map[ValidatorID]bool + // if true it will calculate if the validator/delegator is rewarded between 2 successive blocks, + // otherwise it will calculate if it received any rewards since the 1st block + IsIncrementalReward bool + // if true checks rewards for "stake" token, otherwise checks rewards from + // other chains (e.g. false is used to check if provider received rewards from a consumer chain) + IsNativeDenom bool +} + +type ParamsProposal struct { + Deposit uint + Status string + Subspace string + Key string + Value string +} + +func (p ParamsProposal) isProposal() {} + +type Param struct { + Subspace string + Key string + Value string +} + +type IBCTransferParams struct { + SendEnabled bool `json:"send_enabled"` + ReceiveEnabled bool `json:"receive_enabled"` +} diff --git a/tests/e2e/testlib/utils.go b/tests/e2e/testlib/utils.go new file mode 100644 index 0000000000..8b68c25947 --- /dev/null +++ b/tests/e2e/testlib/utils.go @@ -0,0 +1,43 @@ +package e2e + +import ( + "bufio" + "fmt" + "log" + "os/exec" +) + +var verbose *bool //TODO: remove hack + +func ExecuteCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) { + if verbose { + fmt.Println(cmdName+" cmd:", cmd.String()) + } + + cmdReader, err := cmd.StdoutPipe() + if err != nil { + log.Fatal(err) + } + cmd.Stderr = cmd.Stdout + + if err := cmd.Start(); err != nil { + log.Fatal(err) + } + + scanner := bufio.NewScanner(cmdReader) + + for scanner.Scan() { + out := scanner.Text() + if verbose { + fmt.Println(cmdName + ": " + out) + } + } + if err := scanner.Err(); err != nil { + log.Fatal(err) + } +} + +// Executes a command with verbosity specified by CLI flag +func ExecuteCommand(cmd *exec.Cmd, cmdName string) { + ExecuteCommandWithVerbosity(cmd, cmdName, *verbose) +} diff --git a/tests/e2e/v4/state.go b/tests/e2e/v4/state.go new file mode 100644 index 0000000000..9ee3c7ec97 --- /dev/null +++ b/tests/e2e/v4/state.go @@ -0,0 +1,654 @@ +package v4 + +import ( + "bufio" + "fmt" + "log" + "os/exec" + "regexp" + "strconv" + "time" + + gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + e2e "github.com/cosmos/interchain-security/v5/tests/e2e/testlib" + "gopkg.in/yaml.v2" + + "github.com/kylelemons/godebug/pretty" + "github.com/tidwall/gjson" +) + +type ( + ChainID = e2e.ChainID + ValidatorID = e2e.ValidatorID + ChainState = e2e.ChainState + Proposal = e2e.Proposal + Rewards = e2e.Rewards + TextProposal = e2e.TextProposal + UpgradeProposal = e2e.UpgradeProposal + ConsumerAdditionProposal = e2e.ConsumerAdditionProposal + ConsumerRemovalProposal = e2e.ConsumerRemovalProposal + IBCTransferParams = e2e.IBCTransferParams + IBCTransferParamsProposal = e2e.IBCTransferParamsProposal + Param = e2e.Param + ParamsProposal = e2e.ParamsProposal + ValidatorConfig = e2e.ValidatorConfig + ChainConfig = e2e.ChainConfig + ContainerConfig = e2e.ContainerConfig + TargetDriver = e2e.TargetDriver +) + +type State map[ChainID]ChainState + +type Commands struct { + ContainerConfig ContainerConfig // FIXME only needed for 'Now' time tracking + ValidatorConfigs map[ValidatorID]ValidatorConfig + ChainConfigs map[ChainID]ChainConfig + Target e2e.PlatformDriver +} + +func (tr Commands) ExecCommand(name string, arg ...string) *exec.Cmd { + return tr.Target.ExecCommand(name, arg...) +} + +func (tr Commands) ExecDetachedCommand(name string, args ...string) *exec.Cmd { + return tr.Target.ExecDetachedCommand(name, args...) +} + +func (tr Commands) GetTestScriptPath(isConsumer bool, script string) string { + return tr.Target.GetTestScriptPath(isConsumer, script) +} + +func (tr Commands) GetBlockHeight(chain ChainID) uint { + binaryName := tr.ChainConfigs[chain].BinaryName + bz, err := tr.Target.ExecCommand(binaryName, + + "query", "tendermint-validator-set", + + `--node`, tr.GetQueryNode(chain), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + blockHeightRegex := regexp.MustCompile(`block_height: "(\d+)"`) + blockHeight, err := strconv.Atoi(blockHeightRegex.FindStringSubmatch(string(bz))[1]) + if err != nil { + log.Fatal(err) + } + + return uint(blockHeight) +} + +func (tr Commands) waitUntilBlock(chain ChainID, block uint, timeout time.Duration) { + start := time.Now() + for { + thisBlock := tr.GetBlockHeight(chain) + if thisBlock >= block { + return + } + if time.Since(start) > timeout { + panic(fmt.Sprintf("\n\n\nwaitBlocks method has timed out after: %s\n\n", timeout)) + } + time.Sleep(500 * time.Millisecond) + } +} + +type ValPubKey struct { + Value string `yaml:"value"` +} + +type TmValidatorSetYaml struct { + Total string `yaml:"total"` + Validators []struct { + Address string `yaml:"address"` + VotingPower string `yaml:"voting_power"` + PubKey ValPubKey `yaml:"pub_key"` + } +} + +func (tr Commands) GetValPower(chain ChainID, validator ValidatorID) uint { + /* if *verbose { + log.Println("getting validator power for chain: ", chain, " validator: ", validator) + } + */ + binaryName := tr.ChainConfigs[chain].BinaryName + command := tr.Target.ExecCommand(binaryName, + + "query", "tendermint-validator-set", + + `--node`, tr.GetQueryNode(chain), + ) + bz, err := command.CombinedOutput() + if err != nil { + log.Fatalf("encountered an error when executing command '%s': %v, output: %s", command.String(), err, string(bz)) + } + + valset := TmValidatorSetYaml{} + err = yaml.Unmarshal(bz, &valset) + if err != nil { + log.Fatalf("yaml.Unmarshal returned an error while unmarshalling validator set: %v, input: %s", err, string(bz)) + } + + total, err := strconv.Atoi(valset.Total) + if err != nil { + log.Fatalf("v4: strconv.Atoi returned an error while converting total for validator set: %v, input: %s, validator set: %s, src: %s", + err, valset.Total, pretty.Sprint(valset), string(bz)) + } + + if total != len(valset.Validators) { + log.Fatalf("Total number of validators %v does not match number of validators in list %v. Probably a query pagination issue. Validator set: %v", + valset.Total, uint(len(valset.Validators)), pretty.Sprint(valset)) + } + + for _, val := range valset.Validators { + if chain == ChainID("provi") { + // use binary with Bech32Prefix set to ProviderAccountPrefix + if val.Address != tr.ValidatorConfigs[validator].ValconsAddress { + continue + } + } else { + // use binary with Bech32Prefix set to ConsumerAccountPrefix + if val.Address != tr.ValidatorConfigs[validator].ValconsAddressOnConsumer && + val.Address != tr.ValidatorConfigs[validator].ConsumerValconsAddress { + continue + } + } + + votingPower, err := strconv.Atoi(val.VotingPower) + if err != nil { + log.Fatalf("strconv.Atoi returned an error while converting validator voting power: %v, voting power string: %s, validator set: %s", err, val.VotingPower, pretty.Sprint(valset)) + } + + return uint(votingPower) + } + + // Validator not in set, its validator power is zero. + return 0 +} + +func (tr Commands) GetReward(chain ChainID, validator ValidatorID, blockHeight uint, isNativeDenom bool) float64 { + valCfg := tr.ValidatorConfigs[validator] + delAddresss := valCfg.DelAddress + if chain != ChainID("provi") { + // use binary with Bech32Prefix set to ConsumerAccountPrefix + if valCfg.UseConsumerKey { + delAddresss = valCfg.ConsumerDelAddress + } else { + // use the same address as on the provider but with different prefix + delAddresss = valCfg.DelAddressOnConsumer + } + } + + binaryName := tr.ChainConfigs[chain].BinaryName + bz, err := tr.Target.ExecCommand(binaryName, + + "query", "distribution", "rewards", + delAddresss, + + `--height`, fmt.Sprint(blockHeight), + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + denomCondition := `total.#(denom!="stake").amount` + if isNativeDenom { + denomCondition = `total.#(denom=="stake").amount` + } + + return gjson.Get(string(bz), denomCondition).Float() +} + +func (tr Commands) GetBalance(chain ChainID, validator ValidatorID) uint { + valCfg := tr.ValidatorConfigs[validator] + valDelAddress := valCfg.DelAddress + if chain != ChainID("provi") { + // use binary with Bech32Prefix set to ConsumerAccountPrefix + if valCfg.UseConsumerKey { + valDelAddress = valCfg.ConsumerDelAddress + } else { + // use the same address as on the provider but with different prefix + valDelAddress = valCfg.DelAddressOnConsumer + } + } + + binaryName := tr.ChainConfigs[chain].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + + "query", "bank", "balances", + valDelAddress, + + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal("getBalance() failed: ", cmd, ": ", err, "\n", string(bz)) + } + + amount := gjson.Get(string(bz), `balances.#(denom=="stake").amount`) + + return uint(amount.Uint()) +} + +// interchain-securityd query gov proposals +func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { + var noProposalRegex = regexp.MustCompile(`doesn't exist: key not found`) + + binaryName := tr.ChainConfigs[chain].BinaryName + bz, err := tr.Target.ExecCommand(binaryName, + + "query", "gov", "proposal", + fmt.Sprint(proposal), + + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + + prop := TextProposal{} + + if err != nil { + if noProposalRegex.Match(bz) { + return prop + } + + log.Fatal(err, "\n", string(bz)) + } + + propType := gjson.Get(string(bz), `messages.0.content.@type`).String() + deposit := gjson.Get(string(bz), `total_deposit.#(denom=="stake").amount`).Uint() + status := gjson.Get(string(bz), `status`).String() + + // This is a breaking change in the query output for proposals: bug in SDK?? + proposal_value, exists := gov.ProposalStatus_value[status] + if !exists { + panic("invalid proposal status value: " + status) + } + status = strconv.Itoa(int(proposal_value)) + + chainConfigs := tr.ChainConfigs + containerConfig := tr.ContainerConfig + + switch propType { + case "/cosmos.gov.v1beta1.TextProposal": + title := gjson.Get(string(bz), `content.title`).String() + description := gjson.Get(string(bz), `content.description`).String() + + return TextProposal{ + Deposit: uint(deposit), + Status: status, + Title: title, + Description: description, + } + case "/interchain_security.ccv.provider.v1.ConsumerAdditionProposal": + chainId := gjson.Get(string(bz), `messages.0.content.chain_id`).String() + spawnTime := gjson.Get(string(bz), `messages.0.content.spawn_time`).Time().Sub(containerConfig.Now) + + var chain ChainID + for i, conf := range chainConfigs { + if string(conf.ChainId) == chainId { + chain = i + break + } + } + + return ConsumerAdditionProposal{ + Deposit: uint(deposit), + Status: status, + Chain: chain, + SpawnTime: int(spawnTime.Milliseconds()), + InitialHeight: clienttypes.Height{ + RevisionNumber: gjson.Get(string(bz), `messages.0.content.initial_height.revision_number`).Uint(), + RevisionHeight: gjson.Get(string(bz), `messages.0.content.initial_height.revision_height`).Uint(), + }, + } + case "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal": + height := gjson.Get(string(bz), `messages.0.content.plan.height`).Uint() + title := gjson.Get(string(bz), `messages.0.content.plan.name`).String() + return UpgradeProposal{ + Deposit: uint(deposit), + Status: status, + UpgradeHeight: height, + Title: title, + Type: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", + } + case "/interchain_security.ccv.provider.v1.ConsumerRemovalProposal": + chainId := gjson.Get(string(bz), `messages.0.content.chain_id`).String() + stopTime := gjson.Get(string(bz), `messages.0.content.stop_time`).Time().Sub(containerConfig.Now) + + var chain ChainID + for i, conf := range chainConfigs { + if string(conf.ChainId) == chainId { + chain = i + break + } + } + + return ConsumerRemovalProposal{ + Deposit: uint(deposit), + Status: status, + Chain: chain, + StopTime: int(stopTime.Milliseconds()), + } + case "/cosmos.params.v1beta1.ParameterChangeProposal": + return ParamsProposal{ + Deposit: uint(deposit), + Status: status, + Subspace: gjson.Get(string(bz), `messages.0.content.changes.0.subspace`).String(), + Key: gjson.Get(string(bz), `messages.0.content.changes.0.key`).String(), + Value: gjson.Get(string(bz), `messages.0.content.changes.0.value`).String(), + } + } + + log.Fatal("unknown proposal type", string(bz)) + + return nil +} + +func (tr Commands) GetValStakedTokens(chain ChainID, valoperAddress string) uint { + binaryName := tr.ChainConfigs[chain].BinaryName + bz, err := tr.Target.ExecCommand(binaryName, + + "query", "staking", "validator", + valoperAddress, + + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + amount := gjson.Get(string(bz), `tokens`) + + return uint(amount.Uint()) +} + +func (tr Commands) GetParam(chain ChainID, param Param) string { + binaryName := tr.ChainConfigs[chain].BinaryName + bz, err := tr.Target.ExecCommand(binaryName, + "query", "params", "subspace", + param.Subspace, + param.Key, + + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + value := gjson.Get(string(bz), `value`) + + return value.String() +} + +// GetConsumerChains returns a list of consumer chains that're being secured by the provider chain, +// determined by querying the provider chain. +func (tr Commands) GetConsumerChains(chain ChainID) map[ChainID]bool { + binaryName := tr.ChainConfigs[chain].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + + "query", "provider", "list-consumer-chains", + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ) + + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + arr := gjson.Get(string(bz), "chains").Array() + chains := make(map[ChainID]bool) + for _, c := range arr { + id := c.Get("chain_id").String() + chains[ChainID(id)] = true + } + + return chains +} +func (tr Commands) GetConsumerAddress(consumerChain ChainID, validator ValidatorID) string { + binaryName := tr.ChainConfigs[ChainID("provi")].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + + "query", "provider", "validator-consumer-key", + string(consumerChain), tr.ValidatorConfigs[validator].ValconsAddress, + `--node`, tr.GetQueryNode(ChainID("provi")), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + addr := gjson.Get(string(bz), "consumer_address").String() + return addr +} + +func (tr Commands) GetProviderAddressFromConsumer(consumerChain ChainID, validator ValidatorID) string { + binaryName := tr.ChainConfigs[ChainID("provi")].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + + "query", "provider", "validator-provider-key", + string(consumerChain), tr.ValidatorConfigs[validator].ConsumerValconsAddressOnProvider, + `--node`, tr.GetQueryNode(ChainID("provi")), + `-o`, `json`, + ) + + bz, err := cmd.CombinedOutput() + if err != nil { + log.Println("error running ", cmd) + log.Fatal(err, "\n", string(bz)) + } + + addr := gjson.Get(string(bz), "provider_address").String() + return addr +} + +func (tr Commands) GetSlashMeter() int64 { + binaryName := tr.ChainConfigs[ChainID("provi")].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + + "query", "provider", "throttle-state", + `--node`, tr.GetQueryNode(ChainID("provi")), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + slashMeter := gjson.Get(string(bz), "slash_meter") + return slashMeter.Int() +} + +func (tr Commands) GetRegisteredConsumerRewardDenoms(chain ChainID) []string { + binaryName := tr.ChainConfigs[chain].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + "query", "provider", "registered-consumer-reward-denoms", + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + denoms := gjson.Get(string(bz), "denoms").Array() + rewardDenoms := make([]string, len(denoms)) + for i, d := range denoms { + rewardDenoms[i] = d.String() + } + + return rewardDenoms +} + +func (tr Commands) GetPendingPacketQueueSize(chain ChainID) uint { + binaryName := tr.ChainConfigs[chain].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + "query", "ccvconsumer", "throttle-state", + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + if !gjson.ValidBytes(bz) { + panic("invalid json response from query ccvconsumer throttle-state: " + string(bz)) + } + + packetData := gjson.Get(string(bz), "packet_data_queue").Array() + return uint(len(packetData)) +} + +func (tr Commands) GetValidatorIP(chain ChainID, validator ValidatorID) string { + return tr.ChainConfigs[chain].IpPrefix + "." + tr.ValidatorConfigs[validator].IpSuffix +} + +// getQueryNode returns query node tcp address on chain. +func (tr Commands) GetQueryNode(chain ChainID) string { + return fmt.Sprintf("tcp://%s", tr.GetQueryNodeRPCAddress(chain)) +} + +func (tr Commands) GetQueryNodeRPCAddress(chain ChainID) string { + return fmt.Sprintf("%s:26658", tr.GetQueryNodeIP(chain)) +} + +// getQueryNodeIP returns query node IP for chain, +// ipSuffix is hardcoded to be 253 on all query nodes +// except for "sover" chain where there's only one node +func (tr Commands) GetQueryNodeIP(chain ChainID) string { + if chain == ChainID("sover") { + // return address of first and only validator + return fmt.Sprintf("%s.%s", + tr.ChainConfigs[chain].IpPrefix, + tr.ValidatorConfigs[ValidatorID("alice")].IpSuffix) + } + return fmt.Sprintf("%s.253", tr.ChainConfigs[chain].IpPrefix) +} + +func (tr Commands) curlJsonRPCRequest(method, params, address string) { + cmd_template := `curl -H 'Content-Type: application/json' -H 'Accept:application/json' --data '{"jsonrpc":"2.0","method":"%s","params":%s,"id":1}' %s` + cmd := tr.Target.ExecCommand("bash", "-c", fmt.Sprintf(cmd_template, method, params, address)) + + verbosity := false + e2e.ExecuteCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) +} + +// GetClientFrozenHeight returns the frozen height for a client with the given client ID +// by querying the hosting chain with the given chainID +func (tr Commands) GetClientFrozenHeight(chain ChainID, clientID string) (uint64, uint64) { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + //cmd := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[ChainID("provi")].BinaryName, + binaryName := tr.ChainConfigs[ChainID("provi")].BinaryName + cmd := tr.Target.ExecCommand(binaryName, + "query", "ibc", "client", "state", clientID, + `--node`, tr.GetQueryNode(ChainID("provi")), + `-o`, `json`, + ) + + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + frozenHeight := gjson.Get(string(bz), "client_state.frozen_height") + + revHeight, err := strconv.Atoi(frozenHeight.Get("revision_height").String()) + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + revNumber, err := strconv.Atoi(frozenHeight.Get("revision_number").String()) + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + return uint64(revHeight), uint64(revNumber) +} + +func (tr Commands) GetTrustedHeight( + chain ChainID, + clientID string, + index int, +) (uint64, uint64) { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + //configureNodeCmd := exec.Command("docker", "exec", tc.testConfig.containerConfig.InstanceName, "hermes", + configureNodeCmd := tr.Target.ExecCommand("hermes", + "--json", "query", "client", "consensus", "--chain", string(chain), + `--client`, clientID, + ) + + cmdReader, err := configureNodeCmd.StdoutPipe() + if err != nil { + log.Fatal(err) + } + + configureNodeCmd.Stderr = configureNodeCmd.Stdout + + if err := configureNodeCmd.Start(); err != nil { + log.Fatal(err) + } + + scanner := bufio.NewScanner(cmdReader) + + var trustedHeight gjson.Result + // iterate on the relayer's response + // and parse the the command "result" + for scanner.Scan() { + out := scanner.Text() + if len(gjson.Get(out, "result").Array()) > 0 { + trustedHeight = gjson.Get(out, "result").Array()[index] + break + } + } + + revHeight, err := strconv.Atoi(trustedHeight.Get("revision_height").String()) + if err != nil { + log.Fatal(err) + } + + revNumber, err := strconv.Atoi(trustedHeight.Get("revision_number").String()) + if err != nil { + log.Fatal(err) + } + return uint64(revHeight), uint64(revNumber) +} + +func (tr Commands) GetProposedConsumerChains(chain ChainID) []string { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + //bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, + binaryName := tr.ChainConfigs[chain].BinaryName + bz, err := tr.Target.ExecCommand(binaryName, + "query", "provider", "list-proposed-consumer-chains", + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + arr := gjson.Get(string(bz), "proposedChains").Array() + chains := []string{} + for _, c := range arr { + cid := c.Get("chainID").String() + chains = append(chains, cid) + } + + return chains +} + +// Breaking forward compatibility +func (tr Commands) GetIBCTransferParams(chain ChainID) IBCTransferParams { + panic("'GetIBCTransferParams' is not implemented in this version") +} + +func uintPtr(i uint) *uint { + return &i +}