Skip to content

Commit

Permalink
feat: fix equivocation e2e-test + fix CLI (#2248)
Browse files Browse the repository at this point in the history
* save

* save

* rm file

* reformat

* reformat

* nit

* Update tests/e2e/actions.go

Co-authored-by: insumity <[email protected]>

* Update tests/e2e/actions.go

Co-authored-by: bernd-m <[email protected]>

* address comments

---------

Co-authored-by: insumity <[email protected]>
Co-authored-by: bernd-m <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent b68c0f5 commit 804b53b
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 29 deletions.
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(),
GetStartConsumerEvidenceDetectorActionGen().AsAny(),
GetDetectorConsumerEvidenceActionGen().AsAny(),
GetForkConsumerChainActionGen().AsAny(),
GetUpdateLightClientActionGen().AsAny(),
)
Expand Down Expand Up @@ -504,9 +504,9 @@ func GetForkConsumerChainActionGen() *rapid.Generator[ForkConsumerChainAction] {
})
}

func GetStartConsumerEvidenceDetectorActionGen() *rapid.Generator[StartConsumerEvidenceDetectorAction] {
return rapid.Custom(func(t *rapid.T) StartConsumerEvidenceDetectorAction {
return StartConsumerEvidenceDetectorAction{
func GetDetectorConsumerEvidenceActionGen() *rapid.Generator[DetectorConsumerEvidenceAction] {
return rapid.Custom(func(t *rapid.T) DetectorConsumerEvidenceAction {
return DetectorConsumerEvidenceAction{
Chain: GetChainIDGen().Draw(t, "Chain"),
}
})
Expand Down
160 changes: 148 additions & 12 deletions tests/e2e/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2857,26 +2857,162 @@ func (tr Chain) 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.
// detect evidences committed to the blocks of a consumer chain
// either by running an instance of the Hermes relayer using the "evidence" command,
// 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 StartConsumerEvidenceDetectorAction struct {
Chain ChainID
type DetectorConsumerEvidenceAction struct {
Chain ChainID
Submitter ValidatorID
}

func (tr Chain) startConsumerEvidenceDetector(
action StartConsumerEvidenceDetectorAction,
func (tr Chain) detectConsumerEvidence(
action DetectorConsumerEvidenceAction,
useRelayer bool,
verbose bool,
) {
chainConfig := tr.testConfig.chainConfigs[action.Chain]
// run in detached mode so it will keep running in the background
bz, err := tr.target.ExecDetachedCommand(
"hermes", "evidence", "--chain", string(chainConfig.ChainId)).CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
// the Hermes 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
bz, err := tr.target.ExecDetachedCommand(
"hermes", "evidence", "--chain", string(chainConfig.ChainId)).CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
}
tr.waitBlocks("provi", 10, 2*time.Minute)
} else {
// detect the evidence on the consumer chain
consumerBinaryName := tr.testConfig.chainConfigs[action.Chain].BinaryName

// get the infraction height by querying the SDK evidence module of the consumer
timeout := time.Now().Add(30 * time.Second)
infractionHeight := int64(0)
for {
cmd := tr.target.ExecCommand(
consumerBinaryName,
"query", "evidence", "list",
`--node`, tr.target.GetQueryNode(action.Chain),
`-o`, `json`,
)

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

bz, err := cmd.CombinedOutput()
if err == nil {
evidence := gjson.Get(string(bz), "evidence")
// we only expect only one evidence
if len(evidence.Array()) == 1 {
infractionHeight = evidence.Array()[0].Get("value.height").Int()
break
}
}

if err != nil || time.Now().After(timeout) {
log.Print("Failed running command: ", cmd)
log.Fatal(err, "\n", string(bz))
}
time.Sleep(2 * time.Second)
}

// get the evidence data from the block
// note that the evidence is added to the next block after the infraction height
cmd := tr.target.ExecCommand(
consumerBinaryName,
"query", "block", "--type=height", strconv.Itoa(int(infractionHeight+1)),
`--node`, tr.target.GetQueryNode(action.Chain),
`-o`, `json`,
)

if verbose {
fmt.Println("query block for evidence cmd:", cmd.String())
}

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

evidence := gjson.Get(string(bz), "evidence.evidence").Array()
if len(evidence) == 0 {
log.Fatal("expected at least one evidence in block but found zero")
}

if equivocation := evidence[0].Get("duplicate_vote_evidence"); equivocation.String() != "" {
// persist evidence in the json format
evidenceJson := equivocation.Raw
evidencePath := "/temp-evidence.json"
bz, err = tr.target.ExecCommand(
"/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, evidenceJson, evidencePath),
).CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
}

// query IBC header at the infraction height
cmd = tr.target.ExecCommand(
consumerBinaryName,
"query", "ibc", "client", "header", "--height", strconv.Itoa(int(infractionHeight)),
`--node`, tr.target.GetQueryNode(action.Chain),
`-o`, `json`,
)

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

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

// persist IBC header in json format
headerPath := "/temp-header.json"
bz, err = tr.target.ExecCommand(
"/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, string(bz), headerPath),
).CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
}

// submit consumer equivocation to provider
gas := "auto"
submitEquivocation := fmt.Sprintf(
`%s tx provider submit-consumer-double-voting %s %s %s --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y -o json`,
tr.testConfig.chainConfigs[ChainID("provi")].BinaryName,
string(tr.testConfig.chainConfigs[action.Chain].ConsumerId),
evidencePath,
headerPath,
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",
submitEquivocation,
)

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

bz, err = cmd.CombinedOutput()
if err != nil {
log.Fatal(err, "\n", string(bz))
}
} else {
log.Fatal("invalid evidence type", evidence[0].String())
}

tr.waitBlocks("provi", 3, 1*time.Minute)
}
tr.waitBlocks("provi", 10, 2*time.Minute)
}

type OptInAction struct {
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/json_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ func UnmarshalMapToActionType(rawAction json.RawMessage, actionTypeString string
if err == nil {
return a, nil
}
case "main.StartConsumerEvidenceDetectorAction":
var a StartConsumerEvidenceDetectorAction
case "main.DetectorConsumerEvidenceAction":
var a DetectorConsumerEvidenceAction
err := json.Unmarshal(rawAction, &a)
if err == nil {
return a, nil
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/steps_consumer_misbehaviour.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func stepsCauseConsumerMisbehaviour(consumerName string) []Step {
// run Hermes relayer instance to detect the ICS misbehaviour
// and jail alice on the provider
{
Action: StartConsumerEvidenceDetectorAction{
Action: DetectorConsumerEvidenceAction{
Chain: ChainID(consumerName),
},
State: State{
Expand Down
5 changes: 3 additions & 2 deletions tests/e2e/steps_double_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ func stepsCauseDoubleSignOnConsumer(consumerName, providerName string) []Step {
// detect the double voting infraction
// and jail and slashing of bob on the provider
{
Action: StartConsumerEvidenceDetectorAction{
Chain: ChainID(consumerName),
Action: DetectorConsumerEvidenceAction{
Chain: ChainID(consumerName),
Submitter: ValidatorID("bob"),
},
State: State{
ChainID(providerName): ChainState{
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/test_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@ func (td *DefaultDriver) runAction(action interface{}) error {
case UpdateLightClientAction:
target := td.getTargetDriver("")
target.updateLightClient(action, td.verbose)
case StartConsumerEvidenceDetectorAction:
case DetectorConsumerEvidenceAction:
target := td.getTargetDriver("")
target.startConsumerEvidenceDetector(action, td.verbose)
target.detectConsumerEvidence(action, false, td.verbose)
case SubmitChangeRewardDenomsProposalAction:
target := td.getTargetDriver(action.Chain)
version := target.testConfig.providerVersion
Expand Down
11 changes: 5 additions & 6 deletions x/ccv/provider/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,23 @@ Example:
txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)

submitter := clientCtx.GetFromAddress()
cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry)

ev := tmproto.DuplicateVoteEvidence{}
evidenceJson, err := os.ReadFile(args[0])
evidenceJson, err := os.ReadFile(args[1])
if err != nil {
return err
}

if err := json.Unmarshal(evidenceJson, &ev); err != nil {
ev := tmproto.DuplicateVoteEvidence{}
if err := cdc.UnmarshalJSON(evidenceJson, &ev); err != nil {
return fmt.Errorf("duplicate vote evidence unmarshalling failed: %s", err)
}

headerJson, err := os.ReadFile(args[1])
headerJson, err := os.ReadFile(args[2])
if err != nil {
return err
}

cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry)

header := ibctmtypes.Header{}
if err := cdc.UnmarshalJSON(headerJson, &header); err != nil {
return fmt.Errorf("infraction IBC header unmarshalling failed: %s", err)
Expand Down

0 comments on commit 804b53b

Please sign in to comment.