-
Notifications
You must be signed in to change notification settings - Fork 138
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!: Cryptographic verification of equivocation #1287
Changes from 9 commits
a77eea1
21e3d83
292ad75
f168b9b
f12a5c0
2501e83
eb6a079
98af9c0
c881a1a
a71f1fe
3be76ad
691d206
88e0717
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,11 +7,16 @@ import "google/api/annotations.proto"; | |
import "gogoproto/gogo.proto"; | ||
import "cosmos_proto/cosmos.proto"; | ||
import "google/protobuf/any.proto"; | ||
import "ibc/lightclients/tendermint/v1/tendermint.proto"; | ||
import "tendermint/types/evidence.proto"; | ||
|
||
|
||
// Msg defines the Msg service. | ||
service Msg { | ||
rpc AssignConsumerKey(MsgAssignConsumerKey) returns (MsgAssignConsumerKeyResponse); | ||
rpc RegisterConsumerRewardDenom(MsgRegisterConsumerRewardDenom) returns (MsgRegisterConsumerRewardDenomResponse); | ||
rpc SubmitConsumerMisbehaviour(MsgSubmitConsumerMisbehaviour) returns (MsgSubmitConsumerMisbehaviourResponse); | ||
rpc SubmitConsumerDoubleVoting(MsgSubmitConsumerDoubleVoting) returns (MsgSubmitConsumerDoubleVotingResponse); | ||
} | ||
|
||
message MsgAssignConsumerKey { | ||
|
@@ -42,4 +47,34 @@ message MsgRegisterConsumerRewardDenom { | |
} | ||
|
||
// MsgRegisterConsumerRewardDenomResponse defines the Msg/RegisterConsumerRewardDenom response type. | ||
message MsgRegisterConsumerRewardDenomResponse {} | ||
message MsgRegisterConsumerRewardDenomResponse {} | ||
|
||
// MsgSubmitConsumerMisbehaviour defines a message that reports a misbehaviour | ||
// observed on a consumer chain | ||
// Note that the misbheaviour' headers must contain the same trusted states | ||
message MsgSubmitConsumerMisbehaviour { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. explain it's about light client attacks |
||
option (gogoproto.equal) = false; | ||
option (gogoproto.goproto_getters) = false; | ||
string submitter = 1; | ||
// The Misbehaviour of the consumer chain wrapping | ||
// two conflicting IBC headers | ||
ibc.lightclients.tendermint.v1.Misbehaviour misbehaviour = 2; | ||
} | ||
|
||
message MsgSubmitConsumerMisbehaviourResponse {} | ||
|
||
|
||
// MsgSubmitConsumerDoubleVoting defines a message that reports an equivocation | ||
// observed on a consumer chain | ||
message MsgSubmitConsumerDoubleVoting { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. emphasize it's about double signing attacks |
||
option (gogoproto.equal) = false; | ||
option (gogoproto.goproto_getters) = false; | ||
string submitter = 1; | ||
// The equivocation of the consumer chain wrapping | ||
// an evidence of a validator that signed two conflicting votes | ||
tendermint.types.DuplicateVoteEvidence duplicate_vote_evidence = 2; | ||
// The light client header of the infraction block | ||
ibc.lightclients.tendermint.v1.Header infraction_block_header = 3; | ||
} | ||
|
||
message MsgSubmitConsumerDoubleVotingResponse {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,7 @@ type StartChainAction struct { | |
validators []StartChainValidator | ||
// Genesis changes specific to this action, appended to genesis changes defined in chain config | ||
genesisChanges string | ||
skipGentx bool | ||
isConsumer bool | ||
} | ||
|
||
type StartChainValidator struct { | ||
|
@@ -133,7 +133,7 @@ func (tr TestRun) startChain( | |
cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "/bin/bash", | ||
"/testnet-scripts/start-chain.sh", chainConfig.binaryName, string(vals), | ||
string(chainConfig.chainId), chainConfig.ipPrefix, genesisChanges, | ||
fmt.Sprint(action.skipGentx), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. check that the sovereign tests are not broken @MSalopek |
||
fmt.Sprint(action.isConsumer), | ||
// override config/config.toml for each node on chain | ||
// usually timeout_commit and peer_gossip_sleep_duration are changed to vary the test run duration | ||
// lower timeout_commit means the blocks are produced faster making the test run shorter | ||
|
@@ -170,6 +170,7 @@ func (tr TestRun) startChain( | |
tr.addChainToRelayer(addChainToRelayerAction{ | ||
chain: action.chain, | ||
validator: action.validators[0].id, | ||
consumer: action.isConsumer, | ||
}, verbose) | ||
} | ||
|
||
|
@@ -280,6 +281,8 @@ func (tr TestRun) submitConsumerAdditionProposal( | |
if err != nil { | ||
log.Fatal(err, "\n", string(bz)) | ||
} | ||
|
||
tr.waitBlocks(action.chain, 1, 5*time.Second) | ||
} | ||
|
||
type submitConsumerRemovalProposalAction struct { | ||
|
@@ -521,7 +524,7 @@ func (tr TestRun) voteGovProposal( | |
} | ||
|
||
wg.Wait() | ||
time.Sleep(time.Duration(tr.chainConfigs[action.chain].votingWaitTime) * time.Second) | ||
time.Sleep((time.Duration(tr.chainConfigs[action.chain].votingWaitTime)) * time.Second) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: extra brackets |
||
} | ||
|
||
type startConsumerChainAction struct { | ||
|
@@ -564,7 +567,7 @@ func (tr TestRun) startConsumerChain( | |
chain: action.consumerChain, | ||
validators: action.validators, | ||
genesisChanges: consumerGenesis, | ||
skipGentx: true, | ||
isConsumer: true, | ||
}, verbose) | ||
} | ||
|
||
|
@@ -698,6 +701,7 @@ func (tr TestRun) startChangeover( | |
type addChainToRelayerAction struct { | ||
chain chainID | ||
validator validatorID | ||
consumer bool | ||
} | ||
|
||
const hermesChainConfigTemplate = ` | ||
|
@@ -714,7 +718,8 @@ 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] | ||
denom = "stake" | ||
|
@@ -813,7 +818,7 @@ func (tr TestRun) addChainToHermes( | |
keyName, | ||
rpcAddr, | ||
wsAddr, | ||
// action.consumer, | ||
action.consumer, | ||
) | ||
|
||
bashCommand := fmt.Sprintf(`echo '%s' >> %s`, chainConfig, "/root/.hermes/config.toml") | ||
|
@@ -827,7 +832,16 @@ func (tr TestRun) addChainToHermes( | |
} | ||
|
||
// Save mnemonic to file within container | ||
saveMnemonicCommand := fmt.Sprintf(`echo '%s' > %s`, tr.validatorConfigs[action.validator].mnemonic, "/root/.hermes/mnemonic.txt") | ||
var mnemonic string | ||
if tr.validatorConfigs[action.validator].useConsumerKey && action.consumer { | ||
mnemonic = tr.validatorConfigs[action.validator].consumerMnemonic | ||
} else { | ||
mnemonic = tr.validatorConfigs[action.validator].mnemonic | ||
} | ||
|
||
saveMnemonicCommand := fmt.Sprintf(`echo '%s' > %s`, mnemonic, "/root/.hermes/mnemonic.txt") | ||
fmt.Println("Add to hermes", action.validator) | ||
fmt.Println(mnemonic) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove it or log it |
||
//#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. | ||
bz, err = exec.Command("docker", "exec", tr.containerConfig.instanceName, "bash", "-c", | ||
saveMnemonicCommand, | ||
|
@@ -1767,6 +1781,8 @@ func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbos | |
valCfg.useConsumerKey = true | ||
tr.validatorConfigs[action.validator] = valCfg | ||
} | ||
|
||
time.Sleep(1 * time.Second) | ||
} | ||
|
||
// slashThrottleDequeue polls slash queue sizes until nextQueueSize is achieved | ||
|
@@ -1828,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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename to |
||
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment that it will keep running on the background |
||
"hermes", "evidence", "--chain", string(chainConfig.chainId)).CombinedOutput() | ||
if err != nil { | ||
log.Fatal(err, "\n", string(bz)) | ||
} | ||
tr.waitBlocks("provi", 10, 2*time.Minute) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"log" | ||
"os/exec" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
type forkConsumerChainAction struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you comment somewhere what we can expect when calling this? |
||
consumerChain chainID | ||
providerChain chainID | ||
validator validatorID | ||
relayerConfig string | ||
} | ||
|
||
func (tr TestRun) forkConsumerChain(action forkConsumerChainAction, verbose bool) { | ||
valCfg := tr.validatorConfigs[action.validator] | ||
|
||
//#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. | ||
configureNodeCmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "/bin/bash", | ||
"/testnet-scripts/fork-consumer.sh", tr.chainConfigs[action.consumerChain].binaryName, | ||
string(action.validator), string(action.consumerChain), | ||
tr.chainConfigs[action.consumerChain].ipPrefix, | ||
tr.chainConfigs[action.providerChain].ipPrefix, | ||
valCfg.mnemonic, | ||
action.relayerConfig, | ||
) | ||
|
||
if verbose { | ||
log.Println("forkConsumerChain - reconfigure node cmd:", configureNodeCmd.String()) | ||
} | ||
|
||
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) | ||
|
||
for scanner.Scan() { | ||
out := scanner.Text() | ||
if verbose { | ||
log.Println("fork consumer validator : " + out) | ||
} | ||
if out == done { | ||
break | ||
} | ||
} | ||
if err := scanner.Err(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
time.Sleep(5 * time.Second) | ||
} | ||
|
||
type updateLightClientAction struct { | ||
chain chainID | ||
hostChain chainID | ||
relayerConfig string | ||
clientID string | ||
} | ||
|
||
func (tr TestRun) updateLightClient( | ||
action updateLightClientAction, | ||
verbose bool, | ||
) { | ||
// 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, | ||
"update", | ||
"client", | ||
"--client", action.clientID, | ||
"--host-chain", string(action.hostChain), | ||
"--trusted-height", strconv.Itoa(int(trustedHeight.RevisionHeight)), | ||
) | ||
if verbose { | ||
log.Println("updateLightClientAction cmd:", cmd.String()) | ||
} | ||
|
||
bz, err := cmd.CombinedOutput() | ||
if err != nil { | ||
log.Fatal(err, "\n", string(bz)) | ||
} | ||
|
||
tr.waitBlocks(action.hostChain, 5, 30*time.Second) | ||
} | ||
|
||
type assertChainIsHaltedAction struct { | ||
chain chainID | ||
} | ||
|
||
// assertChainIsHalted verifies that the chain isn't producing blocks | ||
// by checking that the block height is still the same after 20 seconds | ||
func (tr TestRun) assertChainIsHalted( | ||
action assertChainIsHaltedAction, | ||
verbose bool, | ||
) { | ||
blockHeight := tr.getBlockHeight(action.chain) | ||
time.Sleep(20 * time.Second) | ||
if blockHeight != tr.getBlockHeight(action.chain) { | ||
panic(fmt.Sprintf("chain %v isn't expected to produce blocks", action.chain)) | ||
} | ||
if verbose { | ||
log.Printf("assertChainIsHalted - chain %v was successfully halted\n", action.chain) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe change this from latest to a specific tag