Skip to content

Commit

Permalink
Add time and block advancement integration for CometMock (#1017)
Browse files Browse the repository at this point in the history
* Add time and block advancement

* Adhere to gocritic: use +=

* Remove extra debug output

* Fix: use correct key when consumer key is not assigned

* Correct private key address field

* Clarify comment for WaitTime

* Use bool instead of *bool type

* Add review comments
  • Loading branch information
p-offtermatt authored Jun 19, 2023
1 parent 05c2dae commit 8c8e6a0
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 13 deletions.
103 changes: 96 additions & 7 deletions tests/e2e/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"log"
"math"
"os/exec"
"strconv"
"strings"
Expand Down Expand Up @@ -72,7 +73,7 @@ type StartChainValidator struct {
stake uint
}

func (tr TestRun) startChain(
func (tr *TestRun) startChain(
action StartChainAction,
verbose bool,
) {
Expand Down Expand Up @@ -171,6 +172,14 @@ func (tr TestRun) startChain(
chain: action.chain,
validator: action.validators[0].id,
}, verbose)

// store the fact that we started the chain
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 {
Expand Down Expand Up @@ -489,7 +498,7 @@ type voteGovProposalAction struct {
propNumber uint
}

func (tr TestRun) voteGovProposal(
func (tr *TestRun) voteGovProposal(
action voteGovProposalAction,
verbose bool,
) {
Expand Down Expand Up @@ -521,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 {
Expand All @@ -531,7 +540,7 @@ type startConsumerChainAction struct {
genesisChanges string
}

func (tr TestRun) startConsumerChain(
func (tr *TestRun) startConsumerChain(
action startConsumerChainAction,
verbose bool,
) {
Expand Down Expand Up @@ -1219,8 +1228,8 @@ func (tr TestRun) transferChannelComplete(
executeCommand(chanOpenConfirmCmd, "transferChanOpenConfirm")
}

func executeCommand(cmd *exec.Cmd, cmdName string) {
if verbose != nil && *verbose {
func executeCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) {
if verbose {
fmt.Println(cmdName+" cmd:", cmd.String())
}

Expand All @@ -1238,7 +1247,7 @@ func executeCommand(cmd *exec.Cmd, cmdName string) {

for scanner.Scan() {
out := scanner.Text()
if verbose != nil && *verbose {
if verbose {
fmt.Println(cmdName + ": " + out)
}
}
Expand All @@ -1247,6 +1256,11 @@ func executeCommand(cmd *exec.Cmd, cmdName string) {
}
}

// Executes a command with verbosity specified by CLI flag
func executeCommand(cmd *exec.Cmd, cmdName string) {
executeCommandWithVerbosity(cmd, cmdName, *verbose)
}

type relayPacketsAction struct {
chainA chainID
chainB chainID
Expand Down Expand Up @@ -1284,6 +1298,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(
Expand Down Expand Up @@ -1466,13 +1482,29 @@ 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 {
chain chainID
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)
Expand All @@ -1491,6 +1523,30 @@ 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
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(`{"private_key_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.
cmd := exec.Command(
"docker",
Expand Down Expand Up @@ -1764,6 +1820,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
}
Expand Down Expand Up @@ -1828,3 +1886,34 @@ 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)
} else {
tr.timeOffset += duration
for chain, running := range tr.runningChains {
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)
}
29 changes: 24 additions & 5 deletions tests/e2e/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,19 @@ 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
}

// 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"): {
Expand Down Expand Up @@ -143,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",
Expand Down Expand Up @@ -183,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",
Expand Down Expand Up @@ -226,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 {
Expand All @@ -241,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",
Expand Down Expand Up @@ -276,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",
Expand Down Expand Up @@ -329,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",
Expand Down Expand Up @@ -374,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) {
Expand Down
26 changes: 25 additions & 1 deletion tests/e2e/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,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()
Expand Down Expand Up @@ -722,7 +732,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,
Expand All @@ -737,3 +751,13 @@ 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))

verbosity := false
executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity)
}
1 change: 1 addition & 0 deletions tests/e2e/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8c8e6a0

Please sign in to comment.