Skip to content

Commit

Permalink
Rmnhome dynamic config revoke changesets (#15951)
Browse files Browse the repository at this point in the history
* SetDynamicConfig and Revoke test

* RevokeConfig test

* Skip flaky test

* Skip flaky test

* Change RMN test package

* Move long changeset test to smoke package

* Remove skip

* Update deployment/ccip/changeset/cs_update_rmn_config.go

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

* Update deployment/ccip/changeset/cs_update_rmn_config.go

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

* Update deployment/ccip/changeset/cs_update_rmn_config.go

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

* Update deployment/ccip/changeset/cs_update_rmn_config.go

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

* Address PR feedback

* Address PR feedback

* Update smoke path

* Rename file

* Change package

* Fix gh action

* Fix gh action

* Fix gh action

* Fix gh action

* Fix missing import

* Address PR comments

---------

Co-authored-by: Makram <[email protected]>
  • Loading branch information
carte7000 and makramkd authored Jan 21, 2025
1 parent 344d591 commit f367549
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 6 deletions.
17 changes: 17 additions & 0 deletions .github/integration-in-memory-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,21 @@ runner-test-matrix:
- PR Integration CCIP Tests
test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_token_transfer_test.go -timeout 16m -test.parallel=1 -count=1 -json

- id: smoke/ccip/ccip_cs_update_rmn_config_test.go:*
path: integration-tests/smoke/ccip/ccip_cs_update_rmn_config_test.go
test_env_type: in-memory
runs_on: ubuntu-latest
triggers:
- PR Integration CCIP Tests
test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_cs_update_rmn_config_test.go -timeout 20m -test.parallel=1 -count=1 -json

- id: smoke/ccip/ccip_cs_rmn_curse_uncurse_test.go:*
path: integration-tests/smoke/ccip/ccip_cs_rmn_curse_uncurse_test.go
test_env_type: in-memory
runs_on: ubuntu-latest
triggers:
- PR Integration CCIP Tests
test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_cs_rmn_curse_uncurse_test.go -timeout 10m -test.parallel=1 -count=1 -json


# END: CCIP tests
165 changes: 163 additions & 2 deletions deployment/ccip/changeset/cs_update_rmn_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ var (
_ deployment.ChangeSet[SetRMNHomeCandidateConfig] = SetRMNHomeCandidateConfigChangeset
_ deployment.ChangeSet[PromoteRMNHomeCandidateConfig] = PromoteRMNHomeCandidateConfigChangeset
_ deployment.ChangeSet[SetRMNRemoteConfig] = SetRMNRemoteConfigChangeset
_ deployment.ChangeSet[SetRMNHomeDynamicConfigConfig] = SetRMNHomeDynamicConfigChangeset
_ deployment.ChangeSet[RevokeCandidateConfig] = RevokeRMNHomeCandidateConfigChangeset
)

type SetRMNRemoteOnRMNProxyConfig struct {
Expand Down Expand Up @@ -211,8 +213,13 @@ func (c SetRMNHomeCandidateConfig) Validate(state CCIPOnChainState) error {
}
offchainPublicKeys[node.OffchainPublicKey] = struct{}{}
}
rmnHome := state.Chains[c.HomeChainSelector].RMNHome

homeChain, ok := state.Chains[c.HomeChainSelector]
if !ok {
return fmt.Errorf("chain %d not found", c.HomeChainSelector)
}

rmnHome := homeChain.RMNHome
if rmnHome == nil {
return fmt.Errorf("RMNHome not found for chain %d", c.HomeChainSelector)
}
Expand Down Expand Up @@ -241,7 +248,13 @@ func (c PromoteRMNHomeCandidateConfig) Validate(state CCIPOnChainState) error {
return err
}

rmnHome := state.Chains[c.HomeChainSelector].RMNHome
homeChain, ok := state.Chains[c.HomeChainSelector]

if !ok {
return fmt.Errorf("chain %d not found", c.HomeChainSelector)
}

rmnHome := homeChain.RMNHome
if rmnHome == nil {
return fmt.Errorf("RMNHome not found for chain %d", c.HomeChainSelector)
}
Expand Down Expand Up @@ -326,6 +339,10 @@ func SetRMNHomeCandidateConfigChangeset(e deployment.Environment, config SetRMNH
config.MCMSConfig.MinDelay,
)

if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal for chain %s: %w", homeChain.String(), err)
}

return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{*prop},
}, nil
Expand Down Expand Up @@ -459,6 +476,150 @@ func (c SetRMNRemoteConfig) Validate() error {
return nil
}

type SetRMNHomeDynamicConfigConfig struct {
HomeChainSelector uint64
RMNDynamicConfig rmn_home.RMNHomeDynamicConfig
ActiveDigest [32]byte
MCMS *MCMSConfig
}

func (c SetRMNHomeDynamicConfigConfig) Validate(e deployment.Environment) error {
err := deployment.IsValidChainSelector(c.HomeChainSelector)
if err != nil {
return err
}

state, err := LoadOnchainState(e)
if err != nil {
return fmt.Errorf("failed to load onchain state: %w", err)
}

rmnHome := state.Chains[c.HomeChainSelector].RMNHome
if rmnHome == nil {
return fmt.Errorf("RMNHome not found for chain %s", e.Chains[c.HomeChainSelector].String())
}

currentDigest, err := rmnHome.GetActiveDigest(nil)
if err != nil {
return fmt.Errorf("failed to get RMNHome candidate digest for chain %s: %w", e.Chains[c.HomeChainSelector].String(), err)
}

if currentDigest != c.ActiveDigest {
return fmt.Errorf("onchain active digest (%x) does not match provided digest (%x)", currentDigest[:], c.ActiveDigest[:])
}

if len(c.RMNDynamicConfig.OffchainConfig) != 0 {
return errors.New("RMNDynamicConfig.OffchainConfig must be empty")
}

return nil
}

func SetRMNHomeDynamicConfigChangeset(e deployment.Environment, cfg SetRMNHomeDynamicConfigConfig) (deployment.ChangesetOutput, error) {
err := cfg.Validate(e)
if err != nil {
return deployment.ChangesetOutput{}, err
}

state, err := LoadOnchainState(e)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load onchain state: %w", err)
}
deployerGroup := NewDeployerGroup(e, state, cfg.MCMS)

chain, exists := e.Chains[cfg.HomeChainSelector]
if !exists {
return deployment.ChangesetOutput{}, fmt.Errorf("chain %d not found", cfg.HomeChainSelector)
}

rmnHome := state.Chains[cfg.HomeChainSelector].RMNHome
if rmnHome == nil {
return deployment.ChangesetOutput{}, fmt.Errorf("RMNHome not found for chain %s", chain.String())
}

deployer, err := deployerGroup.GetDeployer(cfg.HomeChainSelector)
if err != nil {
return deployment.ChangesetOutput{}, err
}

_, err = rmnHome.SetDynamicConfig(deployer, cfg.RMNDynamicConfig, cfg.ActiveDigest)

if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to set RMNHome dynamic config for chain %s: %w", chain.String(), err)
}

return deployerGroup.Enact("Set RMNHome dynamic config")
}

type RevokeCandidateConfig struct {
HomeChainSelector uint64
CandidateDigest [32]byte
MCMS *MCMSConfig
}

func (c RevokeCandidateConfig) Validate(e deployment.Environment) error {
err := deployment.IsValidChainSelector(c.HomeChainSelector)
if err != nil {
return err
}

state, err := LoadOnchainState(e)
if err != nil {
return fmt.Errorf("failed to load onchain state: %w", err)
}

rmnHome := state.Chains[c.HomeChainSelector].RMNHome
if rmnHome == nil {
return fmt.Errorf("RMNHome not found for chain %s", e.Chains[c.HomeChainSelector].String())
}

currentDigest, err := rmnHome.GetCandidateDigest(nil)
if err != nil {
return fmt.Errorf("failed to get RMNHome candidate digest for chain %s: %w", e.Chains[c.HomeChainSelector].String(), err)
}

if currentDigest != c.CandidateDigest {
return fmt.Errorf("onchain candidate digest (%x) does not match provided digest (%x)", currentDigest[:], c.CandidateDigest[:])
}

return nil
}

func RevokeRMNHomeCandidateConfigChangeset(e deployment.Environment, cfg RevokeCandidateConfig) (deployment.ChangesetOutput, error) {
err := cfg.Validate(e)
if err != nil {
return deployment.ChangesetOutput{}, err
}

state, err := LoadOnchainState(e)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load onchain state: %w", err)
}
deployerGroup := NewDeployerGroup(e, state, cfg.MCMS)

chain, exists := e.Chains[cfg.HomeChainSelector]
if !exists {
return deployment.ChangesetOutput{}, fmt.Errorf("chain %d not found", cfg.HomeChainSelector)
}

rmnHome := state.Chains[cfg.HomeChainSelector].RMNHome
if rmnHome == nil {
return deployment.ChangesetOutput{}, fmt.Errorf("RMNHome not found for chain %s", chain.String())
}

deployer, err := deployerGroup.GetDeployer(cfg.HomeChainSelector)
if err != nil {
return deployment.ChangesetOutput{}, err
}

_, err = rmnHome.RevokeCandidate(deployer, cfg.CandidateDigest)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to revoke candidate config for chain %s: %w", chain.String(), err)
}

return deployerGroup.Enact("Revoke candidate config")
}

func SetRMNRemoteConfigChangeset(e deployment.Environment, config SetRMNRemoteConfig) (deployment.ChangesetOutput, error) {
state, err := LoadOnchainState(e)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package changeset_test
package ccip

import (
"testing"
Expand Down Expand Up @@ -120,7 +120,7 @@ func TestRMNCurseIdempotent(t *testing.T) {

func TestRMNUncurseIdempotent(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name+"_UNCURESE_IDEMPOTENT_NO_MCMS", func(t *testing.T) {
t.Run(tc.name+"_UNCURSE_IDEMPOTENT_NO_MCMS", func(t *testing.T) {
runRmnUncurseIdempotentTest(t, tc)
})
}
Expand Down Expand Up @@ -172,7 +172,14 @@ func runRmnUncurseTest(t *testing.T, tc CurseTestCase) {

func transferRMNContractToMCMS(t *testing.T, e *testhelpers.DeployedEnv, state changeset.CCIPOnChainState, timelocksPerChain map[uint64]*proposalutils.TimelockExecutionContracts) {
contractsByChain := make(map[uint64][]common.Address)
rmnRemoteAddressesByChain := buildRMNRemoteAddressPerChain(e.Env, state)
rmnRemotePerChain := changeset.BuildRMNRemotePerChain(e.Env, state)
rmnRemoteAddressesByChain := make(map[uint64]common.Address)
for chain, remote := range rmnRemotePerChain {
if remote == nil {
continue
}
rmnRemoteAddressesByChain[chain] = remote.Address()
}
for chainSelector, rmnRemoteAddress := range rmnRemoteAddressesByChain {
contractsByChain[chainSelector] = []common.Address{rmnRemoteAddress}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package changeset_test
package ccip

import (
"testing"

"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -66,6 +68,114 @@ func TestUpdateRMNConfig(t *testing.T) {
}
}

func TestSetDynamicConfig(t *testing.T) {
e, _ := testhelpers.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(e.Env)
require.NoError(t, err)
rmnHome := state.Chains[e.HomeChainSel].RMNHome

nops := []changeset.RMNNopConfig{rmnStaging1, rmnStaging2, rmnStaging3}
nodes := make([]rmn_home.RMNHomeNode, 0, len(nops))
for _, nop := range nops {
nodes = append(nodes, nop.ToRMNHomeNode())
}

setRMNHomeCandidateConfig := changeset.SetRMNHomeCandidateConfig{
HomeChainSelector: e.HomeChainSel,
RMNStaticConfig: rmn_home.RMNHomeStaticConfig{
Nodes: nodes,
OffchainConfig: []byte(""),
},
RMNDynamicConfig: rmn_home.RMNHomeDynamicConfig{
SourceChains: []rmn_home.RMNHomeSourceChain{},
OffchainConfig: []byte(""),
},
}

_, err = changeset.SetRMNHomeCandidateConfigChangeset(e.Env, setRMNHomeCandidateConfig)
require.NoError(t, err)

candidate, err := rmnHome.GetCandidateDigest(nil)
require.NoError(t, err)

promoteCandidateConfig := changeset.PromoteRMNHomeCandidateConfig{
HomeChainSelector: e.HomeChainSel,
DigestToPromote: candidate,
}

_, err = changeset.PromoteRMNHomeCandidateConfigChangeset(e.Env, promoteCandidateConfig)
require.NoError(t, err)

active, err := rmnHome.GetActiveDigest(nil)
require.NoError(t, err)

setDynamicConfig := changeset.SetRMNHomeDynamicConfigConfig{
HomeChainSelector: e.HomeChainSel,
RMNDynamicConfig: rmn_home.RMNHomeDynamicConfig{
SourceChains: []rmn_home.RMNHomeSourceChain{
{
ChainSelector: e.HomeChainSel,
ObserverNodesBitmap: big.NewInt(1),
},
},
OffchainConfig: []byte(""),
},
ActiveDigest: active,
}

_, err = changeset.SetRMNHomeDynamicConfigChangeset(e.Env, setDynamicConfig)
require.NoError(t, err)

dynamicConfig, err := rmnHome.GetConfig(nil, active)
require.NoError(t, err)

require.True(t, dynamicConfig.Ok)
require.Equal(t, setDynamicConfig.RMNDynamicConfig, dynamicConfig.VersionedConfig.DynamicConfig)
}

func TestRevokeConfig(t *testing.T) {
e, _ := testhelpers.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(e.Env)
require.NoError(t, err)
rmnHome := state.Chains[e.HomeChainSel].RMNHome

nops := []changeset.RMNNopConfig{rmnStaging1, rmnStaging2, rmnStaging3}
nodes := make([]rmn_home.RMNHomeNode, 0, len(nops))
for _, nop := range nops {
nodes = append(nodes, nop.ToRMNHomeNode())
}

setRMNHomeCandidateConfig := changeset.SetRMNHomeCandidateConfig{
HomeChainSelector: e.HomeChainSel,
RMNStaticConfig: rmn_home.RMNHomeStaticConfig{
Nodes: nodes,
OffchainConfig: []byte(""),
},
RMNDynamicConfig: rmn_home.RMNHomeDynamicConfig{
SourceChains: []rmn_home.RMNHomeSourceChain{},
OffchainConfig: []byte(""),
},
}

_, err = changeset.SetRMNHomeCandidateConfigChangeset(e.Env, setRMNHomeCandidateConfig)
require.NoError(t, err)

candidate, err := rmnHome.GetCandidateDigest(nil)
require.NoError(t, err)

revokeCandidateConfig := changeset.RevokeCandidateConfig{
HomeChainSelector: e.HomeChainSel,
CandidateDigest: candidate,
}

_, err = changeset.RevokeRMNHomeCandidateConfigChangeset(e.Env, revokeCandidateConfig)
require.NoError(t, err)

newCandidate, err := rmnHome.GetCandidateDigest(nil)
require.NoError(t, err)
require.NotEqual(t, candidate, newCandidate)
}

func updateRMNConfig(t *testing.T, tc updateRMNConfigTestCase) {
e, _ := testhelpers.NewMemoryEnvironment(t)

Expand All @@ -92,6 +202,7 @@ func updateRMNConfig(t *testing.T, tc updateRMNConfigTestCase) {
},
},
})
require.NoError(t, err)
}

rmnHome := state.Chains[e.HomeChainSel].RMNHome
Expand Down

0 comments on commit f367549

Please sign in to comment.