@@ -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