diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 9c7d41af17..0c47b452e2 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -718,7 +718,7 @@ rpc_addr = "%s" rpc_timeout = "10s" store_prefix = "ibc" trusting_period = "14days" -websocket_addr = "%s" +event_source = { mode = "push", url = "%s", batch_delay = "50ms" } ccv_consumer_chain = %v [chains.gas_price] @@ -1844,3 +1844,25 @@ func (tr TestRun) GetPathNameForGorelayer(chainA, chainB chainID) string { return pathName } + +// Run an instance of the Hermes relayer using the "evidence" command, +// which detects evidences committed to the blocks of a consumer chain. +// Each infraction detected is reported to the provider chain using +// either a SubmitConsumerDoubleVoting or a SubmitConsumerMisbehaviour message. +type detectConsumerEvidenceAction struct { + chain chainID +} + +func (tr TestRun) detectConsumerEvidence( + action detectConsumerEvidenceAction, + verbose bool, +) { + chainConfig := tr.chainConfigs[action.chain] + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", "-d", tr.containerConfig.instanceName, + "hermes", "evidence", "--chain", string(chainConfig.chainId)).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + tr.waitBlocks("provi", 10, 2*time.Minute) +} diff --git a/tests/e2e/actions_consumer_misbehaviour.go b/tests/e2e/actions_consumer_misbehaviour.go index 84eb93152c..0da2f9e56f 100644 --- a/tests/e2e/actions_consumer_misbehaviour.go +++ b/tests/e2e/actions_consumer_misbehaviour.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "os/exec" + "strconv" "time" ) @@ -61,6 +62,7 @@ func (tr TestRun) forkConsumerChain(action forkConsumerChainAction, verbose bool } type updateLightClientAction struct { + chain chainID hostChain chainID relayerConfig string clientID string @@ -70,7 +72,9 @@ func (tr TestRun) updateLightClient( action updateLightClientAction, verbose bool, ) { - // hermes clear packets ibc0 transfer channel-13 + // retrieve a trusted height of the consumer light client + trustedHeight := tr.getTrustedHeight(action.hostChain, action.clientID, 2) + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "hermes", "--config", action.relayerConfig, @@ -78,6 +82,7 @@ func (tr TestRun) updateLightClient( "client", "--client", action.clientID, "--host-chain", string(action.hostChain), + "--trusted-height", strconv.Itoa(int(trustedHeight.RevisionHeight)), ) if verbose { log.Println("updateLightClientAction cmd:", cmd.String()) diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 406a015e63..84a06eeab9 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -180,6 +180,8 @@ func (tr *TestRun) runStep(step Step, verbose bool) { tr.updateLightClient(action, verbose) case assertChainIsHaltedAction: tr.assertChainIsHalted(action, verbose) + case detectConsumerEvidenceAction: + tr.detectConsumerEvidence(action, verbose) default: log.Fatalf("unknown action in testRun %s: %#v", tr.name, action) } diff --git a/tests/e2e/state.go b/tests/e2e/state.go index 8d9ba9a81e..d0d908a494 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "fmt" "log" "os/exec" @@ -776,3 +777,50 @@ func (tr TestRun) getClientFrozenHeight(chain chainID, clientID string) clientty return clienttypes.Height{RevisionHeight: uint64(revHeight), RevisionNumber: uint64(revNumber)} } + +func (tr TestRun) 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", tr.containerConfig.instanceName, "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 clienttypes.Height{RevisionHeight: uint64(revHeight), RevisionNumber: uint64(revNumber)} +} diff --git a/tests/e2e/steps_consumer_misbehaviour.go b/tests/e2e/steps_consumer_misbehaviour.go index 6401b5f638..53cfb78fae 100644 --- a/tests/e2e/steps_consumer_misbehaviour.go +++ b/tests/e2e/steps_consumer_misbehaviour.go @@ -213,43 +213,65 @@ func stepsCauseConsumerMisbehaviour(consumerName string) []Step { }, state: State{}, }, + // start relayer to detect IBC misbehaviour { - // start relayer to detect ICS misbehaviour action: startRelayerAction{}, state: State{}, }, + // detect the ICS misbehaviour + // and jail alice on the provider + { + action: detectConsumerEvidenceAction{ + chain: chainID(consumerName), + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 20, + }, + }, + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 20, + }, + }, + }, + }, { // update the fork consumer client to create a light client attack // which should trigger a ICS misbehaviour message action: updateLightClientAction{ + chain: chainID(consumerName), + clientID: consumerClientID, hostChain: chainID("provi"), relayerConfig: forkRelayerConfig, // this relayer config uses the "forked" consumer - clientID: consumerClientID, }, state: State{ chainID("provi"): ChainState{ - // validator should be jailed on the provider + // alice should be jailed on the provider ValPowers: &map[validatorID]uint{ validatorID("alice"): 0, validatorID("bob"): 20, }, - // The consumer light client should not be frozen + // The consumer light client should be frozen on the provider ClientsFrozenHeights: &map[string]clienttypes.Height{ - "07-tendermint-0": { + consumerClientID: { RevisionNumber: 0, - RevisionHeight: 0, + RevisionHeight: 1, }, }, }, + chainID(consumerName): ChainState{ + // consumer should not have learned the jailing of alice + // since its light client is frozen on the provider + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 20, + }, + }, }, }, - // we expect the consumer chain to be halted since the last VSC packet should - // have updated the alice validator power to 0. - { - action: assertChainIsHaltedAction{ - chain: chainID("consu"), - }, - state: State{}, - }, } } diff --git a/tests/e2e/testnet-scripts/fork-consumer.sh b/tests/e2e/testnet-scripts/fork-consumer.sh index 0bf96fcb79..7c12438b71 100644 --- a/tests/e2e/testnet-scripts/fork-consumer.sh +++ b/tests/e2e/testnet-scripts/fork-consumer.sh @@ -63,7 +63,7 @@ rpc_addr = "http://$CONS_CHAIN_PREFIX.252:26658" rpc_timeout = "10s" store_prefix = "ibc" trusting_period = "2days" -websocket_addr = "ws://$CONS_CHAIN_PREFIX.252:26658/websocket" +event_source = { mode = 'push', url = 'ws://$CONS_CHAIN_PREFIX.252:26658/websocket' , batch_delay = '50ms' } [chains.gas_price] denom = "stake" @@ -85,7 +85,9 @@ rpc_addr = "http://$PROV_CHAIN_PREFIX.4:26658" rpc_timeout = "10s" store_prefix = "ibc" trusting_period = "2days" -websocket_addr = "ws://$PROV_CHAIN_PREFIX.4:26658/websocket" +event_source = { mode = 'push', url = 'ws://$PROV_CHAIN_PREFIX.4:26658/websocket' , batch_delay = '50ms' } + + [chains.gas_price] denom = "stake"