@@ -2,6 +2,7 @@ package solana
22
33import (
44 "context"
5+ "crypto/ecdsa"
56 "fmt"
67 "testing"
78 "time"
@@ -21,16 +22,60 @@ import (
2122 "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller"
2223 cpistubbindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/external_program_cpi_stub"
2324 mcmbindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm"
25+ rmnremotebindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/rmn_remote"
2426 timelockbindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock"
2527 "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/accesscontroller"
2628 "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common"
2729 timelockutils "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/timelock"
30+ "github.com/smartcontractkit/mcms"
31+ mcmssdk "github.com/smartcontractkit/mcms/sdk"
2832 solanasdk "github.com/smartcontractkit/mcms/sdk/solana"
2933 mcmstypes "github.com/smartcontractkit/mcms/types"
3034
3135 "github.com/smartcontractkit/timelock-worker/pkg/timelock"
36+ evmtests "github.com/smartcontractkit/timelock-worker/tests/integration/evm"
3237)
3338
39+ var solChainSelector = mcmstypes .ChainSelector (chainsel .SOLANA_DEVNET .Selector )
40+
41+ func (s * solanaIntegrationTestSuite ) scheduleProposal (
42+ proposal * mcms.TimelockProposal , signerKey * ecdsa.PrivateKey , auth solana.PrivateKey ,
43+ ) {
44+ converters := map [mcmstypes.ChainSelector ]mcmssdk.TimelockConverter {solChainSelector : solanasdk.TimelockConverter {}}
45+ mcmProposal , _ , err := proposal .Convert (s .Ctx , converters )
46+ s .Require ().NoError (err )
47+
48+ inspectors := map [mcmstypes.ChainSelector ]mcmssdk.Inspector {solChainSelector : solanasdk .NewInspector (s .solanaClient )}
49+ encoders , err := mcmProposal .GetEncoders ()
50+ s .Require ().NoError (err )
51+ encoder := encoders [solChainSelector ]
52+ executor := solanasdk .NewExecutor (encoder .(* solanasdk.Encoder ), s .solanaClient , auth )
53+ executors := map [mcmstypes.ChainSelector ]mcmssdk.Executor {solChainSelector : executor }
54+
55+ signable , err := mcms .NewSignable (& mcmProposal , inspectors )
56+ s .Require ().NoError (err )
57+ s .Require ().NotNil (signable )
58+ _ , err = signable .SignAndAppend (mcms .NewPrivateKeySigner (signerKey ))
59+ s .Require ().NoError (err )
60+
61+ executable , err := mcms .NewExecutable (& mcmProposal , executors )
62+ s .Require ().NoError (err )
63+
64+ signature , err := executable .SetRoot (s .Ctx , solChainSelector )
65+ s .Require ().NoError (err )
66+ _ , err = solana .SignatureFromBase58 (signature .Hash )
67+ s .Require ().NoError (err )
68+ s .Logf ("mcm.SetRoot()" )
69+
70+ for i := range mcmProposal .Operations {
71+ s .Logf ("mcm.Execute(%d)" , i )
72+ signature , err = executable .Execute (s .Ctx , i )
73+ s .Require ().NoError (err )
74+ _ , err = solana .SignatureFromBase58 (signature .Hash )
75+ s .Require ().NoError (err )
76+ }
77+ }
78+
3479// getBatchAddAccessIxs returns a slice of instructions to batch add access for multiple addresses to a specific role in the Solana timelock instance.
3580func (s * solanaIntegrationTestSuite ) getBatchAddAccessIxs (
3681 ctx context.Context , timelockID [32 ]byte , roleAcAccount solana.PublicKey , role timelockbindings.Role ,
@@ -70,14 +115,13 @@ func (s *solanaIntegrationTestSuite) getBatchAddAccessIxs(
70115 return ixs
71116}
72117
73- // AssignRoleToAccounts assigns the specified role to the provided accounts in the Solana timelock instance.
74- func (s * solanaIntegrationTestSuite ) AssignRoleToAccounts (
75- ctx context.Context , pdaSeed solanasdk.PDASeed , auth solana.PrivateKey ,
76- accounts []solana.PublicKey , role timelockbindings.Role ,
118+ // assignRoleToAccounts assigns the specified role to the provided accounts in the Solana timelock instance.
119+ func (s * solanaIntegrationTestSuite ) assignRoleToAccounts (
120+ pdaSeed solanasdk.PDASeed , accounts []solana.PublicKey , role timelockbindings.Role ,
77121) {
78- instructions := s .getBatchAddAccessIxs (ctx , pdaSeed , s .RoleMap [role ].AccessController .PublicKey (),
79- role , accounts , auth , 1 )
80- testutils .SendAndConfirm (ctx , s .T (), s .solanaClient , instructions , auth , rpc .CommitmentConfirmed )
122+ instructions := s .getBatchAddAccessIxs (s . Ctx , pdaSeed , s .RoleMap [role ].AccessController .PublicKey (),
123+ role , accounts , s . TestPrivateKey , 1 )
124+ testutils .SendAndConfirm (s . Ctx , s .T (), s .solanaClient , instructions , s . TestPrivateKey , rpc .CommitmentConfirmed )
81125}
82126
83127func (s * solanaIntegrationTestSuite ) initializeAccessController (auth solana.PrivateKey ) {
@@ -108,17 +152,6 @@ func (s *solanaIntegrationTestSuite) initializeTimelock(pdaSeed solanasdk.PDASee
108152 s .initializeAccessController (admin )
109153
110154 s .Run ("init timelock" , func () {
111- data , accErr := s .solanaClient .GetAccountInfoWithOpts (s .Ctx , s .TimelockProgramID , & rpc.GetAccountInfoOpts {
112- Commitment : rpc .CommitmentConfirmed ,
113- })
114- s .Require ().NoError (accErr )
115-
116- var programData struct {
117- DataType uint32
118- Address solana.PublicKey
119- }
120- s .Require ().NoError (bin .UnmarshalBorsh (& programData , data .Bytes ()))
121-
122155 configPDA , err2 := solanasdk .FindTimelockConfigPDA (s .TimelockProgramID , pdaSeed )
123156 s .Require ().NoError (err2 )
124157
@@ -129,7 +162,7 @@ func (s *solanaIntegrationTestSuite) initializeTimelock(pdaSeed solanasdk.PDASee
129162 admin .PublicKey (),
130163 solana .SystemProgramID ,
131164 s .TimelockProgramID ,
132- programData . Address ,
165+ s . getProgramDataAddress ( s . TimelockProgramID ) ,
133166 s .AccessControllerProgramID ,
134167 s .ProposerAccessController ,
135168 s .ExecutorAccessController ,
@@ -159,7 +192,7 @@ func (s *solanaIntegrationTestSuite) initializeTimelock(pdaSeed solanasdk.PDASee
159192 accounts = append (accounts , account .PublicKey ())
160193 }
161194
162- s .AssignRoleToAccounts ( s . Ctx , pdaSeed , admin , accounts , roleAccounts .Role )
195+ s .assignRoleToAccounts ( pdaSeed , accounts , roleAccounts .Role )
163196
164197 for _ , account := range roleAccounts .Accounts {
165198 found , err := accesscontroller .HasAccess (s .Ctx , s .solanaClient , roleAccounts .AccessController .PublicKey (),
@@ -185,18 +218,8 @@ func (s *solanaIntegrationTestSuite) initializeMcm(pdaSeed [32]byte, signer eth.
185218 s .Require ().NoError (err )
186219 chainSelector := chainsel .SOLANA_DEVNET .Selector
187220
188- var programData struct {
189- DataType uint32
190- Address solana.PublicKey
191- }
192- data , err := s .solanaClient .GetAccountInfoWithOpts (s .Ctx , s .McmProgramID ,
193- & rpc.GetAccountInfoOpts {Commitment : rpc .CommitmentConfirmed })
194- s .Require ().NoError (err )
195- err = bin .UnmarshalBorsh (& programData , data .Bytes ())
196- s .Require ().NoError (err )
197-
198221 ix , err := mcmbindings .NewInitializeInstruction (chainSelector , pdaSeed , configPDA , auth .PublicKey (),
199- solana .SystemProgramID , s .McmProgramID , programData . Address , rootMetadataPDA ,
222+ solana .SystemProgramID , s .McmProgramID , s . getProgramDataAddress ( s . McmProgramID ) , rootMetadataPDA ,
200223 expiringRootAndOpCountPDA ).ValidateAndBuild ()
201224 s .Require ().NoError (err )
202225
@@ -238,7 +261,7 @@ func (s *solanaIntegrationTestSuite) getInitAccessControllersIxs(ctx context.Con
238261 return ixs
239262}
240263
241- func (s * solanaIntegrationTestSuite ) setupCPIStub ( ctx context. Context ) {
264+ func (s * solanaIntegrationTestSuite ) initializeCPIStub ( ) {
242265 cpistubbindings .SetProgramID (s .StubProgramID )
243266
244267 admin , err := solana .PrivateKeyFromBase58 (privateKey )
@@ -251,8 +274,82 @@ func (s *solanaIntegrationTestSuite) setupCPIStub(ctx context.Context) {
251274 ValidateAndBuild ()
252275 s .Require ().NoError (err )
253276
254- _ , err = sendAndConfirm (ctx , s .T (), s .solanaClient , admin , []solana.Instruction {instruction })
277+ _ , err = sendAndConfirm (s .Ctx , s .T (), s .solanaClient , admin , []solana.Instruction {instruction })
278+ s .Require ().NoError (err )
279+ }
280+
281+ func (s * solanaIntegrationTestSuite ) initializeRmnRemote () {
282+ rmnremotebindings .SetProgramID (s .RmnRemoteProgramID )
283+
284+ auth , err := solana .PrivateKeyFromBase58 (privateKey )
285+ s .Require ().NoError (err )
286+ configPDA , _ , err := solana .FindProgramAddress ([][]byte {[]byte ("config" )}, s .RmnRemoteProgramID )
287+ s .Require ().NoError (err )
288+ cursesPDA , _ , err := solana .FindProgramAddress ([][]byte {[]byte ("curses" )}, s .RmnRemoteProgramID )
289+ s .Require ().NoError (err )
290+ // s.Logf("ACCOUNTS:\n%v\n%v\n%v\n%v\n%v\n%v\n", configPDA, cursesPDA, auth.PublicKey(),
291+ // solana.SystemProgramID, s.RmnRemoteProgramID, s.getProgramDataAddress(s.RmnRemoteProgramID))
292+
293+ instruction , err := rmnremotebindings .NewInitializeInstruction (configPDA , cursesPDA , auth .PublicKey (),
294+ solana .SystemProgramID , s .RmnRemoteProgramID , s .getProgramDataAddress (s .RmnRemoteProgramID )).ValidateAndBuild ()
295+ s .Require ().NoError (err )
296+
297+ _ , err = sendAndConfirm (s .Ctx , s .T (), s .solanaClient , auth , []solana.Instruction {instruction })
298+ s .Require ().NoError (err )
299+ }
300+
301+ func (s * solanaIntegrationTestSuite ) transferOwnershipRmnRemote (seed solanasdk.PDASeed , signer evmtests.TestAccount ) {
302+ configPDA , _ , err := solana .FindProgramAddress ([][]byte {[]byte ("config" )}, s .RmnRemoteProgramID )
303+ s .Require ().NoError (err )
304+ cursesPDA , _ , err := solana .FindProgramAddress ([][]byte {[]byte ("curses" )}, s .RmnRemoteProgramID )
305+ s .Require ().NoError (err )
306+ timelockSignerPDA , err := solanasdk .FindTimelockSignerPDA (s .TimelockProgramID , seed )
307+ s .Require ().NoError (err )
308+
309+ transferInstruction , err := rmnremotebindings .NewTransferOwnershipInstruction (timelockSignerPDA , configPDA ,
310+ cursesPDA , s .TestPrivateKey .PublicKey ()).ValidateAndBuild ()
311+ s .Require ().NoError (err )
312+
313+ acceptInstruction , err := rmnremotebindings .NewAcceptOwnershipInstruction (configPDA ,
314+ timelockSignerPDA ).ValidateAndBuild ()
315+ s .Require ().NoError (err )
316+
317+ s .transferOwnership (transferInstruction , acceptInstruction , seed , signer )
318+ s .Logf ("transferred ownership of RMNRemote to timelock" )
319+ }
320+
321+ func (s * solanaIntegrationTestSuite ) transferOwnership (
322+ transferInstruction , acceptInstruction solana.Instruction , seed solanasdk.PDASeed , signer evmtests.TestAccount ,
323+ ) {
324+ // --- transfer ---
325+ _ , err := sendAndConfirm (s .Ctx , s .T (), s .solanaClient , s .TestPrivateKey , []solana.Instruction {transferInstruction })
326+ s .Require ().NoError (err )
327+
328+ // --- accept ---
329+ acceptOwnershipTransaction , err := solanasdk .NewTransactionFromInstruction (acceptInstruction , "" , nil )
255330 s .Require ().NoError (err )
331+
332+ chainMetadata , err := solanasdk .NewChainMetadata (0 , s .McmProgramID , seed , s .ProposerAccessController ,
333+ s .CancellerAccessController , s .BypasserAccessController )
334+ s .Require ().NoError (err )
335+ timelockAddress := solanasdk .ContractAddress (s .TimelockProgramID , seed )
336+
337+ proposal , err := mcms .NewTimelockProposalBuilder ().
338+ SetValidUntil (uint32 (2051222400 )). // 2035-01-01T12:00:00 UTC
339+ SetDescription ("proposal to accept ownership" ).
340+ SetOverridePreviousRoot (true ).
341+ SetVersion ("v1" ).
342+ SetAction (mcmstypes .TimelockActionBypass ).
343+ SetChainMetadata (map [mcmstypes.ChainSelector ]mcmstypes.ChainMetadata {solChainSelector : chainMetadata }).
344+ AddTimelockAddress (solChainSelector , timelockAddress ).
345+ AddOperation (mcmstypes.BatchOperation {
346+ ChainSelector : solChainSelector ,
347+ Transactions : []mcmstypes.Transaction {acceptOwnershipTransaction },
348+ }).
349+ Build ()
350+ s .Require ().NoError (err )
351+
352+ s .scheduleProposal (proposal , signer .PrivateKey , s .TestPrivateKey )
256353}
257354
258355// runTimelockWorkerSolana runs the Solana timelock worker with the provided parameters.
@@ -410,6 +507,21 @@ func (s *solanaIntegrationTestSuite) scheduleTestIx(
410507 return ixStub , operationID
411508}
412509
510+ func (s * solanaIntegrationTestSuite ) getProgramDataAddress (programID solana.PublicKey ) solana.PublicKey {
511+ opts := & rpc.GetAccountInfoOpts {Commitment : rpc .CommitmentConfirmed }
512+ data , accErr := s .solanaClient .GetAccountInfoWithOpts (s .Ctx , programID , opts )
513+ s .Require ().NoError (accErr )
514+
515+ var programData struct {
516+ DataType uint32
517+ Address solana.PublicKey
518+ }
519+ err := bin .UnmarshalBorsh (& programData , data .Bytes ())
520+ s .Require ().NoError (err )
521+
522+ return programData .Address
523+ }
524+
413525func sendAndConfirm (
414526 ctx context.Context , t * testing.T , client * rpc.Client , txPayer solana.PrivateKey , instructions []solana.Instruction ,
415527) (solana.Signature , error ) {
0 commit comments