Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fix consumer misbehaviour e2e-test + fix CLI #2258

Merged
merged 14 commits into from
Sep 11, 2024
8 changes: 4 additions & 4 deletions tests/e2e/action_rapid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func GetActionGen() *rapid.Generator[any] {
CreateLightClientEquivocationAttackActionGen().AsAny(),
CreateLightClientAmnesiaAttackActionGen().AsAny(),
CreateLightClientLunaticAttackActionGen().AsAny(),
GetDetectorConsumerEvidenceActionGen().AsAny(),
GetDetectConsumerEvidenceActionGen().AsAny(),
GetForkConsumerChainActionGen().AsAny(),
GetUpdateLightClientActionGen().AsAny(),
)
Expand Down Expand Up @@ -504,9 +504,9 @@ func GetForkConsumerChainActionGen() *rapid.Generator[ForkConsumerChainAction] {
})
}

func GetDetectorConsumerEvidenceActionGen() *rapid.Generator[DetectorConsumerEvidenceAction] {
return rapid.Custom(func(t *rapid.T) DetectorConsumerEvidenceAction {
return DetectorConsumerEvidenceAction{
func GetDetectConsumerEvidenceActionGen() *rapid.Generator[DetectConsumerEvidenceAction] {
return rapid.Custom(func(t *rapid.T) DetectConsumerEvidenceAction {
return DetectConsumerEvidenceAction{
Chain: GetChainIDGen().Draw(t, "Chain"),
}
})
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2862,18 +2862,18 @@ func (tr Chain) GetPathNameForGorelayer(chainA, chainB ChainID) string {
// or by queyring manually the consumer chain.
// Each infraction detected is reported to the provider chain using
// either a SubmitConsumerDoubleVoting or a SubmitConsumerMisbehaviour message.
type DetectorConsumerEvidenceAction struct {
type DetectConsumerEvidenceAction struct {
Chain ChainID
Submitter ValidatorID
}

func (tr Chain) detectConsumerEvidence(
action DetectorConsumerEvidenceAction,
action DetectConsumerEvidenceAction,
useRelayer bool,
verbose bool,
) {
chainConfig := tr.testConfig.chainConfigs[action.Chain]
// the Hermes doesn't support evidence handling for Permissionless ICS yet
// the Hermes relayer doesn't support evidence handling for Permissionless ICS yet
// TODO: @Simon refactor once https://github.com/informalsystems/hermes/pull/4182 is merged.
if useRelayer {
// run in detached mode so it will keep running in the background
Expand Down
156 changes: 156 additions & 0 deletions tests/e2e/actions_consumer_misbehaviour.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"bufio"
"encoding/json"
"fmt"
"log"
"strconv"
"time"
Expand Down Expand Up @@ -92,3 +94,157 @@ func (tc Chain) updateLightClient(

tc.waitBlocks(action.HostChain, 8, 30*time.Second)
}

type SubmitConsumerMisbehaviourAction struct {
FromChain ChainID
ToChain ChainID
Submitter ValidatorID
ClientID string
}

func (tr Chain) submitConsumerMisbehaviour(action SubmitConsumerMisbehaviourAction, verbose bool) {
consumerBinaryName := tr.testConfig.chainConfigs[action.FromChain].BinaryName

// retrieve a trusted height of the consumer client from the provider
trustedHeight, _ := tr.target.GetTrustedHeight(action.ToChain, action.ClientID, 2)

// get current block height
currHeight := tr.target.GetBlockHeight(action.FromChain)

tr.waitBlocks(action.FromChain, 3, 10*time.Second)

// retrieve both IBC headers from main consumer and forked consumer,
// which are conflicting at the current block height
cmd := tr.target.ExecCommand(
consumerBinaryName,
"query", "ibc", "client", "header", "--height", strconv.Itoa(int(currHeight)),
`--node`, tr.target.GetQueryNode(action.FromChain),
`-o`, `json`,
)

if verbose {
fmt.Println("query IBC header cmd:", cmd.String())
}

header, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(header))
}

cmd = tr.target.ExecCommand(
consumerBinaryName,
"query", "ibc", "client", "header", "--height", strconv.Itoa(int(currHeight)),
`--node`, fmt.Sprintf("tcp://%s.252:26658", tr.testConfig.chainConfigs[action.FromChain].IpPrefix),
`-o`, `json`,
)

conflictingHeader, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(conflictingHeader))
}

// get IBC header at TrustedHeight+1 since the trusted validators hash
// corresponds to the consensus state "NextValidatorHash" of the consumer client
cmd = tr.target.ExecCommand(
consumerBinaryName,
"query", "ibc", "client", "header", "--height", strconv.Itoa(int(trustedHeight+1)),
`--node`, fmt.Sprintf("tcp://%s.252:26658", tr.testConfig.chainConfigs[action.FromChain].IpPrefix),
`-o`, `json`,
)

if verbose {
fmt.Println("query IBC header cmd:", cmd.String())
}

trustedHeader, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(trustedHeader))
}

// persist consumer misbehaviour in json format
misbPath := "/misbehaviour.json"
bz, err := tr.target.ExecCommand(
"/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, prepareMisb(header, conflictingHeader, trustedHeader, trustedHeight, action.ClientID), misbPath),
Dismissed Show dismissed Hide dismissed
).CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
}

if verbose {
fmt.Println("query IBC header cmd:", cmd.String())
}

// submit consumer misbehaviour
gas := "auto"
submitMisb := fmt.Sprintf(
`%s tx provider submit-consumer-misbehaviour %s %s --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y`,
tr.testConfig.chainConfigs[ChainID("provi")].BinaryName,
string(tr.testConfig.chainConfigs[action.FromChain].ConsumerId),
misbPath,
action.Submitter,
tr.testConfig.chainConfigs[ChainID("provi")].ChainId,
tr.getValidatorHome(ChainID("provi"), action.Submitter),
tr.getValidatorNode(ChainID("provi"), action.Submitter),
gas,
)

cmd = tr.target.ExecCommand(
"/bin/bash", "-c",
submitMisb,
)

if verbose {
fmt.Println("submit consumer misbehaviour cmd:", cmd.String())
}

bz, err = cmd.CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
}

fmt.Println(string(bz))
}

type Header struct {
SignedHeader json.RawMessage `json:"signed_header"`
ValidatorSet json.RawMessage `json:"validator_set"`
TrustedHeight json.RawMessage `json:"trusted_height"`
TrustedValidators json.RawMessage `json:"trusted_validators"`
}

func prepareMisb(header1, header2, trustedHeader []byte, trustedHeight uint64, clientID string) []byte {
misb := struct {
ClientID string `json:"client_id"`
Header1 Header `json:"header_1"`
Header2 Header `json:"header_2"`
}{
ClientID: clientID,
Header1: prepareHeader(header1, trustedHeader, trustedHeight),
Header2: prepareHeader(header2, trustedHeader, trustedHeight),
}

bz, err := json.Marshal(misb)
if err != nil {
log.Fatal(err)
}
return bz
}

func prepareHeader(headerJson, trustedHeaderJson []byte, trustedHeight uint64) Header {
header := Header{}
err := json.Unmarshal(headerJson, &header)
if err != nil {
log.Fatal(err)
}

trustedHeader := Header{}
err = json.Unmarshal(trustedHeaderJson, &trustedHeader)
if err != nil {
log.Fatal(err)
}

header.TrustedValidators = trustedHeader.ValidatorSet
header.TrustedHeight = []byte(fmt.Sprintf(`{"revision_number": "0","revision_height": "%d"}`, trustedHeight))

return header
}
10 changes: 8 additions & 2 deletions tests/e2e/json_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,14 @@ func UnmarshalMapToActionType(rawAction json.RawMessage, actionTypeString string
if err == nil {
return a, nil
}
case "main.DetectorConsumerEvidenceAction":
var a DetectorConsumerEvidenceAction
case "main.DetectConsumerEvidenceAction":
var a DetectConsumerEvidenceAction
err := json.Unmarshal(rawAction, &a)
if err == nil {
return a, nil
}
case "main.SubmitConsumerMisbehaviourAction":
var a SubmitConsumerMisbehaviourAction
err := json.Unmarshal(rawAction, &a)
if err == nil {
return a, nil
Expand Down
131 changes: 80 additions & 51 deletions tests/e2e/steps_consumer_misbehaviour.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,75 +211,104 @@ func stepsCauseConsumerMisbehaviour(consumerName string) []Step {
},
State: State{},
},
// start relayer to detect IBC misbehaviour
{
Action: StartRelayerAction{},
State: State{},
},
// run Hermes relayer instance to detect the ICS misbehaviour
// and jail alice on the provider
{
Action: DetectorConsumerEvidenceAction{
Chain: ChainID(consumerName),
},
State: State{
ChainID("provi"): ChainState{
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 511,
ValidatorID("bob"): 20,
},
StakedTokens: &map[ValidatorID]uint{
ValidatorID("alice"): 511000000,
ValidatorID("bob"): 20000000,
},
},
ChainID(consumerName): ChainState{
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 511,
ValidatorID("bob"): 0,
},
},
},
},
{
// 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
Action: SubmitConsumerMisbehaviourAction{
FromChain: ChainID(consumerName),
ToChain: ChainID("provi"),
ClientID: consumerClientID,
Submitter: ValidatorID("bob"),
},
State: State{
ChainID("provi"): ChainState{
// alice should be jailed on the provider
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 0,
ValidatorID("alice"): 0, // alice is jailed
ValidatorID("bob"): 20,
},
// "alice" should be slashed on the provider, hence representative
// power is 511000000 - 0.05 * 511000000 = 485450000
StakedTokens: &map[ValidatorID]uint{
ValidatorID("alice"): 485450000,
ValidatorID("alice"): 485450000, // alice is slashed
ValidatorID("bob"): 20000000,
},
// The consumer light client should be frozen on the provider
ClientsFrozenHeights: &map[string]clienttypes.Height{
consumerClientID: {
RevisionNumber: 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"): 0,
},
},
},
},
// the Hermes relayer doesn't support evidence handling for Permissionless ICS yet
// TODO: @Simon refactor once https://github.com/informalsystems/hermes/pull/4182 is merged.
// start relayer to detect IBC misbehaviour
// {
// Action: StartRelayerAction{},
// State: State{},
// },
// {
// // 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
// },
// State: State{
// ChainID("provi"): ChainState{
// // alice should be jailed on the provider
// ValPowers: &map[ValidatorID]uint{
// ValidatorID("alice"): 0,
// ValidatorID("bob"): 20,
// },
// // "alice" should be slashed on the provider, hence representative
// // power is 511000000 - 0.05 * 511000000 = 485450000
// StakedTokens: &map[ValidatorID]uint{
// ValidatorID("alice"): 485450000,
// ValidatorID("bob"): 20000000,
// },
// // The consumer light client should be frozen on the provider
// ClientsFrozenHeights: &map[string]clienttypes.Height{
// consumerClientID: {
// RevisionNumber: 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"): 0,
// },
// },
// },
// },
// run Hermes relayer instance to detect the ICS misbehaviour
// and jail alice on the provider
// {
// Action: StartConsumerEvidenceDetectorAction{
// Chain: ChainID(consumerName),
// Submitter: ValidatorID("bob"),
// },
// State: State{
// ChainID("provi"): ChainState{
// ValPowers: &map[ValidatorID]uint{
// ValidatorID("alice"): 511,
// ValidatorID("bob"): 20,
// },
// StakedTokens: &map[ValidatorID]uint{
// ValidatorID("alice"): 511000000,
// ValidatorID("bob"): 20000000,
// },
// },
// ChainID(consumerName): ChainState{
// ValPowers: &map[ValidatorID]uint{
// ValidatorID("alice"): 511,
// ValidatorID("bob"): 0,
// },
// },
// },
// },
}
}
2 changes: 1 addition & 1 deletion tests/e2e/steps_double_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func stepsCauseDoubleSignOnConsumer(consumerName, providerName string) []Step {
// detect the double voting infraction
// and jail and slashing of bob on the provider
{
Action: DetectorConsumerEvidenceAction{
Action: DetectConsumerEvidenceAction{
Chain: ChainID(consumerName),
Submitter: ValidatorID("bob"),
},
Expand Down
Loading
Loading