Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,100 @@ import (
"github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms"
)

// TestTransferOwnershipAddressOnlyRef verifies that TransferOwnershipChangeset and
// AcceptOwnershipChangeset succeed when ContractRef contains only an Address with no
// Type/Version/Qualifier, and the contract is not present in the datastore. This exercises
// the best-effort DS enrichment path: DS miss is tolerated as long as Address is set.
Comment thread
chris-de-leon-cll marked this conversation as resolved.
func TestTransferOwnershipAddressOnlyRef(t *testing.T) {
Comment thread
chris-de-leon-cll marked this conversation as resolved.
t.Parallel()
selector := chainsel.TEST_90000001.Selector
env, err := environment.New(t.Context(),
environment.WithEVMSimulated(t, []uint64{selector}),
)
require.NoError(t, err)
env.Logger = logger.Test(t)

evmDeployer := &adapters.EVMDeployer{}
dReg := deploy.GetRegistry()
dReg.RegisterDeployer(chainsel.FamilyEVM, deploy.MCMSVersion, evmDeployer)
deployMCMS := deploy.DeployMCMS(dReg, nil)

output, err := deployMCMS.Apply(*env, deploy.MCMSDeploymentConfig{
AdapterVersion: semver.MustParse("1.0.0"),
MCMS: testhelpers.MCMSInputForQualifier(deploymentutils.CLLQualifier),
Chains: map[uint64]deploy.MCMSDeploymentConfigPerChain{
selector: {
Canceller: testhelpers.SingleGroupMCMS(),
Bypasser: testhelpers.SingleGroupMCMS(),
Proposer: testhelpers.SingleGroupMCMS(),
TimelockMinDelay: big.NewInt(1),
Qualifier: deploymentutils.StringPtr(deploymentutils.CLLQualifier),
},
},
})
require.NoError(t, err)
ds := output.DataStore
require.NoError(t, ds.Merge(env.DataStore))
env.DataStore = ds.Seal()
Comment thread
chris-de-leon-cll marked this conversation as resolved.

// Deploy a router but intentionally do NOT add it to the datastore.
evmChain := env.BlockChains.EVMChains()[selector]
deployRouterOp, err := cldf_ops.ExecuteOperation(env.OperationsBundle, routerops1_2.Deploy, evmChain, contract.DeployInput[routerops1_2.ConstructorArgs]{
ChainSelector: evmChain.Selector,
TypeAndVersion: deployment.NewTypeAndVersion(routerops1_2.ContractType, *semver.MustParse("1.2.0")),
Args: routerops1_2.ConstructorArgs{
WrappedNative: utils.RandomAddress(),
RMNProxy: utils.RandomAddress(),
},
})
require.NoError(t, err)
routerAddr := common.HexToAddress(deployRouterOp.Output.Address)

timelockRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{
ChainSelector: selector,
Type: datastore.ContractType(deploymentutils.RBACTimelock),
Qualifier: deploymentutils.CLLQualifier,
Version: semver.MustParse("1.0.0"),
}, selector, datastore_utils.FullRef)
require.NoError(t, err)

cr := deploy.GetTransferOwnershipRegistry()
evmAdapter := &adapters.EVMTransferOwnershipAdapter{}
cr.RegisterAdapter(chainsel.FamilyEVM, semver.MustParse("1.0.0"), evmAdapter)
mcmsRegistry := changesets.GetRegistry()
mcmsRegistry.RegisterMCMSReader(chainsel.FamilyEVM, &adapters.EVMMCMSReader{})

mcmsInput := mcms.Input{
OverridePreviousRoot: false,
ValidUntil: 3759765795,
TimelockAction: mcms_types.TimelockActionSchedule,
Qualifier: deploymentutils.CLLQualifier,
Description: "address-only ref test",
}

// Transfer ownership using an address-only ref — router is not in the datastore.
transferOutput, err := deploy.TransferOwnershipChangeset(cr, mcmsRegistry).Apply(*env, deploy.TransferOwnershipInput{
AdapterVersion: semver.MustParse("1.0.0"),
MCMS: mcmsInput,
ChainInputs: []deploy.TransferOwnershipPerChainInput{
{
ChainSelector: selector,
ContractRef: []datastore.AddressRef{{Address: routerAddr.Hex()}},
ProposedOwner: timelockRef.Address,
},
},
})
require.NoError(t, err)
require.Equal(t, 1, len(transferOutput.MCMSTimelockProposals))
testhelpers.ProcessTimelockProposals(t, *env, transferOutput.MCMSTimelockProposals, false)

r, err := router.NewRouter(routerAddr, evmChain.Client)
require.NoError(t, err)
owner, err := r.Owner(&bind.CallOpts{Context: t.Context()})
require.NoError(t, err)
require.Equal(t, common.HexToAddress(timelockRef.Address), owner)
}

// TestTransferOwnership tests transferring ownership of deployed contracts via MCMS timelocks.
// It deploys MCMS contracts on two EVM chains, deploys routers, transfers ownership of routers from deployer key to the timelock,
// then transfers ownership from the first timelock to a second timelock.
Expand Down
40 changes: 28 additions & 12 deletions deployment/deploy/transfer_ownership.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,25 @@ func acceptOwnershipApply(cr *TransferOwnershipAdapterRegistry, mcmsRegistry *ch
if err != nil {
return cldf.ChangesetOutput{}, err
}
// if partial refs are provided, resolve to full refs
for i, contractRef := range perChainInputs.ContractRef {
normRef, err := TryNormalizeAddressRef(perChainInputs.ChainSelector, contractRef)
addrRef, err := TryNormalizeAddressRef(perChainInputs.ChainSelector, contractRef)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to normalize contract ref %s for chain %d: %w", datastore_utils.SprintRef(contractRef), perChainInputs.ChainSelector, err)
}
fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, normRef, perChainInputs.ChainSelector, datastore_utils.FullRef)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve contract ref %s for chain %d: %w", datastore_utils.SprintRef(normRef), perChainInputs.ChainSelector, err)
if addrRef.Address != "" {
if fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, addrRef, perChainInputs.ChainSelector, datastore_utils.FullRef); err != nil {
e.Logger.Warnf("failed to resolve contract ref %s for chain %d - using raw input ref instead: %v", datastore_utils.SprintRef(addrRef), perChainInputs.ChainSelector, err)
} else {
addrRef = fullRef
}
} else {
if fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, addrRef, perChainInputs.ChainSelector, datastore_utils.FullRef); err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve contract ref %s for chain %d: %w", datastore_utils.SprintRef(addrRef), perChainInputs.ChainSelector, err)
} else {
addrRef = fullRef
}
}
Comment thread
chris-de-leon-cll marked this conversation as resolved.
Outdated
perChainInputs.ContractRef[i] = fullRef
perChainInputs.ContractRef[i] = addrRef
}
report, err := cldf_ops.ExecuteSequence(e.OperationsBundle, adapter.SequenceAcceptOwnership(), e.BlockChains, perChainInputs)
if err != nil {
Expand Down Expand Up @@ -90,17 +98,25 @@ func transferOwnershipApply(cr *TransferOwnershipAdapterRegistry, mcmsRegistry *
if err != nil {
return cldf.ChangesetOutput{}, err
}
// if partial refs are provided, resolve to full refs
for i, contractRef := range perChainInputs.ContractRef {
normRef, err := TryNormalizeAddressRef(perChainInputs.ChainSelector, contractRef)
addrRef, err := TryNormalizeAddressRef(perChainInputs.ChainSelector, contractRef)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to normalize contract ref %s for chain %d: %w", datastore_utils.SprintRef(contractRef), perChainInputs.ChainSelector, err)
}
fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, normRef, perChainInputs.ChainSelector, datastore_utils.FullRef)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve contract ref %s for chain %d: %w", datastore_utils.SprintRef(normRef), perChainInputs.ChainSelector, err)
if addrRef.Address != "" {
if fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, addrRef, perChainInputs.ChainSelector, datastore_utils.FullRef); err != nil {
e.Logger.Warnf("failed to resolve contract ref %s for chain %d - using raw input ref instead: %v", datastore_utils.SprintRef(addrRef), perChainInputs.ChainSelector, err)
} else {
addrRef = fullRef
}
} else {
if fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, addrRef, perChainInputs.ChainSelector, datastore_utils.FullRef); err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve contract ref %s for chain %d: %w", datastore_utils.SprintRef(addrRef), perChainInputs.ChainSelector, err)
} else {
addrRef = fullRef
}
}
Comment thread
chris-de-leon-cll marked this conversation as resolved.
Outdated
perChainInputs.ContractRef[i] = fullRef
perChainInputs.ContractRef[i] = addrRef
}
chainBatchOps, chainReports, err := transferAndAcceptOwnership(e, adapter, perChainInputs, input.MCMS)
if err != nil {
Expand Down
Loading