Skip to content

Commit d2eb87f

Browse files
authored
Ccip-7913 Changeset for global curse on a network (#1339)
* global curse uncurse * changes * comments and fixes * fix tests * skip zero address offramps
1 parent 812a127 commit d2eb87f

File tree

7 files changed

+656
-29
lines changed

7 files changed

+656
-29
lines changed

chains/evm/deployment/fastcurse_test.go

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
mcms_types "github.com/smartcontractkit/mcms/types"
1515

1616
"github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_0_0/adapters"
17+
rmnproxyops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_0_0/operations/rmn_proxy"
1718
adaptersv1_5_0 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/adapters"
1819
"github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_6_0/rmn_remote"
1920
"github.com/smartcontractkit/chainlink-ccip/deployment/deploy"
@@ -341,3 +342,350 @@ func TestFastCurse(t *testing.T) {
341342
require.False(t, isCursed, "subject on chain1 should be uncursed on rmnremote in chain2")
342343
t.Logf("Subjects successfully uncursed %x on chain1 %d and %x on chain2 %d", adv1_5_0.SelectorToSubject(chain2), chain1, adv1_6_0.SelectorToSubject(chain1), chain2)
343344
}
345+
346+
func TestFastCurseGlobalCurseOnChain(t *testing.T) {
347+
chain1 := chainsel.TEST_90000004.Selector
348+
chain2 := chainsel.TEST_90000005.Selector
349+
chain3 := chainsel.TEST_90000006.Selector
350+
env, err := environment.New(t.Context(),
351+
environment.WithEVMSimulated(t, []uint64{chain1, chain2, chain3}),
352+
)
353+
require.NoError(t, err)
354+
bundle := env.OperationsBundle
355+
var rmnAddress common.Address
356+
// deploy RMN 1.5 on chain1 and RMN 1.6 on chain2, set up routers, etc.
357+
chain := env.BlockChains.EVMChains()[chain1]
358+
deployRMNOp, err := cldf_ops.ExecuteOperation(bundle, rmnops1_5.Deploy, chain, contract.DeployInput[rmnops1_5.ConstructorArgs]{
359+
TypeAndVersion: deployment.NewTypeAndVersion(rmnops1_5.ContractType, *semver.MustParse("1.5.0")),
360+
ChainSelector: chain.Selector,
361+
Args: rmnops1_5.ConstructorArgs{
362+
RMNConfig: rmn_contract.RMNConfig{
363+
BlessWeightThreshold: 2,
364+
CurseWeightThreshold: 2,
365+
// setting dummy voters
366+
Voters: []rmn_contract.RMNVoter{
367+
{
368+
BlessWeight: 2,
369+
CurseWeight: 2,
370+
BlessVoteAddr: utils.RandomAddress(),
371+
CurseVoteAddr: utils.RandomAddress(),
372+
},
373+
},
374+
},
375+
},
376+
})
377+
require.NoError(t, err)
378+
ds := datastore.NewMemoryDataStore()
379+
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
380+
Type: datastore.ContractType(rmnops1_5.ContractType),
381+
Version: semver.MustParse("1.5.0"),
382+
ChainSelector: chain1,
383+
Address: deployRMNOp.Output.Address,
384+
}))
385+
rmnAddress = common.HexToAddress(deployRMNOp.Output.Address)
386+
rmnRemoteAddresses := make(map[uint64]common.Address)
387+
// deploy RMNRemote 1.6 on chain2 and chain 3
388+
for _, chainSel := range []uint64{chain2, chain3} {
389+
chain = env.BlockChains.EVMChains()[chainSel]
390+
deployRMNRemoteOp, err := cldf_ops.ExecuteOperation(bundle, rmnremoteops1_6.Deploy, chain, contract.DeployInput[rmnremoteops1_6.ConstructorArgs]{
391+
TypeAndVersion: deployment.NewTypeAndVersion(rmnremoteops1_6.ContractType, *semver.MustParse("1.6.0")),
392+
ChainSelector: chain.Selector,
393+
Args: rmnremoteops1_6.ConstructorArgs{
394+
LocalChainSelector: chain.Selector,
395+
LegacyRMN: utils.RandomAddress(),
396+
},
397+
})
398+
require.NoError(t, err)
399+
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
400+
Type: datastore.ContractType(rmnremoteops1_6.ContractType),
401+
Version: semver.MustParse("1.6.0"),
402+
ChainSelector: chainSel,
403+
Address: deployRMNRemoteOp.Output.Address,
404+
}))
405+
rmnRemoteAddresses[chainSel] = common.HexToAddress(deployRMNRemoteOp.Output.Address)
406+
}
407+
// deploy router and rmnProxy in all chains
408+
for _, sel := range []uint64{chain1, chain2, chain3} {
409+
evmChain := env.BlockChains.EVMChains()[sel]
410+
// mock wrapped native and rmnproxy address
411+
wNative := utils.RandomAddress()
412+
var rmnAddr common.Address
413+
if sel == chain1 {
414+
rmnAddr = rmnAddress
415+
} else {
416+
rmnAddr = rmnRemoteAddresses[sel]
417+
}
418+
deployRMNProxyOp, err := cldf_ops.ExecuteOperation(bundle, rmnproxyops.Deploy, evmChain, contract.DeployInput[rmnproxyops.ConstructorArgs]{
419+
ChainSelector: evmChain.Selector,
420+
TypeAndVersion: deployment.NewTypeAndVersion(rmnproxyops.ContractType, *semver.MustParse("1.0.0")),
421+
Args: rmnproxyops.ConstructorArgs{
422+
RMN: rmnAddr,
423+
},
424+
})
425+
require.NoError(t, err)
426+
rmnProxy := deployRMNProxyOp.Output.Address
427+
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
428+
Type: datastore.ContractType(rmnproxyops.ContractType),
429+
Version: semver.MustParse("1.0.0"),
430+
ChainSelector: sel,
431+
Address: rmnProxy,
432+
}))
433+
deployRouterOp, err := cldf_ops.ExecuteOperation(bundle, routerops1_2.Deploy, evmChain, contract.DeployInput[routerops1_2.ConstructorArgs]{
434+
ChainSelector: evmChain.Selector,
435+
TypeAndVersion: deployment.NewTypeAndVersion(routerops1_2.ContractType, *semver.MustParse("1.2.0")),
436+
Args: routerops1_2.ConstructorArgs{
437+
WrappedNative: wNative,
438+
RMNProxy: common.HexToAddress(rmnProxy),
439+
},
440+
})
441+
require.NoError(t, err)
442+
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
443+
Type: datastore.ContractType(routerops1_2.ContractType),
444+
Version: semver.MustParse("1.2.0"),
445+
ChainSelector: sel,
446+
Address: deployRouterOp.Output.Address,
447+
}))
448+
routerAddr := deployRouterOp.Output.Address
449+
// add some dummy onramps to the router so that chain is supported,
450+
// on chain1, add chain2 as supported dest chain and vice versa
451+
var onRampUpdates []routerops1_2.OnRamp
452+
var offRampAdds []routerops1_2.OffRamp
453+
for _, otherSel := range []uint64{chain1, chain2, chain3} {
454+
if sel != otherSel {
455+
onRamp := utils.RandomAddress()
456+
offRamp := utils.RandomAddress()
457+
onRampUpdates = append(onRampUpdates, routerops1_2.OnRamp{
458+
DestChainSelector: otherSel,
459+
OnRamp: onRamp,
460+
})
461+
offRampAdds = append(offRampAdds, routerops1_2.OffRamp{
462+
SourceChainSelector: otherSel,
463+
OffRamp: offRamp,
464+
})
465+
}
466+
}
467+
_, err = cldf_ops.ExecuteOperation(bundle, routerops1_2.ApplyRampUpdates, evmChain, contract.FunctionInput[routerops1_2.ApplyRampsUpdatesArgs]{
468+
Address: common.HexToAddress(routerAddr),
469+
ChainSelector: evmChain.Selector,
470+
Args: routerops1_2.ApplyRampsUpdatesArgs{
471+
OnRampUpdates: onRampUpdates,
472+
OffRampAdds: offRampAdds,
473+
},
474+
})
475+
require.NoError(t, err)
476+
}
477+
478+
// deploy mcms
479+
evmDeployer := &adapters.EVMDeployer{}
480+
dReg := deploy.GetRegistry()
481+
dReg.RegisterDeployer(chainsel.FamilyEVM, deploy.MCMSVersion, evmDeployer)
482+
cs := deploy.DeployMCMS(dReg)
483+
mcmsChainInput := make(map[uint64]deploy.MCMSDeploymentConfigPerChain)
484+
for _, sel := range []uint64{chain1, chain2, chain3} {
485+
evmChain := env.BlockChains.EVMChains()[sel]
486+
mcmsChainInput[sel] = deploy.MCMSDeploymentConfigPerChain{
487+
Canceller: testhelpers.SingleGroupMCMS(),
488+
Bypasser: testhelpers.SingleGroupMCMS(),
489+
Proposer: testhelpers.SingleGroupMCMS(),
490+
TimelockMinDelay: big.NewInt(0),
491+
Qualifier: ptr.String("test"),
492+
TimelockAdmin: evmChain.DeployerKey.From,
493+
}
494+
}
495+
output, err := cs.Apply(*env, deploy.MCMSDeploymentConfig{
496+
AdapterVersion: semver.MustParse("1.0.0"),
497+
Chains: mcmsChainInput,
498+
})
499+
require.NoError(t, err)
500+
// store addresses in ds
501+
allAddrRefs, err := output.DataStore.Addresses().Fetch()
502+
require.NoError(t, err)
503+
timelockAddrs := make(map[uint64]string)
504+
for _, addrRef := range allAddrRefs {
505+
require.NoError(t, ds.Addresses().Add(addrRef))
506+
if addrRef.Type == datastore.ContractType(deploymentutils.RBACTimelock) {
507+
timelockAddrs[addrRef.ChainSelector] = addrRef.Address
508+
}
509+
}
510+
// update env datastore
511+
env.DataStore = ds.Seal()
512+
// transfer ownership of RMN and RMNRemote to respective MCMS
513+
transferOwnershipInput := deploy.TransferOwnershipInput{
514+
ChainInputs: []deploy.TransferOwnershipPerChainInput{
515+
{
516+
ChainSelector: chain1,
517+
ContractRef: []datastore.AddressRef{
518+
{
519+
Type: datastore.ContractType(rmnops1_5.ContractType),
520+
Version: semver.MustParse("1.5.0"),
521+
},
522+
},
523+
ProposedOwner: timelockAddrs[chain1],
524+
},
525+
{
526+
ChainSelector: chain2,
527+
ContractRef: []datastore.AddressRef{
528+
{
529+
Type: datastore.ContractType(rmnremoteops1_6.ContractType),
530+
Version: semver.MustParse("1.6.0"),
531+
},
532+
},
533+
ProposedOwner: timelockAddrs[chain2],
534+
},
535+
{
536+
ChainSelector: chain3,
537+
ContractRef: []datastore.AddressRef{
538+
{
539+
Type: datastore.ContractType(rmnremoteops1_6.ContractType),
540+
Version: semver.MustParse("1.6.0"),
541+
},
542+
},
543+
ProposedOwner: timelockAddrs[chain3],
544+
},
545+
},
546+
AdapterVersion: semver.MustParse("1.0.0"),
547+
MCMS: mcms.Input{
548+
OverridePreviousRoot: false,
549+
ValidUntil: 3759765795,
550+
TimelockDelay: mcms_types.MustParseDuration("0s"),
551+
TimelockAction: mcms_types.TimelockActionSchedule,
552+
MCMSAddressRef: datastore.AddressRef{
553+
Type: datastore.ContractType(deploymentutils.ProposerManyChainMultisig),
554+
Qualifier: "test",
555+
Version: semver.MustParse("1.0.0"),
556+
},
557+
TimelockAddressRef: datastore.AddressRef{
558+
Type: datastore.ContractType(deploymentutils.RBACTimelock),
559+
Qualifier: "test",
560+
Version: semver.MustParse("1.0.0"),
561+
},
562+
Description: "Transfer ownership to timelock for fast curse test",
563+
},
564+
}
565+
566+
// register chain adapter
567+
cr := deploy.GetTransferOwnershipRegistry()
568+
evmAdapter := &adapters.EVMTransferOwnershipAdapter{}
569+
cr.RegisterAdapter(chainsel.FamilyEVM, transferOwnershipInput.AdapterVersion, evmAdapter)
570+
mcmsRegistry := changesets.GetRegistry()
571+
evmMCMSReader := &adapters.EVMMCMSReader{}
572+
mcmsRegistry.RegisterMCMSReader(chainsel.FamilyEVM, evmMCMSReader)
573+
transferOwnershipChangeset := deploy.TransferOwnershipChangeset(cr, mcmsRegistry)
574+
output, err = transferOwnershipChangeset.Apply(*env, transferOwnershipInput)
575+
require.NoError(t, err)
576+
require.Greater(t, len(output.Reports), 0)
577+
require.Equal(t, 1, len(output.MCMSTimelockProposals))
578+
testhelpers.ProcessTimelockProposals(t, *env, output.MCMSTimelockProposals, false)
579+
t.Logf("Transferred ownership of RMN and RMNRemote to respective MCMS")
580+
// now generate a global curse on chain 3
581+
curseCfg := fastcurse.GlobalCurseOnNetworkInput{
582+
ChainSelectors: map[uint64]*semver.Version{
583+
chain3: semver.MustParse("1.6.0"),
584+
},
585+
MCMS: mcms.Input{
586+
OverridePreviousRoot: false,
587+
ValidUntil: 3759765795,
588+
TimelockDelay: mcms_types.MustParseDuration("0s"),
589+
TimelockAction: mcms_types.TimelockActionSchedule,
590+
MCMSAddressRef: datastore.AddressRef{
591+
Type: datastore.ContractType(deploymentutils.ProposerManyChainMultisig),
592+
Qualifier: "test",
593+
Version: semver.MustParse("1.0.0"),
594+
},
595+
TimelockAddressRef: datastore.AddressRef{
596+
Type: datastore.ContractType(deploymentutils.RBACTimelock),
597+
Qualifier: "test",
598+
Version: semver.MustParse("1.0.0"),
599+
},
600+
Description: "Curse proposal for fast curse test",
601+
},
602+
}
603+
curseReg := fastcurse.GetCurseRegistry()
604+
adv1_6_0 := adaptersv1_6_0.NewCurseAdapter()
605+
adv1_5_0 := adaptersv1_5_0.NewCurseAdapter()
606+
crInput1_6_0 := fastcurse.CurseRegistryInput{
607+
CursingFamily: chainsel.FamilyEVM,
608+
CursingVersion: semver.MustParse("1.6.0"),
609+
SubjectFamily: chainsel.FamilyEVM,
610+
CurseAdapter: adaptersv1_6_0.NewCurseAdapter(),
611+
CurseSubjectAdapter: adaptersv1_6_0.NewCurseAdapter(),
612+
}
613+
crInput1_5_0 := fastcurse.CurseRegistryInput{
614+
CursingFamily: chainsel.FamilyEVM,
615+
CursingVersion: semver.MustParse("1.5.0"),
616+
SubjectFamily: chainsel.FamilyEVM,
617+
CurseAdapter: adaptersv1_5_0.NewCurseAdapter(),
618+
CurseSubjectAdapter: adaptersv1_5_0.NewCurseAdapter(),
619+
}
620+
curseReg.RegisterNewCurse(crInput1_6_0)
621+
curseReg.RegisterNewCurse(crInput1_5_0)
622+
curseChangeset := fastcurse.GloballyCurseChainChangeset(curseReg, mcmsRegistry)
623+
output, err = curseChangeset.Apply(*env, curseCfg)
624+
require.NoError(t, err)
625+
require.Greater(t, len(output.Reports), 0)
626+
require.Equal(t, 1, len(output.MCMSTimelockProposals))
627+
testhelpers.ProcessTimelockProposals(t, *env, output.MCMSTimelockProposals, false)
628+
629+
// check that the subjects were actually cursed
630+
evmChain1 := env.BlockChains.EVMChains()[chain1]
631+
evmChain2 := env.BlockChains.EVMChains()[chain2]
632+
evmChain3 := env.BlockChains.EVMChains()[chain3]
633+
rmnC, err := rmn_contract.NewRMNContract(rmnAddress, evmChain1.Client)
634+
require.NoError(t, err)
635+
// chain2 subject should not be cursed
636+
isCursed, err := rmnC.IsCursed(nil, adv1_5_0.SelectorToSubject(chain2))
637+
require.NoError(t, err)
638+
require.False(t, isCursed, "subject on chain2 should be cursed on rmn in chain1")
639+
// chain3 subject should be cursed
640+
isCursed, err = rmnC.IsCursed(nil, adv1_5_0.SelectorToSubject(chain3))
641+
require.NoError(t, err)
642+
require.True(t, isCursed, "subject on chain3 should be cursed on rmn in chain1")
643+
644+
rmnRemoteC, err := rmn_remote.NewRMNRemote(rmnRemoteAddresses[chain2], evmChain2.Client)
645+
require.NoError(t, err)
646+
// chain1 subject should not be cursed on chain2
647+
isCursed, err = rmnRemoteC.IsCursed(nil, adv1_6_0.SelectorToSubject(chain1))
648+
require.NoError(t, err)
649+
require.False(t, isCursed, "subject on chain1 should be cursed on rmnremote in chain2")
650+
// chain3 subject should be cursed on chain2
651+
isCursed, err = rmnRemoteC.IsCursed(nil, adv1_6_0.SelectorToSubject(chain3))
652+
require.NoError(t, err)
653+
require.True(t, isCursed, "subject on chain3 should be cursed on rmnremote in chain2")
654+
655+
// chain3 should have a global curse on itself
656+
rmnRemoteC3, err := rmn_remote.NewRMNRemote(rmnRemoteAddresses[chain3], evmChain3.Client)
657+
require.NoError(t, err)
658+
isCursed, err = rmnRemoteC3.IsCursed(nil, fastcurse.GlobalCurseSubject())
659+
require.NoError(t, err)
660+
require.True(t, isCursed, "subject on chain3 should be cursed on rmnremote in chain3")
661+
662+
// Now uncurse the subjects
663+
// reset the operation bundle to clear any cached values
664+
env.OperationsBundle = cldf_ops.NewBundle(env.GetContext, env.Logger, cldf_ops.NewMemoryReporter())
665+
uncurseChangeset := fastcurse.GloballyUncurseChainChangeset(curseReg, mcmsRegistry)
666+
output, err = uncurseChangeset.Apply(*env, curseCfg)
667+
require.NoError(t, err)
668+
require.Greater(t, len(output.Reports), 0)
669+
require.Equal(t, 1, len(output.MCMSTimelockProposals))
670+
testhelpers.ProcessTimelockProposals(t, *env, output.MCMSTimelockProposals, false)
671+
672+
// check that the subjects were actually uncursed
673+
isCursed, err = rmnC.IsCursed(nil, adv1_5_0.SelectorToSubject(chain2))
674+
require.NoError(t, err)
675+
require.False(t, isCursed, "subject on chain2 should be uncursed on rmn in chain1")
676+
677+
isCursed, err = rmnC.IsCursed(nil, adv1_5_0.SelectorToSubject(chain3))
678+
require.NoError(t, err)
679+
require.False(t, isCursed, "subject on chain3 should be uncursed on rmn in chain1")
680+
681+
isCursed, err = rmnRemoteC.IsCursed(nil, adv1_6_0.SelectorToSubject(chain1))
682+
require.NoError(t, err)
683+
require.False(t, isCursed, "subject on chain1 should be uncursed on rmnremote in chain2")
684+
isCursed, err = rmnRemoteC.IsCursed(nil, adv1_6_0.SelectorToSubject(chain3))
685+
require.NoError(t, err)
686+
require.False(t, isCursed, "subject on chain3 should be uncursed on rmnremote in chain2")
687+
688+
isCursed, err = rmnRemoteC3.IsCursed(nil, fastcurse.GlobalCurseSubject())
689+
require.NoError(t, err)
690+
require.False(t, isCursed, "global curse on chain3 should be uncursed on rmnremote in chain3")
691+
}

0 commit comments

Comments
 (0)