diff --git a/tests/integration/actions.go b/tests/integration/actions.go index 9e4d26a4b0..081e4d823e 100644 --- a/tests/integration/actions.go +++ b/tests/integration/actions.go @@ -79,6 +79,10 @@ func (tr TestRun) startChain( PrivValidatorKey string `json:"priv_validator_key"` NodeKey string `json:"node_key"` IpSuffix string `json:"ip_suffix"` + + ConsumerMnemonic string `json:"consumer_mnemonic"` + ConsumerPrivValidatorKey string `json:"consumer_priv_validator_key"` + StartWithConsumerKey bool `json:"start_with_consumer_key"` } var validators []jsonValAttrs @@ -91,6 +95,11 @@ func (tr TestRun) startChain( Allocation: fmt.Sprint(val.allocation) + "stake", Stake: fmt.Sprint(val.stake) + "stake", IpSuffix: tr.validatorConfigs[val.id].ipSuffix, + + ConsumerMnemonic: tr.validatorConfigs[val.id].consumerMnemonic, + ConsumerPrivValidatorKey: tr.validatorConfigs[val.id].consumerPrivValidatorKey, + // if true node will be started with consumer key for each consumer chain + StartWithConsumerKey: tr.validatorConfigs[val.id].useConsumerKey, }) } @@ -786,11 +795,16 @@ func (tr TestRun) delegateTokens( action delegateTokensAction, verbose bool, ) { + toValCfg := tr.validatorConfigs[action.to] + delegateAddr := toValCfg.valoperAddress + if action.chain != chainID("provi") && toValCfg.useConsumerKey { + delegateAddr = toValCfg.consumerValoperAddress + } //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName, "tx", "staking", "delegate", - tr.validatorConfigs[action.to].valoperAddress, + delegateAddr, fmt.Sprint(action.amount)+`stake`, `--from`, `validator`+fmt.Sprint(action.from), @@ -812,21 +826,26 @@ func (tr TestRun) delegateTokens( } type unbondTokensAction struct { - chain chainID - sender validatorID - unbondFrom validatorID - amount uint + chain chainID + sender validatorID + unbondFrom validatorID + amount uint + useFromConsumerKey bool } func (tr TestRun) unbondTokens( action unbondTokensAction, verbose bool, ) { + unbondFrom := tr.validatorConfigs[action.unbondFrom].valoperAddress + if action.chain != chainID("provi") && action.useFromConsumerKey { + unbondFrom = tr.validatorConfigs[action.unbondFrom].consumerValoperAddress + } //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName, "tx", "staking", "unbond", - tr.validatorConfigs[action.unbondFrom].valoperAddress, + unbondFrom, fmt.Sprint(action.amount)+`stake`, `--from`, `validator`+fmt.Sprint(action.sender), @@ -857,14 +876,26 @@ type redelegateTokensAction struct { } func (tr TestRun) redelegateTokens(action redelegateTokensAction, verbose bool) { + srcCfg := tr.validatorConfigs[action.src] + dstCfg := tr.validatorConfigs[action.dst] + + redelegateSrc := srcCfg.valoperAddress + if action.chain != chainID("provi") && srcCfg.useConsumerKey { + redelegateSrc = srcCfg.consumerValoperAddress + } + + redelegateDst := dstCfg.valoperAddress + if action.chain != chainID("provi") && dstCfg.useConsumerKey { + redelegateDst = dstCfg.consumerValoperAddress + } //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName, "tx", "staking", "redelegate", - tr.validatorConfigs[action.src].valoperAddress, - tr.validatorConfigs[action.dst].valoperAddress, + redelegateSrc, + redelegateDst, fmt.Sprint(action.amount)+`stake`, `--from`, `validator`+fmt.Sprint(action.txSender), `--chain-id`, string(tr.chainConfigs[action.chain].chainId), @@ -1055,3 +1086,88 @@ func (tr TestRun) invokeDoublesignSlash( } tr.waitBlocks("provi", 10, 2*time.Minute) } + +type assignConsumerPubKeyAction struct { + chain chainID + validator validatorID + // reconfigureNode will change keys the node uses and restart + reconfigureNode bool +} + +func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbose bool) { + valCfg := tr.validatorConfigs[action.validator] + + assignKey := fmt.Sprintf( + `%s tx provider assign-consensus-key %s '%s' --from validator%s --chain-id %s --home %s --node %s --gas 900000 --keyring-backend test -b block -y`, + tr.chainConfigs[chainID("provi")].binaryName, + string(tr.chainConfigs[action.chain].chainId), + valCfg.consumerValPubKey, + action.validator, + tr.chainConfigs[chainID("provi")].chainId, + tr.getValidatorHome(chainID("provi"), action.validator), + tr.getValidatorNode(chainID("provi"), action.validator), + ) + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + cmd := exec.Command("docker", "exec", + tr.containerConfig.instanceName, + "/bin/bash", "-c", + assignKey, + ) + + if verbose { + fmt.Println("assignConsumerPubKey cmd:", cmd.String()) + } + + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // node was started with provider key + // we swap the nodes's keys for consumer keys and restart it + if action.reconfigureNode { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + configureNodeCmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "/bin/bash", + "/testnet-scripts/reconfigure-node.sh", tr.chainConfigs[action.chain].binaryName, + string(action.validator), string(action.chain), + tr.chainConfigs[action.chain].ipPrefix, valCfg.ipSuffix, + valCfg.consumerMnemonic, valCfg.consumerPrivValidatorKey, + valCfg.consumerNodeKey, + ) + + if verbose { + fmt.Println("assignConsumerPubKey - 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 { + fmt.Println("assign key - reconfigure: " + out) + + } + if out == "done!!!!!!!!" { + break + } + } + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + // make the validator use consumer key + valCfg.useConsumerKey = true + tr.validatorConfigs[action.validator] = valCfg + } + +} diff --git a/tests/integration/config.go b/tests/integration/config.go index 5bbc424959..07a03eef99 100644 --- a/tests/integration/config.go +++ b/tests/integration/config.go @@ -19,8 +19,19 @@ type ValidatorConfig struct { valconsAddress string privValidatorKey string nodeKey string - // Must be an integer greater than 0 and less than 254 + // Must be an integer greater than 0 and less than 253 ipSuffix string + + // consumer chain key assignment data + // keys are used on a new node + consumerMnemonic string + consumerDelAddress string + consumerValoperAddress string + consumerValconsAddress string + consumerValPubKey string + consumerPrivValidatorKey string + consumerNodeKey string + useConsumerKey bool // if true the validator node will start with consumer key } // Attributes that are unique to a chain. Allows us to map (part of) @@ -83,6 +94,107 @@ func getDefaultValidators() map[validatorID]ValidatorConfig { privValidatorKey: `{"address":"C888306A908A217B9A943D1DAD8790044D0947A4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"IHo4QEikWZfIKmM0X+N+BjKttz8HOzGs2npyjiba3Xk="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"z08bmSB91uFVpVmR3t2ewd/bDjZ/AzwQpe5rKjWiPG0gejhASKRZl8gqYzRf434GMq23Pwc7MazaenKOJtrdeQ=="}}`, nodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"WLTcHEjbwB24Wp3z5oBSYTvtGQonz/7IQabOFw85BN0UkkyY5HDf38o8oHlFxVI26f+DFVeICuLbe9aXKGnUeg=="}}`, ipSuffix: "6", + + // consumer chain assigned key + consumerMnemonic: "clip choose cake west range gun slam cry village receive juice galaxy lend ritual range provide ritual can since verify breeze vacant play dragon", + consumerDelAddress: "cosmos1sx6j9g2rh324a342a5f0rnx7me34r9nwgf0mc7", + consumerValoperAddress: "cosmosvaloper1sx6j9g2rh324a342a5f0rnx7me34r9nwdamw5d", + consumerValconsAddress: "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk", + consumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`, + consumerPrivValidatorKey: `{"address":"B41C3A40142963AA5B12DDD1C4E5890C0B3926B1","pub_key":{"type":"tendermint/PubKeyEd25519","value":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"3YaBAZLA+sl/E73lLfbFbG0u6DYm33ayr/0UpCt/vFBSLkZ/X6a1ZR0fy7fGWbN0ogP4Xc8rSx9dnvcZnqrqKw=="}}`, + consumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"rxBzFedtD3pqgfJQblbxGusKOr47oBfr8ba0Iz14gobtDRZQZlSZ/UGP4pSHkVf+4vtkrkO1vRHBYJobuiP+7A=="}}`, + useConsumerKey: true, + }, + } +} + +func getKeyAssignValidators() map[validatorID]ValidatorConfig { + return map[validatorID]ValidatorConfig{ + validatorID("alice"): { + mnemonic: "pave immune ethics wrap gain ceiling always holiday employ earth tumble real ice engage false unable carbon equal fresh sick tattoo nature pupil nuclear", + delAddress: "cosmos19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddwhu7lm", + valoperAddress: "cosmosvaloper19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddtrgtng", + valconsAddress: "cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq", + privValidatorKey: `{"address":"06C0F3E47CC5C748269088DC2F36411D3AAA27C6","pub_key":{"type":"tendermint/PubKeyEd25519","value":"RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"uX+ZpDMg89a6gtqs/+MQpCTSqlkZ0nJQJOhLlCJvwvdGtyVDP1siGQjL+B8vjzmDc9gx6IiS7ip6jj3nvztfXQ=="}}`, + nodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"fjw4/DAhyRPnwKgXns5SV7QfswRSXMWJpHS7TyULDmJ8ofUc5poQP8dgr8bZRbCV5RV8cPqDq3FPdqwpmUbmdA=="}}`, + ipSuffix: "4", + }, + validatorID("bob"): { + mnemonic: "glass trip produce surprise diamond spin excess gaze wash drum human solve dress minor artefact canoe hard ivory orange dinner hybrid moral potato jewel", + delAddress: "cosmos1dkas8mu4kyhl5jrh4nzvm65qz588hy9qcz08la", + valoperAddress: "cosmosvaloper1dkas8mu4kyhl5jrh4nzvm65qz588hy9qakmjnw", + valconsAddress: "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39", + privValidatorKey: `{"address":"99BD3A72EF12CD024E7584B3AC900AE3743C6ADF","pub_key":{"type":"tendermint/PubKeyEd25519","value":"mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"QePcwfWtOavNK7pBJrtoLMzarHKn6iBWfWPFeyV+IdmYA3pFdjFIzgw0ZIiuJiJLuke7ABw4cNADL3/CeVLM4g=="}}`, + nodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"TQ4vHcO/vKdzGtWpelkX53WdMQd4kTsWGFrdcatdXFvWyO215Rewn5IRP0FszPLWr2DqPzmuH8WvxYGk5aeOXw=="}}`, + ipSuffix: "5", + + // consumer chain assigned key + consumerMnemonic: "grunt list hour endless observe better spoil penalty lab duck only layer vague fantasy satoshi record demise topple space shaft solar practice donor sphere", + consumerDelAddress: "cosmos1q90l6j6lzzgt460ehjj56azknlt5yrd4s38n97", + consumerValoperAddress: "cosmosvaloper1q90l6j6lzzgt460ehjj56azknlt5yrd449nxfd", + consumerValconsAddress: "cosmosvalcons1uuec3cjxajv5te08p220usrjhkfhg9wyvqn0tm", + consumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="}`, + consumerPrivValidatorKey: `{"address":"E73388E246EC9945E5E70A94FE4072BD937415C4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"OFR4w+FC6EMw5fAGTrHVexyPrjzQ7QfqgZOMgVf0izlCUb6Jh7oDJim9jXP1E0koJWUfXhD+pLPgSMZ0YKu7eg=="}}`, + consumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"rxBzFedtD3pqgfJQblbxGusKOr47oBfr8ba0Iz14gobtDRZQZlSZ/UGP4pSHkVf+4vtkrkO1vRHBYJobuiP+7A=="}}`, + useConsumerKey: false, + }, + validatorID("carol"): { + mnemonic: "sight similar better jar bitter laptop solve fashion father jelly scissors chest uniform play unhappy convince silly clump another conduct behave reunion marble animal", + delAddress: "cosmos19hz4m226ztankqramvt4a7t0shejv4dc79gp9u", + valoperAddress: "cosmosvaloper19hz4m226ztankqramvt4a7t0shejv4dcm3u5f0", + valconsAddress: "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6", + privValidatorKey: `{"address":"C888306A908A217B9A943D1DAD8790044D0947A4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"IHo4QEikWZfIKmM0X+N+BjKttz8HOzGs2npyjiba3Xk="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"z08bmSB91uFVpVmR3t2ewd/bDjZ/AzwQpe5rKjWiPG0gejhASKRZl8gqYzRf434GMq23Pwc7MazaenKOJtrdeQ=="}}`, + nodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"WLTcHEjbwB24Wp3z5oBSYTvtGQonz/7IQabOFw85BN0UkkyY5HDf38o8oHlFxVI26f+DFVeICuLbe9aXKGnUeg=="}}`, + ipSuffix: "6", + + // consumer chain assigned key + consumerMnemonic: "clip choose cake west range gun slam cry village receive juice galaxy lend ritual range provide ritual can since verify breeze vacant play dragon", + consumerDelAddress: "cosmos1sx6j9g2rh324a342a5f0rnx7me34r9nwgf0mc7", + consumerValoperAddress: "cosmosvaloper1sx6j9g2rh324a342a5f0rnx7me34r9nwdamw5d", + consumerValconsAddress: "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk", + consumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`, + consumerPrivValidatorKey: `{"address":"B41C3A40142963AA5B12DDD1C4E5890C0B3926B1","pub_key":{"type":"tendermint/PubKeyEd25519","value":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"3YaBAZLA+sl/E73lLfbFbG0u6DYm33ayr/0UpCt/vFBSLkZ/X6a1ZR0fy7fGWbN0ogP4Xc8rSx9dnvcZnqrqKw=="}}`, + consumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"rxBzFedtD3pqgfJQblbxGusKOr47oBfr8ba0Iz14gobtDRZQZlSZ/UGP4pSHkVf+4vtkrkO1vRHBYJobuiP+7A=="}}`, + useConsumerKey: true, + }, + } +} + +func KeyAssignmentTestRun() TestRun { + return TestRun{ + name: "key-assignment", + containerConfig: ContainerConfig{ + containerName: "interchain-security-keys-container", + instanceName: "interchain-security-keys-instance", + ccvVersion: "1", + now: time.Now(), + }, + validatorConfigs: getKeyAssignValidators(), + chainConfigs: map[chainID]ChainConfig{ + chainID("provi"): { + chainId: chainID("provi"), + binaryName: "interchain-security-pd", + ipPrefix: "7.7.7", + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + + // Custom slashing parameters for testing validator downtime functionality + // See https://docs.cosmos.network/main/modules/slashing/04_begin_block.html#uptime-tracking + ".app_state.slashing.params.signed_blocks_window = \"2\" | " + + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + + ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", + }, + chainID("consu"): { + chainId: chainID("consu"), + binaryName: "interchain-security-cd", + ipPrefix: "7.7.8", + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + + ".app_state.slashing.params.signed_blocks_window = \"200\" | " + + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + + ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", + }, }, } } @@ -102,8 +214,8 @@ func DefaultTestRun() TestRun { chainId: chainID("provi"), binaryName: "interchain-security-pd", ipPrefix: "7.7.7", - votingWaitTime: 5, - genesisChanges: ".app_state.gov.voting_params.voting_period = \"5s\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + // Custom slashing parameters for testing validator downtime functionality // See https://docs.cosmos.network/main/modules/slashing/04_begin_block.html#uptime-tracking ".app_state.slashing.params.signed_blocks_window = \"2\" | " + @@ -115,8 +227,8 @@ func DefaultTestRun() TestRun { chainId: chainID("consu"), binaryName: "interchain-security-cd", ipPrefix: "7.7.8", - votingWaitTime: 10, - genesisChanges: ".app_state.gov.voting_params.voting_period = \"10s\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + ".app_state.slashing.params.signed_blocks_window = \"2\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + @@ -141,8 +253,8 @@ func DemocracyTestRun() TestRun { chainId: chainID("provi"), binaryName: "interchain-security-pd", ipPrefix: "7.7.7", - votingWaitTime: 5, - genesisChanges: ".app_state.gov.voting_params.voting_period = \"5s\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + // Custom slashing parameters for testing validator downtime functionality // See https://docs.cosmos.network/main/modules/slashing/04_begin_block.html#uptime-tracking ".app_state.slashing.params.signed_blocks_window = \"2\" | " + @@ -154,8 +266,8 @@ func DemocracyTestRun() TestRun { chainId: chainID("democ"), binaryName: "interchain-security-cdd", ipPrefix: "7.7.9", - votingWaitTime: 10, - genesisChanges: ".app_state.ccvconsumer.params.blocks_per_distribution_transmission = \"10\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.ccvconsumer.params.blocks_per_distribution_transmission = \"20\" | " + ".app_state.gov.voting_params.voting_period = \"10s\" | " + ".app_state.slashing.params.signed_blocks_window = \"2\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + @@ -181,8 +293,8 @@ func MultiConsumerTestRun() TestRun { chainId: chainID("provi"), binaryName: "interchain-security-pd", ipPrefix: "7.7.7", - votingWaitTime: 5, - genesisChanges: ".app_state.gov.voting_params.voting_period = \"5s\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + // Custom slashing parameters for testing validator downtime functionality // See https://docs.cosmos.network/main/modules/slashing/04_begin_block.html#uptime-tracking ".app_state.slashing.params.signed_blocks_window = \"2\" | " + @@ -194,8 +306,8 @@ func MultiConsumerTestRun() TestRun { chainId: chainID("consu"), binaryName: "interchain-security-cd", ipPrefix: "7.7.8", - votingWaitTime: 10, - genesisChanges: ".app_state.gov.voting_params.voting_period = \"10s\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + ".app_state.slashing.params.signed_blocks_window = \"2\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + @@ -205,8 +317,8 @@ func MultiConsumerTestRun() TestRun { chainId: chainID("densu"), binaryName: "interchain-security-cd", ipPrefix: "7.7.9", - votingWaitTime: 10, - genesisChanges: ".app_state.gov.voting_params.voting_period = \"10s\" | " + + votingWaitTime: 20, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"20s\" | " + ".app_state.slashing.params.signed_blocks_window = \"2\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + diff --git a/tests/integration/main.go b/tests/integration/main.go index a07043a46b..dc6399289a 100644 --- a/tests/integration/main.go +++ b/tests/integration/main.go @@ -42,6 +42,14 @@ func main() { mul.ValidateStringLiterals() mul.startDocker() + keys := KeyAssignmentTestRun() + keys.SetLocalSDKPath(*localSdkPath) + keys.ValidateStringLiterals() + keys.startDocker() + + wg.Add(1) + go keys.ExecuteSteps(&wg, keyAssignmentSteps) + wg.Add(1) go tr.ExecuteSteps(&wg, happyPathSteps) @@ -99,6 +107,8 @@ func (tr *TestRun) runStep(step Step, verbose bool) { tr.invokeDoublesignSlash(action, verbose) case registerRepresentativeAction: tr.registerRepresentative(action, verbose) + case assignConsumerPubKeyAction: + tr.assignConsumerPubKey(action, verbose) default: log.Fatalf(fmt.Sprintf(`unknown action in testRun %s: %#v`, tr.name, action)) } diff --git a/tests/integration/state.go b/tests/integration/state.go index bfd16ca38e..32c3b704a3 100644 --- a/tests/integration/state.go +++ b/tests/integration/state.go @@ -23,6 +23,8 @@ type ChainState struct { Params *[]Param Rewards *Rewards ConsumerChains *map[chainID]bool + AssignedKeys *map[validatorID]string + ProviderKeys *map[validatorID]string // validatorID: validator provider key } type Proposal interface { @@ -130,6 +132,16 @@ func (tr TestRun) getChainState(chain chainID, modelState ChainState) ChainState chainState.ConsumerChains = &chains } + if modelState.AssignedKeys != nil { + assignedKeys := tr.getConsumerAddresses(chain, *modelState.AssignedKeys) + chainState.AssignedKeys = &assignedKeys + } + + if modelState.ProviderKeys != nil { + providerKeys := tr.getProviderAddresses(chain, *modelState.ProviderKeys) + chainState.ProviderKeys = &providerKeys + } + return chainState } @@ -236,11 +248,16 @@ func (tr TestRun) getRewards(chain chainID, modelState Rewards) Rewards { } func (tr TestRun) getReward(chain chainID, validator validatorID, blockHeight uint, isNativeDenom bool) float64 { + + delAddresss := tr.validatorConfigs[validator].delAddress + if chain != chainID("provi") && tr.validatorConfigs[validator].useConsumerKey { + delAddresss = tr.validatorConfigs[validator].consumerDelAddress + } //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, "query", "distribution", "rewards", - tr.validatorConfigs[validator].delAddress, + delAddresss, `--height`, fmt.Sprint(blockHeight), `--node`, tr.getQueryNode(chain), @@ -261,10 +278,14 @@ func (tr TestRun) getReward(chain chainID, validator validatorID, blockHeight ui func (tr TestRun) getBalance(chain chainID, validator validatorID) uint { //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + valDelAddress := tr.validatorConfigs[validator].delAddress + if chain != chainID("provi") && tr.validatorConfigs[validator].useConsumerKey { + valDelAddress = tr.validatorConfigs[validator].consumerDelAddress + } bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, "query", "bank", "balances", - tr.validatorConfigs[validator].delAddress, + valDelAddress, `--node`, tr.getQueryNode(chain), `-o`, `json`, @@ -418,7 +439,9 @@ func (tr TestRun) getValPower(chain chainID, validator validatorID) uint { } for _, val := range valset.Validators { - if val.Address == tr.validatorConfigs[validator].valconsAddress { + if val.Address == tr.validatorConfigs[validator].valconsAddress || + val.Address == tr.validatorConfigs[validator].consumerValconsAddress { + votingPower, err := strconv.Atoi(val.VotingPower) if err != nil { log.Fatalf("error: %v", err) @@ -499,6 +522,60 @@ func (tr TestRun) getConsumerChains(chain chainID) map[chainID]bool { return chains } +func (tr TestRun) getConsumerAddresses(chain chainID, modelState map[validatorID]string) map[validatorID]string { + actualState := map[validatorID]string{} + for k := range modelState { + actualState[k] = tr.getConsumerAddress(chain, k) + } + + return actualState +} + +func (tr TestRun) getProviderAddresses(chain chainID, modelState map[validatorID]string) map[validatorID]string { + actualState := map[validatorID]string{} + for k := range modelState { + actualState[k] = tr.getProviderAddressFromConsumer(chain, k) + } + + return actualState +} + +func (tr TestRun) getConsumerAddress(consumerChain chainID, validator validatorID) string { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chainID("provi")].binaryName, + + "query", "provider", "validator-consumer-key", + string(consumerChain), tr.validatorConfigs[validator].valconsAddress, + `--node`, tr.getQueryNode(chainID("provi")), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + addr := gjson.Get(string(bz), "consumer_address").String() + return addr +} + +func (tr TestRun) getProviderAddressFromConsumer(consumerChain chainID, validator validatorID) string { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + cmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chainID("provi")].binaryName, + + "query", "provider", "validator-provider-key", + string(consumerChain), tr.validatorConfigs[validator].consumerValconsAddress, + `--node`, tr.getQueryNode(chainID("provi")), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + addr := gjson.Get(string(bz), "provider_address").String() + return addr +} + func (tr TestRun) getValidatorNode(chain chainID, validator validatorID) string { return "tcp://" + tr.getValidatorIP(chain, validator) + ":26658" } diff --git a/tests/integration/step_delegation.go b/tests/integration/step_delegation.go index af969e8f08..d1fb2e9de6 100644 --- a/tests/integration/step_delegation.go +++ b/tests/integration/step_delegation.go @@ -145,6 +145,7 @@ func stepsRedelegate(consumerName string) []Step { ValPowers: &map[validatorID]uint{ validatorID("alice"): 509, validatorID("bob"): 500, + // carol always uses a consumer assigned key validatorID("carol"): 501, }, }, diff --git a/tests/integration/steps.go b/tests/integration/steps.go index 526a036b5c..8cc6a6a491 100644 --- a/tests/integration/steps.go +++ b/tests/integration/steps.go @@ -13,6 +13,14 @@ func concatSteps(steps ...[]Step) []Step { return concat } +var keyAssignmentSteps = concatSteps( + stepsStartChains([]string{"consu"}, false), + stepsDelegate("consu"), + stepsAssignConsumerKeyOnStartedChain("consu", "bob"), + stepsUnbond("consu"), + stepsRedelegate("consu"), +) + var happyPathSteps = concatSteps( stepsStartChains([]string{"consu"}, false), stepsDelegate("consu"), diff --git a/tests/integration/steps_start_chains.go b/tests/integration/steps_start_chains.go index c6ff985615..d40c24dac8 100644 --- a/tests/integration/steps_start_chains.go +++ b/tests/integration/steps_start_chains.go @@ -57,6 +57,28 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint }, }, }, + // add a consumer key before the chain starts + // the key will be present in consumer genesis initial_val_set + { + action: assignConsumerPubKeyAction{ + chain: chainID(consumerName), + validator: validatorID("carol"), + // consumer chain has not started + // we don't need to reconfigure the node + // since it will start with consumer key + reconfigureNode: false, + }, + state: State{ + chainID(consumerName): ChainState{ + AssignedKeys: &map[validatorID]string{ + validatorID("carol"): "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk", + }, + ProviderKeys: &map[validatorID]string{ + validatorID("carol"): "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6", + }, + }, + }, + }, { action: voteGovProposalAction{ chain: chainID("provi"), @@ -86,11 +108,10 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint action: startConsumerChainAction{ consumerChain: chainID(consumerName), providerChain: chainID("provi"), - // genesisChanges: consumerGenesisParams, validators: []StartChainValidator{ - {id: validatorID("carol"), stake: 500000000, allocation: 10000000000}, - {id: validatorID("alice"), stake: 500000000, allocation: 10000000000}, {id: validatorID("bob"), stake: 500000000, allocation: 10000000000}, + {id: validatorID("alice"), stake: 500000000, allocation: 10000000000}, + {id: validatorID("carol"), stake: 500000000, allocation: 10000000000}, }, }, state: State{ @@ -98,12 +119,14 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint ValBalances: &map[validatorID]uint{ validatorID("alice"): 9500000000, validatorID("bob"): 9500000000, + validatorID("carol"): 9500000000, }, }, chainID(consumerName): ChainState{ ValBalances: &map[validatorID]uint{ validatorID("alice"): 10000000000, validatorID("bob"): 10000000000, + validatorID("carol"): 10000000000, }, }, }, @@ -159,3 +182,62 @@ func stepsStartChains(consumerNames []string, setupTransferChans bool) []Step { return s } + +func stepsAssignConsumerKeyOnStartedChain(consumerName, validator string) []Step { + return []Step{ + { + action: assignConsumerPubKeyAction{ + chain: chainID(consumerName), + validator: validatorID("bob"), + // reconfigure the node -> validator was using provider key + // until this point + reconfigureNode: true, + }, + state: State{ + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + // this happens after some delegations + // so that the chain does not halt if 1/3 of power is offline + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + AssignedKeys: &map[validatorID]string{ + validatorID("bob"): "cosmosvalcons1uuec3cjxajv5te08p220usrjhkfhg9wyvqn0tm", + validatorID("carol"): "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk", + }, + ProviderKeys: &map[validatorID]string{ + validatorID("bob"): "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39", + validatorID("carol"): "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6", + }, + }, + }, + }, + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + // this happens after some delegations + // so that the chain does not halt if 1/3 of power is offline + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + AssignedKeys: &map[validatorID]string{ + validatorID("bob"): "cosmosvalcons1uuec3cjxajv5te08p220usrjhkfhg9wyvqn0tm", + validatorID("carol"): "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk", + }, + ProviderKeys: &map[validatorID]string{ + validatorID("bob"): "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39", + validatorID("carol"): "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6", + }, + }, + }, + }, + } +} diff --git a/tests/integration/testnet-scripts/reconfigure-node.sh b/tests/integration/testnet-scripts/reconfigure-node.sh new file mode 100755 index 0000000000..aa9eaf3e91 --- /dev/null +++ b/tests/integration/testnet-scripts/reconfigure-node.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +## Key assignment of a previosuly active validator is done as follows: +# - stop node +# - replace config/node_key.json +# - replace config/priv_validator_key.json with keys to use on consumer chain +# - replace config/priv_validator_state.json -> node will need to catch up +# - restart node -> it will use the new key to sign blocks +set -eux + + +BIN=$1 +VAL_ID=$2 +CHAIN_ID=$3 +CHAIN_IP_PREFIX=$4 +VAL_IP_SUFFIX=$5 + +CONSUMER_MNEMONIC=$6 +CONSUMER_PRIVATE_KEY=$7 +CONSUMER_NODE_KEY=$8 + +# kill validator node +pkill -f /$CHAIN_ID/validator$VAL_ID + +# swap valstate -> validator will sync on restart +echo '{"height": "0","round": 0,"step": 0,"signature":"","signbytes":""}' > /$CHAIN_ID/validator$VAL_ID/data/priv_validator_state.json + + +# swap private key +# echo "$CONSUMER_NODE_KEY" > /$CHAIN_ID/assignvalidator$VAL_ID/config/node_key.json +echo "$CONSUMER_PRIVATE_KEY" > /$CHAIN_ID/validator$VAL_ID/config/priv_validator_key.json + +echo "$CONSUMER_NODE_KEY" > /$CHAIN_ID/validator$VAL_ID/config/node_key.json + +# add new key from mnemonic +echo "$CONSUMER_MNEMONIC" | $BIN keys add $CHAIN_ID-validator$VAL_ID --keyring-backend test --home /$CHAIN_ID/validator$VAL_ID --recover --output json + + +# restart node with new key +ARGS="--home /$CHAIN_ID/validator$VAL_ID --address tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26655 --rpc.laddr tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26658 --grpc.address $CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:9091 --log_level trace --p2p.laddr tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26656 --grpc-web.enable=false" + +ip netns exec $CHAIN_ID-$VAL_ID $BIN $ARGS start &> /$CHAIN_ID/validator$VAL_ID/logs & + +echo 'done!!!!!!!!' diff --git a/tests/integration/testnet-scripts/start-chain.sh b/tests/integration/testnet-scripts/start-chain.sh index b7b16ef512..8416b456dc 100644 --- a/tests/integration/testnet-scripts/start-chain.sh +++ b/tests/integration/testnet-scripts/start-chain.sh @@ -101,14 +101,22 @@ mv /$CHAIN_ID/edited-genesis.json /$CHAIN_ID/genesis.json for i in $(seq 0 $(($NODES - 1))); do VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") - + START_WITH_CONSUMER_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].start_with_consumer_key") # Generate an application key for each validator # Sets up an arbitrary number of validators on a single machine by manipulating # the --home parameter on gaiad - echo "$VALIDATORS" | jq -r ".[$i].mnemonic" | $BIN keys add validator$VAL_ID \ + # optionally start validator with a key different from provider chain key + if [[ "$CHAIN_ID" != "provi" && "$START_WITH_CONSUMER_KEY" = "true" ]]; then + echo "$VALIDATORS" | jq -r ".[$i].consumer_mnemonic" | $BIN keys add validator$VAL_ID \ + --home /$CHAIN_ID/validator$VAL_ID \ + --keyring-backend test \ + --recover > /dev/null + else + echo "$VALIDATORS" | jq -r ".[$i].mnemonic" | $BIN keys add validator$VAL_ID \ --home /$CHAIN_ID/validator$VAL_ID \ --keyring-backend test \ --recover > /dev/null + fi # Give validators their initial token allocations # move the genesis in @@ -131,15 +139,25 @@ done for i in $(seq 0 $(($NODES - 1))); do VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") + # Copy in the genesis.json cp /$CHAIN_ID/genesis.json /$CHAIN_ID/validator$VAL_ID/config/genesis.json # Copy in validator state file echo '{"height": "0","round": 0,"step": 0}' > /$CHAIN_ID/validator$VAL_ID/data/priv_validator_state.json - PRIV_VALIDATOR_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].priv_validator_key") - if [[ "$PRIV_VALIDATOR_KEY" ]]; then - echo "$PRIV_VALIDATOR_KEY" > /$CHAIN_ID/validator$VAL_ID/config/priv_validator_key.json + START_WITH_CONSUMER_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].start_with_consumer_key") + if [[ "$CHAIN_ID" != "provi" && "$START_WITH_CONSUMER_KEY" = "true" ]]; then + # start with assigned consumer key + PRIV_VALIDATOR_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].consumer_priv_validator_key") + if [[ "$PRIV_VALIDATOR_KEY" ]]; then + echo "$PRIV_VALIDATOR_KEY" > /$CHAIN_ID/validator$VAL_ID/config/priv_validator_key.json + fi + else + PRIV_VALIDATOR_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].priv_validator_key") + if [[ "$PRIV_VALIDATOR_KEY" ]]; then + echo "$PRIV_VALIDATOR_KEY" > /$CHAIN_ID/validator$VAL_ID/config/priv_validator_key.json + fi fi NODE_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].node_key") diff --git a/x/ccv/provider/client/cli/flags.go b/x/ccv/provider/client/cli/flags.go deleted file mode 100644 index 2abfa5b78c..0000000000 --- a/x/ccv/provider/client/cli/flags.go +++ /dev/null @@ -1,36 +0,0 @@ -package cli - -import ( - flag "github.com/spf13/pflag" -) - -const ( - FlagConsumerChainId = "validator" - FlagAddressValidator = "validator" - FlagConsumerPubKey = "pubkey" - FlagNodeID = "node-id" - FlagIP = "ip" -) - -// common flagsets to add to various functions -var ( - fsValidator = flag.NewFlagSet("", flag.ContinueOnError) -) - -func init() { - fsValidator.String(FlagAddressValidator, "", "The Bech32 address of the validator") -} - -// FlagSetPublicKey Returns the flagset for Public Key related operations. -func FlagSetPublicKey() *flag.FlagSet { - fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.String(FlagConsumerPubKey, "", "The Protobuf JSON encoded public key to use for the consumer chain") - return fs -} - -// FlagSetPublicKey Returns the flagset for Public Key related operations. -func FlagSetConsumerChainId() *flag.FlagSet { - fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.String(FlagConsumerChainId, "", "The chainId of the consumer chain") - return fs -} diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index de6269d981..e505023fd4 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -1,10 +1,16 @@ package cli import ( + "fmt" + "strings" + "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/interchain-security/x/ccv/provider/types" ) @@ -22,6 +28,8 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdConsumerChains()) cmd.AddCommand(CmdConsumerStartProposals()) cmd.AddCommand(CmdConsumerStopProposals()) + cmd.AddCommand(CmdConsumerValidatorKeyAssignment()) + cmd.AddCommand(CmdProviderValidatorKey()) return cmd } @@ -143,3 +151,98 @@ func CmdConsumerStopProposals() *cobra.Command { return cmd } + +// TODO: fix naming +func CmdConsumerValidatorKeyAssignment() *cobra.Command { + bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() + cmd := &cobra.Command{ + Use: "validator-consumer-key [chainid] [provider-validator-address]", + Short: "Query assigned validator consensus public key for a consumer chain", + Long: strings.TrimSpace( + fmt.Sprintf(`Returns the currently assigned validator consensus public key for a +consumer chain, if one has been assigned. +Example: +$ %s query provider validator-consumer-key foochain %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixConsAddr, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + consumerChainID := args[0] + + addr, err := sdk.ConsAddressFromBech32(args[1]) + if err != nil { + return err + } + + req := &types.QueryValidatorConsumerAddrRequest{ + ChainId: consumerChainID, + ProviderAddress: addr.String(), + } + res, err := queryClient.QueryValidatorConsumerAddr(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// TODO: fix naming +func CmdProviderValidatorKey() *cobra.Command { + bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() + cmd := &cobra.Command{ + Use: "validator-provider-key [chainid] [consumer-validator-address]", + Short: "Query validator consensus public key for the provider chain", + Long: strings.TrimSpace( + fmt.Sprintf(`Returns the currently assigned validator consensus public key for the provider chain. +Example: +$ %s query provider validator-provider-key foochain %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixConsAddr, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + consumerChainID := args[0] + + addr, err := sdk.ConsAddressFromBech32(args[1]) + if err != nil { + return err + } + + req := &types.QueryValidatorProviderAddrRequest{ + ChainId: consumerChainID, + ConsumerAddress: addr.String(), + } + res, err := queryClient.QueryValidatorProviderAddr(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index 13b9413e2e..edd230e15c 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -12,7 +12,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/interchain-security/x/ccv/provider/types" - flag "github.com/spf13/pflag" ) // GetTxCmd returns the transaction commands for this module @@ -34,6 +33,7 @@ func NewAssignConsumerKeyCmd() *cobra.Command { cmd := &cobra.Command{ Use: "assign-consensus-key [consumer-chain-id] [consumer-pubkey]", Short: "assign a consensus public key to use for a consumer chain", + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -42,61 +42,28 @@ func NewAssignConsumerKeyCmd() *cobra.Command { txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()). WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) - txf, msg, err := newAssignConsumerKey(clientCtx, txf, cmd.Flags()) + + providerValAddr := clientCtx.GetFromAddress() + var consumerPubKey cryptotypes.PubKey + if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(args[1]), &consumerPubKey); err != nil { + return err + } + + msg, err := types.NewMsgAssignConsumerKey(args[0], sdk.ValAddress(providerValAddr), consumerPubKey) if err != nil { return err } + if err := msg.ValidateBasic(); err != nil { + return err + } return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) }, } - cmd.Flags().AddFlagSet(FlagSetConsumerChainId()) - cmd.Flags().AddFlagSet(FlagSetPublicKey()) - - cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", flags.FlagGenerateOnly)) - cmd.Flags().String(FlagNodeID, "", "The node's ID") flags.AddTxFlagsToCmd(cmd) _ = cmd.MarkFlagRequired(flags.FlagFrom) - _ = cmd.MarkFlagRequired(FlagConsumerChainId) - _ = cmd.MarkFlagRequired(FlagConsumerPubKey) return cmd } - -func newAssignConsumerKey(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, *types.MsgAssignConsumerKey, error) { - - providerValAddr := clientCtx.GetFromAddress() - consumerPubKeyStr, err := fs.GetString(FlagConsumerPubKey) - if err != nil { - return txf, nil, err - } - - var consumerPubKey cryptotypes.PubKey - if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(consumerPubKeyStr), &consumerPubKey); err != nil { - return txf, nil, err - } - - chainId, _ := fs.GetString(FlagConsumerChainId) - - msg, err := types.NewMsgAssignConsumerKey(chainId, sdk.ValAddress(providerValAddr), consumerPubKey) - if err != nil { - return txf, nil, err - } - if err := msg.ValidateBasic(); err != nil { - return txf, nil, err - } - - genOnly, _ := fs.GetBool(flags.FlagGenerateOnly) - if genOnly { - ip, _ := fs.GetString(FlagIP) - nodeID, _ := fs.GetString(FlagNodeID) - - if nodeID != "" && ip != "" { - txf = txf.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) - } - } - - return txf, msg, nil -} diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index d7477b7927..af24b6cc00 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -80,10 +80,6 @@ func (k Keeper) QueryValidatorConsumerAddr(goCtx context.Context, req *types.Que ctx := sdk.UnwrapSDKContext(goCtx) - if _, found := k.GetConsumerClientId(ctx, req.ChainId); !found { - return nil, types.ErrUnknownConsumerChainId - } - providerAddr, err := sdk.ConsAddressFromBech32(req.ProviderAddress) if err != nil { return nil, err @@ -91,7 +87,7 @@ func (k Keeper) QueryValidatorConsumerAddr(goCtx context.Context, req *types.Que consumerKey, found := k.GetValidatorConsumerPubKey(ctx, req.ChainId, providerAddr) if !found { - return nil, types.ErrNoValidatorConsumerAddress + return &types.QueryValidatorConsumerAddrResponse{}, nil } return &types.QueryValidatorConsumerAddrResponse{ @@ -106,10 +102,6 @@ func (k Keeper) QueryValidatorProviderAddr(goCtx context.Context, req *types.Que ctx := sdk.UnwrapSDKContext(goCtx) - if _, found := k.GetConsumerClientId(ctx, req.ChainId); !found { - return nil, types.ErrUnknownConsumerChainId - } - consumerAddr, err := sdk.ConsAddressFromBech32(req.ConsumerAddress) if err != nil { return nil, err @@ -117,7 +109,7 @@ func (k Keeper) QueryValidatorProviderAddr(goCtx context.Context, req *types.Que providerAddr, found := k.GetValidatorByConsumerAddr(ctx, req.ChainId, consumerAddr) if !found { - return nil, types.ErrNoValidatorProviderAddress + return &types.QueryValidatorProviderAddrResponse{}, nil } return &types.QueryValidatorProviderAddrResponse{ diff --git a/x/ccv/utils/utils.go b/x/ccv/utils/utils.go index 342760f239..9cac156662 100644 --- a/x/ccv/utils/utils.go +++ b/x/ccv/utils/utils.go @@ -84,5 +84,6 @@ func SendIBCPacket( channel.Counterparty.PortId, channel.Counterparty.ChannelId, clienttypes.Height{}, uint64(ctx.BlockTime().Add(timeoutPeriod).UnixNano()), ) + return channelKeeper.SendPacket(ctx, channelCap, packet) }