From ad544a64ef6c49e650d55e89e010099f17614518 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Wed, 14 Jun 2023 11:22:19 +0200 Subject: [PATCH 1/8] Add time and block advancement --- tests/e2e/actions.go | 54 ++++++++++++++++++++++++++++++++++++++++---- tests/e2e/config.go | 4 ++++ tests/e2e/state.go | 25 +++++++++++++++++++- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index b1e6863b7a..4b6fdf168f 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log" + "math" "os/exec" "strconv" "strings" @@ -72,7 +73,7 @@ type StartChainValidator struct { stake uint } -func (tr TestRun) startChain( +func (tr *TestRun) startChain( action StartChainAction, verbose bool, ) { @@ -171,6 +172,17 @@ func (tr TestRun) startChain( chain: action.chain, validator: action.validators[0].id, }, verbose) + + // store the fact that we started the chain + if tr.runningChains == nil { + tr.runningChains = make(map[chainID]bool) + } + tr.runningChains[action.chain] = true + fmt.Println("Started chain", action.chain) + if tr.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) + } } type submitTextProposalAction struct { @@ -486,7 +498,7 @@ type voteGovProposalAction struct { propNumber uint } -func (tr TestRun) voteGovProposal( +func (tr *TestRun) voteGovProposal( action voteGovProposalAction, verbose bool, ) { @@ -518,7 +530,7 @@ func (tr TestRun) voteGovProposal( } wg.Wait() - time.Sleep(time.Duration(tr.chainConfigs[action.chain].votingWaitTime) * time.Second) + tr.WaitTime(time.Duration(tr.chainConfigs[action.chain].votingWaitTime) * time.Second) } type startConsumerChainAction struct { @@ -528,7 +540,7 @@ type startConsumerChainAction struct { genesisChanges string } -func (tr TestRun) startConsumerChain( +func (tr *TestRun) startConsumerChain( action startConsumerChainAction, verbose bool, ) { @@ -1307,6 +1319,10 @@ func (tr TestRun) setValidatorDowntime(chain chainID, validator validatorID, dow lastArg = "up" } + if tr.useCometmock { + // send set_signing_status either to down or up for validator + } + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. cmd := exec.Command( "docker", @@ -1644,3 +1660,33 @@ func (tr TestRun) GetPathNameForGorelayer(chainA, chainB chainID) string { return pathName } + +func (tr *TestRun) WaitTime(duration time.Duration) { + if !tr.useCometmock { + time.Sleep(duration) + } else { + fmt.Println("advancing time with CometMock") + tr.timeOffset = tr.timeOffset + duration + fmt.Println(tr.runningChains) + for chain, running := range tr.runningChains { + fmt.Println("chain: ", chain, "running: ", running) + if !running { + continue + } + tr.AdvanceTimeForChain(chain, duration) + } + } +} + +func (tr TestRun) 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) + + tr.curlJsonRPCRequest(method, params, address) + + // wait for 1 block of the chain to get a block with the advanced timestamp + tr.waitBlocks(chain, 1, time.Minute) +} diff --git a/tests/e2e/config.go b/tests/e2e/config.go index cc755cb97a..321e5a5aef 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -73,6 +73,10 @@ type TestRun struct { useCometmock bool // if false, nodes run CometBFT useGorelayer bool // if false, Hermes is used as the relayer gaiaTag string + // chains which are running, i.e. producing blocks, at the moment + runningChains map[chainID]bool + // Used with CometMock. The time by which chains have been advanced. Used to keep chains in sync: when a new chain is started, advance its time by this value to keep chains in sync. + timeOffset time.Duration name string } diff --git a/tests/e2e/state.go b/tests/e2e/state.go index 9127c65df8..1f69e4d624 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -199,6 +199,16 @@ func (tr TestRun) getBlockHeight(chain chainID) uint { } func (tr TestRun) waitBlocks(chain chainID, blocks uint, timeout time.Duration) { + if tr.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) + method := "advance_blocks" + params := fmt.Sprintf(`{"num_blocks": "%d"}`, blocks) + + tr.curlJsonRPCRequest(method, params, tcpAddress) + return + } startBlock := tr.getBlockHeight(chain) start := time.Now() @@ -687,7 +697,11 @@ func (tr TestRun) getValidatorHome(chain chainID, validator validatorID) string // getQueryNode returns query node tcp address on chain. func (tr TestRun) getQueryNode(chain chainID) string { - return fmt.Sprintf("tcp://%s:26658", tr.getQueryNodeIP(chain)) + return fmt.Sprintf("tcp://%s", tr.getQueryNodeRPCAddress(chain)) +} + +func (tr TestRun) getQueryNodeRPCAddress(chain chainID) string { + return fmt.Sprintf("%s:26658", tr.getQueryNodeIP(chain)) } // getQueryNodeIP returns query node IP for chain, @@ -695,3 +709,12 @@ func (tr TestRun) getQueryNode(chain chainID) string { func (tr TestRun) getQueryNodeIP(chain chainID) string { return fmt.Sprintf("%s.253", tr.chainConfigs[chain].ipPrefix) } + +func (tr TestRun) 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)) + + executeCommand(cmd, "curlJsonRPCRequest") +} From dd65d254fee037b0b636d166168d3e96afbc388b Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Wed, 14 Jun 2023 11:31:24 +0200 Subject: [PATCH 2/8] Adhere to gocritic: use += --- tests/e2e/actions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 4b6fdf168f..d1eb7a4a03 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1666,7 +1666,7 @@ func (tr *TestRun) WaitTime(duration time.Duration) { time.Sleep(duration) } else { fmt.Println("advancing time with CometMock") - tr.timeOffset = tr.timeOffset + duration + tr.timeOffset += duration fmt.Println(tr.runningChains) for chain, running := range tr.runningChains { fmt.Println("chain: ", chain, "running: ", running) From 90bdf11eff3cc8ca7cecc91bf3fa7526609e0ff7 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Wed, 14 Jun 2023 11:32:17 +0200 Subject: [PATCH 3/8] Remove extra debug output --- tests/e2e/actions.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index d1eb7a4a03..742f786c1b 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1665,11 +1665,8 @@ func (tr *TestRun) WaitTime(duration time.Duration) { if !tr.useCometmock { time.Sleep(duration) } else { - fmt.Println("advancing time with CometMock") tr.timeOffset += duration - fmt.Println(tr.runningChains) for chain, running := range tr.runningChains { - fmt.Println("chain: ", chain, "running: ", running) if !running { continue } From 8af729552a6db6cdc3e2ecfa6f0c7e558cdbffb0 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Thu, 15 Jun 2023 08:35:23 +0200 Subject: [PATCH 4/8] Fix: use correct key when consumer key is not assigned --- tests/e2e/actions.go | 46 +++++++++++++++++++++++++++++++++++++++++++- tests/e2e/state.go | 3 ++- tests/e2e/steps.go | 1 + 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 742f786c1b..b0235b1b8d 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1047,7 +1047,7 @@ func (tr TestRun) transferChannelComplete( executeCommand(chanOpenConfirmCmd, "transferChanOpenConfirm") } -func executeCommand(cmd *exec.Cmd, cmdName string) { +func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose *bool) { if verbose != nil && *verbose { fmt.Println(cmdName+" cmd:", cmd.String()) } @@ -1075,6 +1075,10 @@ func executeCommand(cmd *exec.Cmd, cmdName string) { } } +func executeCommand(cmd *exec.Cmd, cmdName string) { + executeCommandWithVerbosity(cmd, cmdName, verbose) +} + type relayPacketsAction struct { chainA chainID chainB chainID @@ -1112,6 +1116,8 @@ func (tr TestRun) relayPacketsGorelayer( if err != nil { log.Fatal(err, "\n", string(bz)) } + + tr.waitBlocks(action.chainA, 1, 30*time.Second) } func (tr TestRun) relayPacketsHermes( @@ -1294,6 +1300,8 @@ func (tr TestRun) redelegateTokens(action redelegateTokensAction, verbose bool) if err != nil { log.Fatal(err, "\n", string(bz)) } + + tr.waitBlocks(action.chain, 1, 10*time.Second) } type downtimeSlashAction struct { @@ -1301,6 +1309,20 @@ type downtimeSlashAction struct { validator validatorID } +// 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 TestRun) getValidatorKeyAddressFromString(keystring string) string { + var key struct { + Address string `json:"address"` + } + err := json.Unmarshal([]byte(keystring), &key) + if err != nil { + log.Fatal(err) + } + return key.Address +} + func (tr TestRun) invokeDowntimeSlash(action downtimeSlashAction, verbose bool) { // Bring validator down tr.setValidatorDowntime(action.chain, action.validator, true, verbose) @@ -1321,6 +1343,26 @@ func (tr TestRun) setValidatorDowntime(chain chainID, validator validatorID, dow if tr.useCometmock { // send set_signing_status either to down or up for validator + var validatorAddress string + if chain == chainID("provi") { + validatorAddress = tr.getValidatorKeyAddressFromString(tr.validatorConfigs[validator].privValidatorKey) + } else { + var valAddressString string + if tr.validatorConfigs[validator].useConsumerKey { + valAddressString = tr.validatorConfigs[validator].consumerPrivValidatorKey + } else { + valAddressString = tr.validatorConfigs[validator].privValidatorKey + } + validatorAddress = tr.getValidatorKeyAddressFromString(valAddressString) + } + + method := "set_signing_status" + params := fmt.Sprintf(`{"validator_address":"%s","status":"%s"}`, validatorAddress, lastArg) + address := tr.getQueryNodeRPCAddress(chain) + + tr.curlJsonRPCRequest(method, params, address) + tr.waitBlocks(chain, 1, 10*time.Second) + return } //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. @@ -1596,6 +1638,8 @@ func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbos // TODO: @MSalopek refactor this so test config is not changed at runtime // make the validator use consumer key + // @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 } diff --git a/tests/e2e/state.go b/tests/e2e/state.go index 1f69e4d624..e80b9b651b 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -716,5 +716,6 @@ func (tr TestRun) curlJsonRPCRequest(method, params, address string) { //#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)) - executeCommand(cmd, "curlJsonRPCRequest") + verbosity := true + executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", &verbosity) } diff --git a/tests/e2e/steps.go b/tests/e2e/steps.go index 8c0b4b2661..9f66735d8d 100644 --- a/tests/e2e/steps.go +++ b/tests/e2e/steps.go @@ -35,6 +35,7 @@ var shortHappyPathSteps = concatSteps( stepsDelegate("consu"), stepsUnbond("consu"), stepsRedelegateShort("consu"), + stepsDowntime("consu"), stepsStartRelayer(), stepsConsumerRemovalPropNotPassing("consu", 2), // submit removal prop but vote no on it - chain should stay stepsStopChain("consu", 3), // stop chain From db450e18665bc136214a65e9e54f946b94c553eb Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Fri, 16 Jun 2023 14:37:28 +0200 Subject: [PATCH 5/8] Correct private key address field --- tests/e2e/actions.go | 2 +- tests/e2e/state.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index b0235b1b8d..70ac3e2bbe 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1357,7 +1357,7 @@ func (tr TestRun) setValidatorDowntime(chain chainID, validator validatorID, dow } method := "set_signing_status" - params := fmt.Sprintf(`{"validator_address":"%s","status":"%s"}`, validatorAddress, lastArg) + params := fmt.Sprintf(`{"private_key_address":"%s","status":"%s"}`, validatorAddress, lastArg) address := tr.getQueryNodeRPCAddress(chain) tr.curlJsonRPCRequest(method, params, address) diff --git a/tests/e2e/state.go b/tests/e2e/state.go index e80b9b651b..cba2881142 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -716,6 +716,6 @@ func (tr TestRun) curlJsonRPCRequest(method, params, address string) { //#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 := true + verbosity := false executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", &verbosity) } From 68823829f99399e782c3cf80aff5c176a3db65c3 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Fri, 16 Jun 2023 15:06:35 +0200 Subject: [PATCH 6/8] Clarify comment for WaitTime --- tests/e2e/actions.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 70ac3e2bbe..a1d3623b70 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1705,6 +1705,10 @@ func (tr TestRun) GetPathNameForGorelayer(chainA, chainB chainID) string { return pathName } +// WaitTime waits for the given duration. +// The CometMock version of this takes a pointer to the TestRun 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 TestRun. func (tr *TestRun) WaitTime(duration time.Duration) { if !tr.useCometmock { time.Sleep(duration) From a1a4705af27452aa5145b4203699dc81cf34d5af Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Mon, 19 Jun 2023 09:14:59 +0200 Subject: [PATCH 7/8] Use bool instead of *bool type --- tests/e2e/actions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 70ac3e2bbe..ce579ef5d5 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1047,8 +1047,8 @@ func (tr TestRun) transferChannelComplete( executeCommand(chanOpenConfirmCmd, "transferChanOpenConfirm") } -func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose *bool) { - if verbose != nil && *verbose { +func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) { + if verbose { fmt.Println(cmdName+" cmd:", cmd.String()) } @@ -1066,7 +1066,7 @@ func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose *bool) { for scanner.Scan() { out := scanner.Text() - if verbose != nil && *verbose { + if verbose { fmt.Println(cmdName + ": " + out) } } @@ -1076,7 +1076,7 @@ func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose *bool) { } func executeCommand(cmd *exec.Cmd, cmdName string) { - executeCommandWithVerbosity(cmd, cmdName, verbose) + executeCommandWithVerbosity(cmd, cmdName, *verbose) } type relayPacketsAction struct { From 934544098063c0941f0bd813b91d5c34d36657be Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Mon, 19 Jun 2023 09:37:51 +0200 Subject: [PATCH 8/8] Add review comments --- tests/e2e/actions.go | 4 +--- tests/e2e/config.go | 25 ++++++++++++++++++++----- tests/e2e/state.go | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 85bf11d85e..ecad541290 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -174,9 +174,6 @@ func (tr *TestRun) startChain( }, verbose) // store the fact that we started the chain - if tr.runningChains == nil { - tr.runningChains = make(map[chainID]bool) - } tr.runningChains[action.chain] = true fmt.Println("Started chain", action.chain) if tr.timeOffset != 0 { @@ -1259,6 +1256,7 @@ func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) { } } +// 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/config.go b/tests/e2e/config.go index e885ea2c71..94fae8438f 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -84,6 +84,11 @@ type TestRun struct { name string } +// Initialize initializes the TestRun instance by setting the runningChains field to an empty map. +func (tr *TestRun) Initialize() { + tr.runningChains = make(map[chainID]bool) +} + func getDefaultValidators() map[validatorID]ValidatorConfig { return map[validatorID]ValidatorConfig{ validatorID("alice"): { @@ -147,7 +152,7 @@ func getDefaultValidators() map[validatorID]ValidatorConfig { } func SlashThrottleTestRun() TestRun { - return TestRun{ + tr := TestRun{ name: "slash-throttling", containerConfig: ContainerConfig{ containerName: "interchain-security-slash-container", @@ -187,10 +192,12 @@ func SlashThrottleTestRun() TestRun { tendermintConfigOverride: `s/timeout_commit = "5s"/timeout_commit = "1s"/;` + `s/peer_gossip_sleep_duration = "100ms"/peer_gossip_sleep_duration = "50ms"/;`, } + tr.Initialize() + return tr } func DefaultTestRun() TestRun { - return TestRun{ + tr := TestRun{ name: "default", containerConfig: ContainerConfig{ containerName: "interchain-security-container", @@ -230,6 +237,8 @@ func DefaultTestRun() TestRun { tendermintConfigOverride: `s/timeout_commit = "5s"/timeout_commit = "1s"/;` + `s/peer_gossip_sleep_duration = "100ms"/peer_gossip_sleep_duration = "50ms"/;`, } + tr.Initialize() + return tr } func DemocracyTestRun(allowReward bool) TestRun { @@ -245,7 +254,7 @@ func DemocracyTestRun(allowReward bool) TestRun { consumerGenChanges += " | .app_state.ccvconsumer.params.reward_denoms = [\"stake\"]" } - return TestRun{ + tr := TestRun{ name: "democracy", containerConfig: ContainerConfig{ containerName: "interchain-security-democ-container", @@ -280,10 +289,12 @@ func DemocracyTestRun(allowReward bool) TestRun { tendermintConfigOverride: `s/timeout_commit = "5s"/timeout_commit = "1s"/;` + `s/peer_gossip_sleep_duration = "100ms"/peer_gossip_sleep_duration = "50ms"/;`, } + tr.Initialize() + return tr } func MultiConsumerTestRun() TestRun { - return TestRun{ + tr := TestRun{ name: "multi-consumer", containerConfig: ContainerConfig{ containerName: "interchain-security-multic-container", @@ -333,10 +344,12 @@ func MultiConsumerTestRun() TestRun { tendermintConfigOverride: `s/timeout_commit = "5s"/timeout_commit = "3s"/;` + `s/peer_gossip_sleep_duration = "100ms"/peer_gossip_sleep_duration = "100ms"/;`, } + tr.Initialize() + return tr } func ChangeoverTestRun() TestRun { - return TestRun{ + tr := TestRun{ name: "changeover", containerConfig: ContainerConfig{ containerName: "interchain-security-changeover-container", @@ -378,6 +391,8 @@ func ChangeoverTestRun() TestRun { tendermintConfigOverride: `s/timeout_commit = "5s"/timeout_commit = "1s"/;` + `s/peer_gossip_sleep_duration = "100ms"/peer_gossip_sleep_duration = "50ms"/;`, } + tr.Initialize() + return tr } func (s *TestRun) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag string) { diff --git a/tests/e2e/state.go b/tests/e2e/state.go index b36132012e..4de28277d2 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -759,5 +759,5 @@ func (tr TestRun) curlJsonRPCRequest(method, params, address string) { cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "bash", "-c", fmt.Sprintf(cmd_template, method, params, address)) verbosity := false - executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", &verbosity) + executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) }