diff --git a/.changeset/some-lamps-agree.md b/.changeset/some-lamps-agree.md new file mode 100644 index 00000000000..a371f5995b5 --- /dev/null +++ b/.changeset/some-lamps-agree.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal changeset support for Solana CCIP attestation signer registry contract diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 909620d84fd..5ff597e65bd 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -248,6 +248,7 @@ require ( github.com/fvbommel/sortorder v1.1.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gagliardetto/anchor-go v0.3.2 // indirect github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/metaplex-go v0.2.1 // indirect github.com/gagliardetto/solana-go v1.13.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 350e39daeca..82abbdacde7 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -473,6 +473,7 @@ github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8 github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -606,7 +607,10 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gagliardetto/anchor-go v0.3.2 h1:/nlAp3B6s4DBlNABWrQ5bA2zpsUGVDqBfwmbUUg4ChQ= +github.com/gagliardetto/anchor-go v0.3.2/go.mod h1:Jf9/DBNo6GsG6RguE4ZuJz+PZtypKg3iUpB++Oc6ynQ= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/binary v0.7.6/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -615,6 +619,7 @@ github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1: github.com/gagliardetto/metaplex-go v0.2.1 h1:NMBsgJe3I2avKZ39dfYQvXsGsr2BxUgARkA9LZ6szBg= github.com/gagliardetto/metaplex-go v0.2.1/go.mod h1:6ZLYBvlWcXktXQ/QcBJYRzKgK7Q3WgiGD7BjE7Zxpw4= github.com/gagliardetto/solana-go v1.4.0/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/solana-go v1.5.0/go.mod h1:1KFOW7mlR/TSjYFeLCYmfpSptRdNJMtpgChelKy2oU0= github.com/gagliardetto/solana-go v1.13.0 h1:uNzhjwdAdbq9xMaX2DF0MwXNMw6f8zdZ7JPBtkJG7Ig= github.com/gagliardetto/solana-go v1.13.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -1721,6 +1726,7 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA= +github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/deployment/ccip/changeset/ccip-attestation-solana/cs_deploy_signer_registry_solana.go b/deployment/ccip/changeset/ccip-attestation-solana/cs_deploy_signer_registry_solana.go new file mode 100644 index 00000000000..68c4f10bf43 --- /dev/null +++ b/deployment/ccip/changeset/ccip-attestation-solana/cs_deploy_signer_registry_solana.go @@ -0,0 +1,279 @@ +package ccip_attestation + +import ( + "archive/zip" + "bytes" + "context" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/gagliardetto/solana-go" + chainsel "github.com/smartcontractkit/chain-selectors" + + signer_registry "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry_solana" + + cldf_solana "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" + + sol_binary "github.com/gagliardetto/binary" + sol_rpc "github.com/gagliardetto/solana-go/rpc" + + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/shared" +) + +// use this changeset to deploy the base signer registry contract +var _ cldf.ChangeSet[DeployBaseSignerRegistryContractConfig] = DeployBaseSignerRegistryContractChangeset + +// use this changeset to initialize the base signer registry contract and set an initial owner +var _ cldf.ChangeSet[InitalizeBaseSignerRegistryContractConfig] = InitializeBaseSignerRegistryContractChangeset + +type DeployBaseSignerRegistryContractConfig struct { + ChainSelector uint64 + Version semver.Version + WorkflowRun string + ArtifactID string + IsUpgrade bool +} + +type InitalizeBaseSignerRegistryContractConfig struct { + ChainSelector uint64 +} + +func DeployBaseSignerRegistryContractChangeset(e cldf.Environment, c DeployBaseSignerRegistryContractConfig) (cldf.ChangesetOutput, error) { + e.Logger.Infow("Deploying base signer registry", "chain_selector", c.ChainSelector) + err := c.Validate(e) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy base signer registry contract: %w", err) + } + chainSel := c.ChainSelector + chain := e.BlockChains.SolanaChains()[chainSel] + + newAddresses := cldf.NewMemoryAddressBook() + + programFileName := deployment.BaseSignerRegistryProgramName + ".so" + programFilePath := filepath.Join(chain.ProgramsPath, programFileName) + if _, err := os.Stat(programFilePath); err != nil { + if !os.IsNotExist(err) { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to check existing program artifact: %w", err) + } + if strings.TrimSpace(c.WorkflowRun) == "" || strings.TrimSpace(c.ArtifactID) == "" { + return cldf.ChangesetOutput{}, fmt.Errorf("program artifact %s not found in %s and workflow run/artifact ID not provided", programFileName, chain.ProgramsPath) + } + if err := DownloadReleaseArtifactsFromGithubWorkflowRun(context.Background(), c.WorkflowRun, c.ArtifactID, chain.ProgramsPath); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to download release artifacts: %w", err) + } + } + _, err = deployBaseSignerRegistryContract(e, chain, newAddresses, c) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy base signer registry contract: %w", err) + } + + return cldf.ChangesetOutput{ + AddressBook: newAddresses, + }, nil +} + +func InitializeBaseSignerRegistryContractChangeset(e cldf.Environment, c InitalizeBaseSignerRegistryContractConfig) (cldf.ChangesetOutput, error) { + e.Logger.Infow("Initializing base signer registry", "chain_selector", c.ChainSelector) + err := c.Validate(e) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to initialize base signer registry contract: %w", err) + } + chainSel := c.ChainSelector + chain := e.BlockChains.SolanaChains()[chainSel] + authority := chain.DeployerKey.PublicKey() + + configPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("config")}, signer_registry.ProgramID) + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + eventAuthorityPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, signer_registry.ProgramID) + programData, err := getSolProgramData(e, chain, signer_registry.ProgramID) + if err != nil { + return cldf.ChangesetOutput{}, err + } + + ix, err := signer_registry.NewInitializeInstruction( + authority, + solana.SystemProgramID, + configPda, + signersPda, + signer_registry.ProgramID, + programData.Address, + eventAuthorityPda, + signer_registry.ProgramID, + ) + + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to initialize base signer registry contract: %w", err) + } + + if err := chain.Confirm([]solana.Instruction{ix}); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to initialize base signer registry contract: %w", err) + } + + return cldf.ChangesetOutput{}, nil +} + +func deployBaseSignerRegistryContract(e cldf.Environment, chain cldf_solana.Chain, ab cldf.AddressBook, config DeployBaseSignerRegistryContractConfig, +) (solana.PublicKey, error) { + contractType := shared.SVMSignerRegistry + programName := deployment.BaseSignerRegistryProgramName + + programID, err := chain.DeployProgram(e.Logger, cldf_solana.ProgramInfo{ + Name: programName, + Bytes: deployment.SolanaProgramBytes[programName], + }, config.IsUpgrade, true) + + if err != nil { + return solana.PublicKey{}, fmt.Errorf("failed to deploy program: %w", err) + } + address := solana.MustPublicKeyFromBase58(programID) + + e.Logger.Infow("Deployed program", "Program", contractType, "addr", programID, "chain", chain.String()) + tv := cldf.NewTypeAndVersion(contractType, config.Version) + err = ab.Save(chain.Selector, programID, tv) + if err != nil { + return solana.PublicKey{}, fmt.Errorf("failed to save address: %w", err) + } + + return address, nil +} + +func (c DeployBaseSignerRegistryContractConfig) Validate(e cldf.Environment) error { + if err := cldf.IsValidChainSelector(c.ChainSelector); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", c.ChainSelector, err) + } + family, _ := chainsel.GetSelectorFamily(c.ChainSelector) + if family != chainsel.FamilySolana { + return fmt.Errorf("chain %d is not a solana chain", c.ChainSelector) + } + + return nil +} + +func (c InitalizeBaseSignerRegistryContractConfig) Validate(e cldf.Environment) error { + if err := cldf.IsValidChainSelector(c.ChainSelector); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", c.ChainSelector, err) + } + family, _ := chainsel.GetSelectorFamily(c.ChainSelector) + if family != chainsel.FamilySolana { + return fmt.Errorf("chain %d is not a solana chain", c.ChainSelector) + } + + return nil +} + +func DownloadReleaseArtifactsFromGithubWorkflowRun( + ctx context.Context, + run string, + artifactID string, + targetPath string, +) error { + url := fmt.Sprintf( + "https://github.com/smartcontractkit/ccip-base/actions/runs/%s/artifacts/%s", + run, + artifactID, + ) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to download release asset: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("failed to download release asset: HTTP %d", resp.StatusCode) + } + + // Read the entire zip file into memory + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %w", err) + } + + zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) + if err != nil { + return fmt.Errorf("failed to create zip reader: %w", err) + } + + // Extract each file from the zip archive + for _, file := range zipReader.File { + // Clean the file path to prevent directory traversal + cleanedName := filepath.Clean(file.Name) + // Ensure the file path doesn't escape the target directory + if strings.Contains(cleanedName, "..") { + return fmt.Errorf("invalid file path in archive: %s", file.Name) + } + filePath := filepath.Join(targetPath, cleanedName) + + if file.FileInfo().IsDir() { + if err := os.MkdirAll(filePath, file.Mode()); err != nil { + return fmt.Errorf("failed to create directory %s: %w", filePath, err) + } + continue + } + + if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil { + return fmt.Errorf("failed to create parent directory for %s: %w", filePath, err) + } + + fileReader, err := file.Open() + if err != nil { + return fmt.Errorf("failed to open file in zip %s: %w", file.Name, err) + } + + targetFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, file.Mode()) + if err != nil { + fileReader.Close() + return fmt.Errorf("failed to create file %s: %w", filePath, err) + } + + // Limit the amount of data to copy to prevent decompression bombs + const maxFileSize = 100 * 1024 * 1024 // 100MB limit per file + limitedReader := io.LimitReader(fileReader, maxFileSize) + n, err := io.Copy(targetFile, limitedReader) + fileReader.Close() + targetFile.Close() + if err != nil { + return fmt.Errorf("failed to write file %s: %w", filePath, err) + } + if n == maxFileSize { + return fmt.Errorf("file %s exceeds maximum allowed size of %d bytes", filePath, maxFileSize) + } + } + + return nil +} + +func getSolProgramData(e cldf.Environment, chain cldf_solana.Chain, programID solana.PublicKey) (struct { + DataType uint32 + Address solana.PublicKey +}, error) { + var programData struct { + DataType uint32 + Address solana.PublicKey + } + data, err := chain.Client.GetAccountInfoWithOpts(e.GetContext(), programID, &sol_rpc.GetAccountInfoOpts{ + Commitment: sol_rpc.CommitmentConfirmed, + }) + if err != nil { + return programData, fmt.Errorf("failed to deploy program: %w", err) + } + + err = sol_binary.UnmarshalBorsh(&programData, data.Bytes()) + if err != nil { + return programData, fmt.Errorf("failed to unmarshal program data: %w", err) + } + return programData, nil +} diff --git a/deployment/ccip/changeset/ccip-attestation-solana/cs_idl.go b/deployment/ccip/changeset/ccip-attestation-solana/cs_idl.go new file mode 100644 index 00000000000..372d1197244 --- /dev/null +++ b/deployment/ccip/changeset/ccip-attestation-solana/cs_idl.go @@ -0,0 +1,129 @@ +package ccip_attestation + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + chainsel "github.com/smartcontractkit/chain-selectors" + + signer_registry "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry_solana" + + cldf_solana "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" + + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + + "github.com/smartcontractkit/chainlink/deployment" + cs_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana_v0_1_1" +) + +// use this changeset to upload the IDL for the attestation program +var _ cldf.ChangeSet[BaseIDLConfig] = BaseUploadIDLChangeset + +// use this changeset to set the authority for the IDL of the attestation program (timelock) +var _ cldf.ChangeSet[BaseIDLConfig] = BaseSetAuthorityIDLChangeset + +const IdlIxTag uint64 = 0x0a69e9a778bcf440 + +type BaseIDLConfig struct { + ChainSelector uint64 + WorkflowRun string + ArtifactID string +} + +// resolve artifacts based on workflow run and write anchor.toml file to simulate anchor workspace +func repoSetup(e cldf.Environment, chain cldf_solana.Chain, run string, artifactID string) error { + programName := deployment.BaseSignerRegistryProgramName + idlFileName := programName + ".json" + idlFilePath := filepath.Join(chain.ProgramsPath, idlFileName) + if _, err := os.Stat(idlFilePath); err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("error checking existing IDL artifact: %w", err) + } + if strings.TrimSpace(run) == "" || strings.TrimSpace(artifactID) == "" { + return fmt.Errorf("IDL artifact %s not found in %s and workflow run/artifact ID not provided", idlFileName, chain.ProgramsPath) + } + e.Logger.Debug("Downloading artifacts from workflow run...") + if err := DownloadReleaseArtifactsFromGithubWorkflowRun(context.Background(), run, artifactID, chain.ProgramsPath); err != nil { + return fmt.Errorf("error downloading program artifacts: %w", err) + } + } + + // get anchor version + output, err := cs_solana.RunCommand("anchor", []string{"--version"}, ".") + if err != nil { + return errors.New("anchor-cli not installed in path") + } + e.Logger.Debugw("Anchor version command output", "output", output) + anchorVersion, err := cs_solana.ParseAnchorVersion(output) + if err != nil { + return fmt.Errorf("error parsing anchor version: %w", err) + } + // create Anchor.toml + // this creates anchor workspace with cluster and wallet configured + if err := cs_solana.WriteAnchorToml(e, filepath.Join(chain.ProgramsPath, "Anchor.toml"), anchorVersion, chain.URL, chain.KeypairPath); err != nil { + return fmt.Errorf("error writing Anchor.toml: %w", err) + } + + return nil +} + +func (c BaseIDLConfig) Validate(e cldf.Environment) error { + if err := cldf.IsValidChainSelector(c.ChainSelector); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", c.ChainSelector, err) + } + family, _ := chainsel.GetSelectorFamily(c.ChainSelector) + if family != chainsel.FamilySolana { + return fmt.Errorf("chain %d is not a solana chain", c.ChainSelector) + } + return nil +} + +func BaseUploadIDLChangeset(e cldf.Environment, c BaseIDLConfig) (cldf.ChangesetOutput, error) { + if err := c.Validate(e); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("error validating idl config: %w", err) + } + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + if err := repoSetup(e, chain, c.WorkflowRun, c.ArtifactID); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("error setting up repo: %w", err) + } + + idlFile := filepath.Join(chain.ProgramsPath, deployment.BaseSignerRegistryProgramName+".json") + if _, err := os.Stat(idlFile); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("idl file not found: %w", err) + } + + e.Logger.Infow("Uploading IDL", "programName", deployment.BaseSignerRegistryProgramName) + args := []string{"idl", "init", "--filepath", idlFile, signer_registry.ProgramID.String()} + e.Logger.Info(args) + output, err := cs_solana.RunCommand("anchor", args, chain.ProgramsPath) + e.Logger.Debugw("IDL init output", "output", output) + if err != nil { + e.Logger.Debugw("IDL init error", "error", err) + return cldf.ChangesetOutput{}, fmt.Errorf("error uploading idl: %w", err) + } + e.Logger.Infow("IDL uploaded", "programName", deployment.BaseSignerRegistryProgramName) + return cldf.ChangesetOutput{}, nil +} + +func BaseSetAuthorityIDLChangeset(e cldf.Environment, c BaseIDLConfig) (cldf.ChangesetOutput, error) { + if err := c.Validate(e); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("error validating idl config: %w", err) + } + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + + timelockSignerPDA, err := cs_solana.FetchTimelockSigner(e, c.ChainSelector) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("error loading timelockSignerPDA: %w", err) + } + + err = cs_solana.SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, signer_registry.ProgramID.String(), deployment.BaseSignerRegistryProgramName, "") + if err != nil { + return cldf.ChangesetOutput{}, err + } + + return cldf.ChangesetOutput{}, nil +} diff --git a/deployment/ccip/changeset/ccip-attestation-solana/cs_ops_solana.go b/deployment/ccip/changeset/ccip-attestation-solana/cs_ops_solana.go new file mode 100644 index 00000000000..06d86835ea8 --- /dev/null +++ b/deployment/ccip/changeset/ccip-attestation-solana/cs_ops_solana.go @@ -0,0 +1,492 @@ +package ccip_attestation + +import ( + "encoding/hex" + "fmt" + "strings" + + "github.com/gagliardetto/solana-go" + "github.com/smartcontractkit/mcms" + mcmsTypes "github.com/smartcontractkit/mcms/types" + + signer_registry "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry_solana" + + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + + cldf_solana "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cs_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana_v0_1_1" + "github.com/smartcontractkit/chainlink/deployment/ccip/shared" + solanastateview "github.com/smartcontractkit/chainlink/deployment/ccip/shared/stateview/solana" +) + +// executeOrBuildMCMSProposal handles the decision to execute instructions directly or build an MCMS proposal. +// If mcmsConfig is nil, it executes the instructions directly on the chain. +// If mcmsConfig is provided, it builds MCMS transactions and creates a proposal. +func executeOrBuildMCMSProposal( + e cldf.Environment, + chain *cldf_solana.Chain, + instructions []solana.Instruction, + programID string, + contractType cldf.ContractType, + mcmsConfig *proposalutils.TimelockConfig, + proposalDescription string, +) (cldf.ChangesetOutput, error) { + if mcmsConfig == nil { + // Direct execution - confirm each instruction individually to avoid Tx size limits + for i, ixn := range instructions { + if err := chain.Confirm([]solana.Instruction{ixn}); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to confirm instruction %d: %w", i, err) + } + } + return cldf.ChangesetOutput{}, nil + } + + mcmsTxns := make([]mcmsTypes.Transaction, 0, len(instructions)) + for _, ixn := range instructions { + tx, err := cs_solana.BuildMCMSTxn(ixn, programID, contractType) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to create MCMS transaction: %w", err) + } + mcmsTxns = append(mcmsTxns, *tx) + } + + proposal, err := cs_solana.BuildProposalsForTxns( + e, chain.Selector, proposalDescription, mcmsConfig.MinDelay, mcmsTxns) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) + } + + return cldf.ChangesetOutput{ + MCMSTimelockProposals: []mcms.TimelockProposal{*proposal}, + }, nil +} + +// use this changeset to rotate NOPs (entirely remove addresses or add new ones) +var _ cldf.ChangeSet[RotateBaseSignerNopsConfig] = RotateBaseSignerNopsChangeset + +// use this changeset to begin a key rotation (add green keys to blue ones) +var _ cldf.ChangeSet[AddGreenKeysConfig] = AddGreenKeysChangeset + +// use this changeset to finalize a key rotation (promote green keys to blue ones) +var _ cldf.ChangeSet[PromoteKeysConfig] = PromoteKeysChangeset + +// use this changeset to change upgrade authority +var _ cldf.ChangeSet[SetUpgradeAuthorityConfig] = SetUpgradeAuthorityChangeset + +type RotateBaseSignerNopsConfig struct { + ChainSelector uint64 + NopKeysToAdd []string + NopKeysToRemove []string + // if set, assumes current upgrade authority is the timelock + MCMS *proposalutils.TimelockConfig +} + +type AddGreenKeysConfig struct { + ChainSelector uint64 + // Pairs of blue key (existing on the account) and new green key for that NOP + BlueGreenKeys [][2]string + // if set, assumes current upgrade authority is the timelock + MCMS *proposalutils.TimelockConfig +} + +type PromoteKeysConfig struct { + ChainSelector uint64 + // Keys to promote (nops can be identified by blue or green indistinctly) + KeysToPromote []string + // if set, assumes current upgrade authority is the timelock + MCMS *proposalutils.TimelockConfig +} + +type SetUpgradeAuthorityConfig struct { + ChainSelector uint64 + NewUpgradeAuthority solana.PublicKey + // if set, assumes current upgrade authority is the timelock + MCMS *proposalutils.TimelockConfig +} + +func RotateBaseSignerNopsChangeset(e cldf.Environment, c RotateBaseSignerNopsConfig) (cldf.ChangesetOutput, error) { + e.Logger.Infow("Rotating Base signer nops", "chain_selector", c.ChainSelector, "removing", c.NopKeysToRemove, "adding", c.NopKeysToAdd) + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + err := c.Validate(e) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to rotate signer nop: %w", err) + } + + configPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("config")}, signer_registry.ProgramID) + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + eventAuthorityPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, signer_registry.ProgramID) + currentAuthority := chain.DeployerKey.PublicKey() + if c.MCMS != nil { + timelockSignerPDA, err := cs_solana.FetchTimelockSigner(e, chain.Selector) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get timelock signer: %w", err) + } + currentAuthority = timelockSignerPDA + } + + var instructions []solana.Instruction + + for _, hexKey := range c.NopKeysToRemove { + key, _ := parseEVMAddress(hexKey) + + ix, err := signer_registry.NewRemoveSignerInstruction(key, currentAuthority, configPda, signersPda, eventAuthorityPda, signer_registry.ProgramID) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to remove signer: %w", err) + } + instructions = append(instructions, ix) + } + for _, hexKey := range c.NopKeysToAdd { + key, _ := parseEVMAddress(hexKey) + ix, err := signer_registry.NewAddSignerInstruction(key, currentAuthority, configPda, signersPda, eventAuthorityPda, signer_registry.ProgramID) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to add signer: %w", err) + } + instructions = append(instructions, ix) + } + + return executeOrBuildMCMSProposal( + e, + &chain, + instructions, + signer_registry.ProgramID.String(), + cldf.ContractType("BaseSignerRegistry"), + c.MCMS, + "proposal to rotate attestation signer NOPs in Solana", + ) +} + +func (c RotateBaseSignerNopsConfig) Validate(e cldf.Environment) error { + keysToAddParsed := make([][20]uint8, len(c.NopKeysToAdd)) + for i, key := range c.NopKeysToAdd { + parsed, err := parseEVMAddress(key) + if err != nil { + return fmt.Errorf("invalid NopKeysToAdd[%d]: %w", i, err) + } + keysToAddParsed[i] = parsed + } + + keysToRemoveParsed := make([][20]uint8, len(c.NopKeysToRemove)) + for i, key := range c.NopKeysToRemove { + parsed, err := parseEVMAddress(key) + if err != nil { + return fmt.Errorf("invalid NopKeysToRemove[%d]: %w", i, err) + } + keysToRemoveParsed[i] = parsed + } + + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + if len(c.NopKeysToRemove) > 0 { + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + + data, err := solanastateview.GetAccountData(e, &chain, signersPda) + if err != nil { + return fmt.Errorf("failed to get signers: %w", err) + } + + signersAccount, err := signer_registry.ParseAccount_Signers(data) + + if err != nil { + return fmt.Errorf("failed to get signers: %w", err) + } + // Check that all NopKeysToRemove exist in signersAccount + for i, keyBytes := range keysToRemoveParsed { + if !keyExistsInSigners(keyBytes, signersAccount.Signers) { + return fmt.Errorf("NopKeysToRemove[%d] (%s) does not exist in current signers", i, c.NopKeysToRemove[i]) + } + } + } + + // Check that there are no keys in common between ToAdd and ToRemove + for i, addKey := range keysToAddParsed { + for j, removeKey := range keysToRemoveParsed { + if addKey == removeKey { + return fmt.Errorf("key %s appears in both NopKeysToAdd[%d] and NopKeysToRemove[%d]", c.NopKeysToAdd[i], i, j) + } + } + } + + return solanastateview.ValidateOwnershipSolana(&e, chain, c.MCMS != nil, signer_registry.ProgramID, shared.SVMSignerRegistry, solana.PublicKey{}) +} + +func AddGreenKeysChangeset(e cldf.Environment, c AddGreenKeysConfig) (cldf.ChangesetOutput, error) { + e.Logger.Infow("Adding green keys to begin rotation", "chain_selector", c.ChainSelector) + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + err := c.Validate(e) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to add green keys: %w", err) + } + configPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("config")}, signer_registry.ProgramID) + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + eventAuthorityPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, signer_registry.ProgramID) + currentAuthority := chain.DeployerKey.PublicKey() + if c.MCMS != nil { + timelockSignerPDA, err := cs_solana.FetchTimelockSigner(e, chain.Selector) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get timelock signer: %w", err) + } + currentAuthority = timelockSignerPDA + } + + var instructions []solana.Instruction + + for _, keyPair := range c.BlueGreenKeys { + blue, _ := parseEVMAddress(keyPair[0]) + green, _ := parseEVMAddress(keyPair[1]) + + ix, err := signer_registry.NewSetSignerNewAddressInstruction(blue, green, currentAuthority, configPda, signersPda, eventAuthorityPda, signer_registry.ProgramID) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to add green key: %w", err) + } + instructions = append(instructions, ix) + } + + return executeOrBuildMCMSProposal( + e, + &chain, + instructions, + signer_registry.ProgramID.String(), + cldf.ContractType("BaseSignerRegistry"), + c.MCMS, + "proposal to add green keys for rotation in Solana", + ) +} + +func (c AddGreenKeysConfig) Validate(e cldf.Environment) error { + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + + // Parse and validate all blue and green keys + blueKeysParsed := make([][20]uint8, len(c.BlueGreenKeys)) + greenKeysParsed := make([][20]uint8, len(c.BlueGreenKeys)) + + for i, keyPair := range c.BlueGreenKeys { + blueParsed, err := parseEVMAddress(keyPair[0]) + if err != nil { + return fmt.Errorf("invalid BlueGreenKeys[%d] blue key: %w", i, err) + } + blueKeysParsed[i] = blueParsed + + greenParsed, err := parseEVMAddress(keyPair[1]) + if err != nil { + return fmt.Errorf("invalid BlueGreenKeys[%d] green key: %w", i, err) + } + greenKeysParsed[i] = greenParsed + } + + // Get current signers account + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + + data, err := solanastateview.GetAccountData(e, &chain, signersPda) + if err != nil { + return fmt.Errorf("failed to get signers: %w", err) + } + + signersAccount, err := signer_registry.ParseAccount_Signers(data) + if err != nil { + return fmt.Errorf("failed to get signers: %w", err) + } + + // Check that all blue keys exist in signersAccount (either as EvmAddress or NewEvmAddress) + for i, blueKey := range blueKeysParsed { + if !keyExistsInSigners(blueKey, signersAccount.Signers) { + return fmt.Errorf("BlueGreenKeys[%d] blue key (%s) does not exist in current signers", i, c.BlueGreenKeys[i][0]) + } + } + + // Check that none of the green keys already exist in signersAccount + for i, greenKey := range greenKeysParsed { + if keyExistsInSigners(greenKey, signersAccount.Signers) { + return fmt.Errorf("BlueGreenKeys[%d] green key (%s) already exists in current signers", i, c.BlueGreenKeys[i][1]) + } + } + + // Check that no green key appears as a blue key in the same config (no circular references) + for i, greenKey := range greenKeysParsed { + for j, blueKey := range blueKeysParsed { + if greenKey == blueKey { + return fmt.Errorf("green key %s appears as both green key in BlueGreenKeys[%d] and blue key in BlueGreenKeys[%d]", c.BlueGreenKeys[i][1], i, j) + } + } + } + + return solanastateview.ValidateOwnershipSolana(&e, chain, c.MCMS != nil, signer_registry.ProgramID, shared.SVMSignerRegistry, solana.PublicKey{}) +} + +func PromoteKeysChangeset(e cldf.Environment, c PromoteKeysConfig) (cldf.ChangesetOutput, error) { + e.Logger.Infow("Promoting green keys to finalize rotation", "chain_selector", c.ChainSelector, "keys", c.KeysToPromote) + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + err := c.Validate(e) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy base signer registry contract: %w", err) + } + configPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("config")}, signer_registry.ProgramID) + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + eventAuthorityPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, signer_registry.ProgramID) + currentAuthority := chain.DeployerKey.PublicKey() + if c.MCMS != nil { + timelockSignerPDA, err := cs_solana.FetchTimelockSigner(e, chain.Selector) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get timelock signer: %w", err) + } + currentAuthority = timelockSignerPDA + } + + var instructions []solana.Instruction + + for _, keyHex := range c.KeysToPromote { + key, _ := parseEVMAddress(keyHex) + + ix, err := signer_registry.NewPromoteSignerAddressInstruction(key, currentAuthority, configPda, signersPda, eventAuthorityPda, signer_registry.ProgramID) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to promote key: %w", err) + } + instructions = append(instructions, ix) + } + + return executeOrBuildMCMSProposal( + e, + &chain, + instructions, + signer_registry.ProgramID.String(), + cldf.ContractType("BaseSignerRegistry"), + c.MCMS, + "proposal to promote green keys in Solana", + ) +} + +func (c PromoteKeysConfig) Validate(e cldf.Environment) error { + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + + // Parse and validate all keys to promote + keysParsed := make([][20]uint8, len(c.KeysToPromote)) + for i, key := range c.KeysToPromote { + parsed, err := parseEVMAddress(key) + if err != nil { + return fmt.Errorf("invalid KeysToPromote[%d]: %w", i, err) + } + keysParsed[i] = parsed + } + + // Get current signers account + signersPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("signers")}, signer_registry.ProgramID) + + data, err := solanastateview.GetAccountData(e, &chain, signersPda) + if err != nil { + return fmt.Errorf("failed to get signers: %w", err) + } + + signersAccount, err := signer_registry.ParseAccount_Signers(data) + if err != nil { + return fmt.Errorf("failed to get signers: %w", err) + } + + // Check that each key exists and has an active blue/green pair + for i, keyBytes := range keysParsed { + signer := findSignerWithKey(keyBytes, signersAccount.Signers) + if signer == nil { + return fmt.Errorf("KeysToPromote[%d] (%s) does not exist in current signers", i, c.KeysToPromote[i]) + } + + // Check that this signer has an active blue/green pair (NewEvmAddress is non-zero) + if signer.NewEvmAddress == nil { + return fmt.Errorf("KeysToPromote[%d] (%s) does not have a green key to promote", i, c.KeysToPromote[i]) + } + } + + return solanastateview.ValidateOwnershipSolana(&e, chain, c.MCMS != nil, signer_registry.ProgramID, shared.SVMSignerRegistry, solana.PublicKey{}) +} + +func (c SetUpgradeAuthorityConfig) Validate(e cldf.Environment) error { + chain := e.BlockChains.SolanaChains()[c.ChainSelector] + return solanastateview.ValidateOwnershipSolana(&e, chain, c.MCMS != nil, signer_registry.ProgramID, shared.SVMSignerRegistry, solana.PublicKey{}) +} + +func SetUpgradeAuthorityChangeset( + e cldf.Environment, + config SetUpgradeAuthorityConfig, +) (cldf.ChangesetOutput, error) { + chain := e.BlockChains.SolanaChains()[config.ChainSelector] + currentAuthority := chain.DeployerKey.PublicKey() + if config.MCMS != nil { + timelockSignerPDA, err := cs_solana.FetchTimelockSigner(e, chain.Selector) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get timelock signer: %w", err) + } + currentAuthority = timelockSignerPDA + } + e.Logger.Infow("Setting upgrade authority", "newUpgradeAuthority", config.NewUpgradeAuthority.String()) + + ixn := cs_solana.SetUpgradeAuthority(&e, &chain, signer_registry.ProgramID, currentAuthority, config.NewUpgradeAuthority, false) + + return executeOrBuildMCMSProposal( + e, + &chain, + []solana.Instruction{ixn}, + solana.BPFLoaderUpgradeableProgramID.String(), + cldf.ContractType(solana.BPFLoaderUpgradeableProgramID.String()), + config.MCMS, + "proposal to SetUpgradeAuthority in Solana", + ) +} + +func parseEVMAddress(addr string) ([20]uint8, error) { + if strings.HasPrefix(addr, "0x") || strings.HasPrefix(addr, "0X") { + addr = addr[2:] + } + + decoded, err := hex.DecodeString(addr) + if err != nil { + return [20]uint8{}, err + } + + if len(decoded) != 20 { + return [20]uint8{}, fmt.Errorf("expected 20 bytes, got %d", len(decoded)) + } + + var result [20]uint8 + copy(result[:], decoded) + return result, nil +} + +// keyExistsInSigners checks if a key exists in the signers list (either as EvmAddress or NewEvmAddress) +func keyExistsInSigners(key [20]uint8, signers []signer_registry.Signer) bool { + // Special case: the zero-key is never considered to be in the signers list (as it's an alias for "removal") + var zeroKey [20]uint8 + if key == zeroKey { + return false + } + + for _, signer := range signers { + // Check current EvmAddress + if signer.EvmAddress == key { + return true + } + // Check NewEvmAddress if it exists + if signer.NewEvmAddress != nil && *signer.NewEvmAddress == key { + return true + } + } + return false +} + +// findSignerWithKey finds and returns the signer that contains the given key (either as EvmAddress or NewEvmAddress) +func findSignerWithKey(key [20]uint8, signers []signer_registry.Signer) *signer_registry.Signer { + // Return nil for all-zero keys + var zeroKey [20]uint8 + if key == zeroKey { + return nil + } + + for i := range signers { + signer := &signers[i] + // Check current EvmAddress + if signer.EvmAddress == key { + return signer + } + // Check NewEvmAddress if it exists + if signer.NewEvmAddress != nil && *signer.NewEvmAddress == key { + return signer + } + } + return nil +} diff --git a/deployment/ccip/changeset/ccip-attestation-solana/cs_transfer_to_mcms_with_timelock_solana.go b/deployment/ccip/changeset/ccip-attestation-solana/cs_transfer_to_mcms_with_timelock_solana.go new file mode 100644 index 00000000000..eb0907ac070 --- /dev/null +++ b/deployment/ccip/changeset/ccip-attestation-solana/cs_transfer_to_mcms_with_timelock_solana.go @@ -0,0 +1,226 @@ +package ccip_attestation + +import ( + "errors" + "fmt" + + "github.com/gagliardetto/solana-go" + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/mcms" + "github.com/smartcontractkit/mcms/sdk" + + mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + mcmsTypes "github.com/smartcontractkit/mcms/types" + + signer_registry "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry_solana" + + cldf_solana "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cs_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana_v0_1_1" + "github.com/smartcontractkit/chainlink/deployment/ccip/shared" + + "github.com/smartcontractkit/chainlink/deployment/ccip/shared/stateview" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + "github.com/smartcontractkit/chainlink/deployment/common/types" +) + +var _ cldf.ChangeSet[TransferSignerRegistryToMCMSWithTimelockSolanaConfig] = TransferSignerRegistryToMCMSWithTimelockSolanaChangeset + +type TransferSignerRegistryToMCMSWithTimelockSolanaConfig struct { + ChainSelector uint64 + CurrentOwner solana.PublicKey + ProposedOwner solana.PublicKey + // MCMSCfg is for the accept ownership proposal + MCMSCfg proposalutils.TimelockConfig +} + +func (c TransferSignerRegistryToMCMSWithTimelockSolanaConfig) Validate(e cldf.Environment) error { + if err := cldf.IsValidChainSelector(c.ChainSelector); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", c.ChainSelector, err) + } + family, _ := chainsel.GetSelectorFamily(c.ChainSelector) + if family != chainsel.FamilySolana { + return fmt.Errorf("chain %d is not a solana chain", c.ChainSelector) + } + ccipState, err := stateview.LoadOnchainStateSolana(e) + if err != nil { + return fmt.Errorf("failed to load onchain state: %w", err) + } + if len(ccipState.SolChains) == 0 { + return errors.New("no chains found") + } + solChain := e.BlockChains.SolanaChains()[c.ChainSelector] + addresses, err := e.ExistingAddresses.AddressesForChain(c.ChainSelector) + if err != nil { + return fmt.Errorf("failed to get addresses for chain: %w", err) + } + _, err = state.MaybeLoadMCMSWithTimelockChainStateSolana(solChain, addresses) + if err != nil { + return fmt.Errorf("failed to load mcm state: %w", err) + } + // If there is no timelock and mcms proposer on the chain, the transfer will fail. + timelockID, err := cldf.SearchAddressBook(e.ExistingAddresses, c.ChainSelector, types.RBACTimelock) + if err != nil { + return fmt.Errorf("timelock not present on the chain %w", err) + } + proposerID, err := cldf.SearchAddressBook(e.ExistingAddresses, c.ChainSelector, types.ProposerManyChainMultisig) + if err != nil { + return fmt.Errorf("mcms proposer not present on the chain %w", err) + } + // Make sure addresses are correctly parsed. Format is: "programID.PDASeed" + _, _, err = mcmsSolana.ParseContractAddress(timelockID) + if err != nil { + return fmt.Errorf("failed to parse timelock address: %w", err) + } + _, _, err = mcmsSolana.ParseContractAddress(proposerID) + if err != nil { + return fmt.Errorf("failed to parse proposer address: %w", err) + } + return nil +} + +// TransferCCIPToMCMSWithTimelockSolana creates a changeset that transfers ownership of the +// signer registry program to the timelock on the chain and generates a corresponding proposal +// with the accept ownership tx to complete the transfer. It assumes that DeployMCMSWithTimelock +// for solana has already been run s.t. the timelock and mcms exist on the chain and that the +// proposed address to transfer ownership is currently owned by the deployer key. +func TransferSignerRegistryToMCMSWithTimelockSolanaChangeset( + e cldf.Environment, + cfg TransferSignerRegistryToMCMSWithTimelockSolanaConfig, +) (cldf.ChangesetOutput, error) { + if err := cfg.Validate(e); err != nil { + return cldf.ChangesetOutput{}, err + } + var batches []mcmsTypes.BatchOperation + + timelocks := map[uint64]string{} + proposers := map[uint64]string{} + inspectors := map[uint64]sdk.Inspector{} + + chainSelector := cfg.ChainSelector + + solChain := e.BlockChains.SolanaChains()[chainSelector] + addresses, _ := e.ExistingAddresses.AddressesForChain(chainSelector) + mcmState, _ := state.MaybeLoadMCMSWithTimelockChainStateSolana(solChain, addresses) + + currentOwner := solChain.DeployerKey.PublicKey() + if !cfg.CurrentOwner.IsZero() { + currentOwner = cfg.CurrentOwner + } + timelockSigner := state.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) + proposedOwner := timelockSigner + if !cfg.ProposedOwner.IsZero() { + proposedOwner = cfg.ProposedOwner + } + if currentOwner.Equals(proposedOwner) { + return cldf.ChangesetOutput{}, fmt.Errorf("current owner and proposed owner are the same: %s", currentOwner) + } + + timelocks[solChain.Selector] = mcmsSolana.ContractAddress( + mcmState.TimelockProgram, + mcmsSolana.PDASeed(mcmState.TimelockSeed), + ) + proposers[solChain.Selector] = mcmsSolana.ContractAddress(mcmState.McmProgram, mcmsSolana.PDASeed(mcmState.ProposerMcmSeed)) + inspectors[solChain.Selector] = mcmsSolana.NewInspector(solChain.Client) + mcmsTxs, err := transferOwnershipSignerRegistry( + solChain, + currentOwner, + proposedOwner, + timelockSigner, + ) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership of base signer registry: %w", err) + } + batches = append(batches, mcmsTypes.BatchOperation{ + ChainSelector: mcmsTypes.ChainSelector(chainSelector), + Transactions: mcmsTxs, + }) + + proposal, err := proposalutils.BuildProposalFromBatchesV2( + e, + timelocks, + proposers, + inspectors, + batches, + "proposal to transfer ownership of CCIP contracts to timelock", + cfg.MCMSCfg) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) + } + + return cldf.ChangesetOutput{MCMSTimelockProposals: []mcms.TimelockProposal{*proposal}}, nil +} + +func transferOwnershipSignerRegistry( + solChain cldf_solana.Chain, + currentOwner solana.PublicKey, + proposedOwner solana.PublicKey, + timelockSigner solana.PublicKey, +) ([]mcmsTypes.Transaction, error) { + var result []mcmsTypes.Transaction + + programID := signer_registry.ProgramID + configPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("config")}, signer_registry.ProgramID) + eventAuthorityPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, signer_registry.ProgramID) + + // Build specialized closures + buildTransfer := func(proposedOwner, config, authority solana.PublicKey) (solana.Instruction, error) { + ix, err := signer_registry.NewProposeNewOwnerInstruction( + proposedOwner, authority, config, eventAuthorityPda, signer_registry.ProgramID, + ) + if err != nil { + return nil, err + } + ixData, err := ix.Data() + if err != nil { + return nil, fmt.Errorf("failed to extract data payload from signer registry transfer ownership instruction: %w", err) + } + transferOwnershipIx := solana.NewInstruction(programID, ix.Accounts(), ixData) + for _, acc := range transferOwnershipIx.Accounts() { + if acc.PublicKey == timelockSigner { + acc.IsSigner = false + } + } + return transferOwnershipIx, nil + } + buildAccept := func(config, newOwnerAuthority solana.PublicKey) (solana.Instruction, error) { + // If the router has its own accept function, use that + ix, err := signer_registry.NewAcceptOwnershipInstruction( + newOwnerAuthority, config, eventAuthorityPda, signer_registry.ProgramID, + ) + if err != nil { + return nil, err + } + ixData, err := ix.Data() + if err != nil { + return nil, fmt.Errorf("failed to extract data payload from signer registry accept ownership instruction: %w", err) + } + acceptOwnershipIx := solana.NewInstruction(programID, ix.Accounts(), ixData) + for _, acc := range acceptOwnershipIx.Accounts() { + if acc.PublicKey == timelockSigner { + acc.IsSigner = false + } + } + return acceptOwnershipIx, nil + } + + tx, err := cs_solana.TransferAndWrapAcceptOwnership( + buildTransfer, + buildAccept, + programID, + proposedOwner, + configPda, + currentOwner, + solChain, + shared.SVMSignerRegistry, + timelockSigner, + ) + + if err != nil { + return nil, fmt.Errorf("failed to transfer signer registry ownership: %w", err) + } + + result = append(result, tx) + return result, nil +} diff --git a/deployment/ccip/changeset/solana_v0_1_1/cs_build_solana.go b/deployment/ccip/changeset/solana_v0_1_1/cs_build_solana.go index 6c928ffc6b1..f85b273dd90 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/cs_build_solana.go +++ b/deployment/ccip/changeset/solana_v0_1_1/cs_build_solana.go @@ -68,7 +68,7 @@ type BuildSolanaConfig struct { } // Run a command in a specific directory -func runCommand(command string, args []string, workDir string) (string, error) { +func RunCommand(command string, args []string, workDir string) (string, error) { cmd := exec.Command(command, args...) cmd.Dir = workDir var stdout, stderr bytes.Buffer @@ -94,27 +94,27 @@ func cloneRepo(e cldf.Environment, revision string, forceClean bool) error { e.Logger.Debugw("Repository already exists, discarding local changes and updating", "dir", cloneDir) // Discard any local changes - _, err := runCommand("git", []string{"reset", "--hard"}, cloneDir) + _, err := RunCommand("git", []string{"reset", "--hard"}, cloneDir) if err != nil { return fmt.Errorf("failed to discard local changes: %w", err) } // Fetch the latest changes from the remote - _, err = runCommand("git", []string{"fetch", "origin"}, cloneDir) + _, err = RunCommand("git", []string{"fetch", "origin"}, cloneDir) if err != nil { return fmt.Errorf("failed to fetch origin: %w", err) } } else { // Repository does not exist, clone it e.Logger.Debugw("Cloning repository", "url", repoURL, "revision", revision) - _, err := runCommand("git", []string{"clone", repoURL, cloneDir}, ".") + _, err := RunCommand("git", []string{"clone", repoURL, cloneDir}, ".") if err != nil { return fmt.Errorf("failed to clone repository: %w", err) } } e.Logger.Debugw("Checking out revision", "revision", revision) - _, err := runCommand("git", []string{"checkout", revision}, cloneDir) + _, err := RunCommand("git", []string{"checkout", revision}, cloneDir) if err != nil { return fmt.Errorf("failed to checkout revision %s: %w", revision, err) } @@ -126,7 +126,7 @@ func cloneRepo(e cldf.Environment, revision string, forceClean bool) error { func replaceKeys(e cldf.Environment) error { solanaDir := filepath.Join(cloneDir, anchorDir, "..") e.Logger.Debugw("Replacing keys", "solanaDir", solanaDir) - output, err := runCommand("make", []string{"docker-update-contracts"}, solanaDir) + output, err := RunCommand("make", []string{"docker-update-contracts"}, solanaDir) if err != nil { return fmt.Errorf("anchor key replacement failed: %s %w", output, err) } @@ -209,7 +209,7 @@ func generateVanityKeys(e cldf.Environment, keys map[cldf.ContractType]string) e args := []string{"grind", "--starts-with", prefix + ":1"} // Run command using helper function - output, err := runCommand("solana-keygen", args, "./") + output, err := RunCommand("solana-keygen", args, "./") if err != nil { return fmt.Errorf("failed to generate vanity key for program %s: %w", program, err) } @@ -252,7 +252,7 @@ func generateVanityKeys(e cldf.Environment, keys map[cldf.ContractType]string) e } func copyFile(srcFile string, destDir string) error { - output, err := runCommand("cp", []string{srcFile, destDir}, ".") + output, err := RunCommand("cp", []string{srcFile, destDir}, ".") if err != nil { return fmt.Errorf("failed to copy file: %s %w", output, err) } @@ -264,7 +264,7 @@ func buildProject(e cldf.Environment) error { solanaDir := filepath.Join(cloneDir, anchorDir, "..") e.Logger.Debugw("Building project", "solanaDir", solanaDir) args := []string{"docker-build-contracts"} - output, err := runCommand("make", args, solanaDir) + output, err := RunCommand("make", args, solanaDir) if err != nil { return fmt.Errorf("anchor build failed: %s %w", output, err) } diff --git a/deployment/ccip/changeset/solana_v0_1_1/cs_deploy_chain.go b/deployment/ccip/changeset/solana_v0_1_1/cs_deploy_chain.go index 81da467d998..6d06de5e1a8 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/cs_deploy_chain.go +++ b/deployment/ccip/changeset/solana_v0_1_1/cs_deploy_chain.go @@ -944,7 +944,7 @@ func generateUpgradeTxns( if err != nil { return txns, fmt.Errorf("failed to deploy program: %w", err) } - ixn := setUpgradeAuthority(&e, &chain, bufferProgram, chain.DeployerKey.PublicKey(), config.UpgradeConfig.UpgradeAuthority, true) + ixn := SetUpgradeAuthority(&e, &chain, bufferProgram, chain.DeployerKey.PublicKey(), config.UpgradeConfig.UpgradeAuthority, true) if err := chain.Confirm([]solana.Instruction{ixn}); err != nil { return txns, fmt.Errorf("failed to confirm setUpgradeAuthority: %w", err) } diff --git a/deployment/ccip/changeset/solana_v0_1_1/cs_idl.go b/deployment/ccip/changeset/solana_v0_1_1/cs_idl.go index d40044e5339..34474c8950e 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/cs_idl.go +++ b/deployment/ccip/changeset/solana_v0_1_1/cs_idl.go @@ -122,7 +122,7 @@ func (c IDLConfig) Validate(e cldf.Environment) error { } commitSha := VersionToShortCommitSHA[c.SolanaContractVersion] - return repoSetup(e, chain, commitSha) + return RepoSetup(e, chain, commitSha) } // ANCHOR CLI OPERATIONS @@ -138,45 +138,45 @@ func UploadIDL(e cldf.Environment, c IDLConfig) (cldf.ChangesetOutput, error) { // start uploading if c.Router { - err := idlInit(e, chain.ProgramsPath, chainState.Router.String(), deployment.RouterProgramName) + err := IdlInit(e, chain.ProgramsPath, chainState.Router.String(), deployment.RouterProgramName) if err != nil { return cldf.ChangesetOutput{}, err } } if c.FeeQuoter { - err := idlInit(e, chain.ProgramsPath, chainState.FeeQuoter.String(), deployment.FeeQuoterProgramName) + err := IdlInit(e, chain.ProgramsPath, chainState.FeeQuoter.String(), deployment.FeeQuoterProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } if c.OffRamp { - err := idlInit(e, chain.ProgramsPath, chainState.OffRamp.String(), deployment.OffRampProgramName) + err := IdlInit(e, chain.ProgramsPath, chainState.OffRamp.String(), deployment.OffRampProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } if c.RMNRemote { - err := idlInit(e, chain.ProgramsPath, chainState.RMNRemote.String(), deployment.RMNRemoteProgramName) + err := IdlInit(e, chain.ProgramsPath, chainState.RMNRemote.String(), deployment.RMNRemoteProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } for _, bnmMetadata := range c.BurnMintTokenPoolMetadata { tokenPool := chainState.GetActiveTokenPool(shared.BurnMintTokenPool, bnmMetadata) - err := idlInit(e, chain.ProgramsPath, tokenPool.String(), deployment.BurnMintTokenPoolProgramName) + err := IdlInit(e, chain.ProgramsPath, tokenPool.String(), deployment.BurnMintTokenPoolProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } for _, lrMetadata := range c.LockReleaseTokenPoolMetadata { tokenPool := chainState.GetActiveTokenPool(shared.LockReleaseTokenPool, lrMetadata) - err := idlInit(e, chain.ProgramsPath, tokenPool.String(), deployment.LockReleaseTokenPoolProgramName) + err := IdlInit(e, chain.ProgramsPath, tokenPool.String(), deployment.LockReleaseTokenPoolProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } if c.CCTPTokenPool { - err := idlInit(e, chain.ProgramsPath, chainState.CCTPTokenPool.String(), deployment.CCTPTokenPoolProgramName) + err := IdlInit(e, chain.ProgramsPath, chainState.CCTPTokenPool.String(), deployment.CCTPTokenPoolProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } @@ -190,19 +190,19 @@ func UploadIDL(e cldf.Environment, c IDLConfig) (cldf.ChangesetOutput, error) { return cldf.ChangesetOutput{}, fmt.Errorf("failed to load MCMS with timelock chain state: %w", err) } if c.MCM { - err := idlInit(e, chain.ProgramsPath, mcmState.McmProgram.String(), deployment.McmProgramName) + err := IdlInit(e, chain.ProgramsPath, mcmState.McmProgram.String(), deployment.McmProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } if c.Timelock { - err := idlInit(e, chain.ProgramsPath, mcmState.TimelockProgram.String(), deployment.TimelockProgramName) + err := IdlInit(e, chain.ProgramsPath, mcmState.TimelockProgram.String(), deployment.TimelockProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } } if c.AccessController { - err := idlInit(e, chain.ProgramsPath, mcmState.AccessControllerProgram.String(), deployment.AccessControllerProgramName) + err := IdlInit(e, chain.ProgramsPath, mcmState.AccessControllerProgram.String(), deployment.AccessControllerProgramName) if err != nil { return cldf.ChangesetOutput{}, nil } @@ -227,45 +227,45 @@ func SetAuthorityIDL(e cldf.Environment, c IDLConfig) (cldf.ChangesetOutput, err // set idl authority if c.Router { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.Router.String(), deployment.RouterProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.Router.String(), deployment.RouterProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } } if c.FeeQuoter { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.FeeQuoter.String(), deployment.FeeQuoterProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.FeeQuoter.String(), deployment.FeeQuoterProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } } if c.OffRamp { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.OffRamp.String(), deployment.OffRampProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.OffRamp.String(), deployment.OffRampProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } } if c.RMNRemote { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.RMNRemote.String(), deployment.RMNRemoteProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.RMNRemote.String(), deployment.RMNRemoteProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } } for _, bnmMetadata := range c.BurnMintTokenPoolMetadata { tokenPool := chainState.GetActiveTokenPool(shared.BurnMintTokenPool, bnmMetadata) - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, tokenPool.String(), deployment.BurnMintTokenPoolProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, tokenPool.String(), deployment.BurnMintTokenPoolProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } } for _, lrMetadata := range c.LockReleaseTokenPoolMetadata { tokenPool := chainState.GetActiveTokenPool(shared.LockReleaseTokenPool, lrMetadata) - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, tokenPool.String(), deployment.LockReleaseTokenPoolProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, tokenPool.String(), deployment.LockReleaseTokenPoolProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } } if c.CCTPTokenPool { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.CCTPTokenPool.String(), deployment.CCTPTokenPoolProgramName, "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, chainState.CCTPTokenPool.String(), deployment.CCTPTokenPoolProgramName, "") if err != nil { return cldf.ChangesetOutput{}, err } @@ -281,19 +281,19 @@ func SetAuthorityIDL(e cldf.Environment, c IDLConfig) (cldf.ChangesetOutput, err } if c.AccessController { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, mcmState.AccessControllerProgram.String(), types.AccessControllerProgram.String(), "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, mcmState.AccessControllerProgram.String(), types.AccessControllerProgram.String(), "") if err != nil { return cldf.ChangesetOutput{}, err } } if c.Timelock { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, mcmState.TimelockProgram.String(), types.RBACTimelockProgram.String(), "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, mcmState.TimelockProgram.String(), types.RBACTimelockProgram.String(), "") if err != nil { return cldf.ChangesetOutput{}, err } } if c.MCM { - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, mcmState.McmProgram.String(), types.ManyChainMultisigProgram.String(), "") + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), chain.ProgramsPath, mcmState.McmProgram.String(), types.ManyChainMultisigProgram.String(), "") if err != nil { return cldf.ChangesetOutput{}, err } @@ -303,7 +303,7 @@ func SetAuthorityIDL(e cldf.Environment, c IDLConfig) (cldf.ChangesetOutput, err } // parse anchor version from running anchor --version -func parseAnchorVersion(output string) (string, error) { +func ParseAnchorVersion(output string) (string, error) { const prefix = "anchor-cli " if strings.HasPrefix(output, prefix) { return strings.TrimSpace(strings.TrimPrefix(output, prefix)), nil @@ -312,7 +312,7 @@ func parseAnchorVersion(output string) (string, error) { } // create Anchor.toml file to simulate anchor workspace -func writeAnchorToml(e cldf.Environment, filename, anchorVersion, cluster, wallet string) error { +func WriteAnchorToml(e cldf.Environment, filename, anchorVersion, cluster, wallet string) error { e.Logger.Debugw("Writing Anchor.toml", "filename", filename, "anchorVersion", anchorVersion, "cluster", cluster, "wallet", wallet) config := map[string]interface{}{ "toolchain": map[string]string{ @@ -344,7 +344,7 @@ func writeAnchorToml(e cldf.Environment, filename, anchorVersion, cluster, walle } // resolve artifacts based on sha and write anchor.toml file to simulate anchor workspace -func repoSetup(e cldf.Environment, chain cldfsolana.Chain, gitCommitSha string) error { +func RepoSetup(e cldf.Environment, chain cldfsolana.Chain, gitCommitSha string) error { e.Logger.Debug("Downloading Solana CCIP program artifacts...") err := memory.DownloadSolanaCCIPProgramArtifacts(e.GetContext(), chain.ProgramsPath, e.Logger, gitCommitSha) if err != nil { @@ -352,18 +352,18 @@ func repoSetup(e cldf.Environment, chain cldfsolana.Chain, gitCommitSha string) } // get anchor version - output, err := runCommand("anchor", []string{"--version"}, ".") + output, err := RunCommand("anchor", []string{"--version"}, ".") if err != nil { return errors.New("anchor-cli not installed in path") } e.Logger.Debugw("Anchor version command output", "output", output) - anchorVersion, err := parseAnchorVersion(output) + anchorVersion, err := ParseAnchorVersion(output) if err != nil { return fmt.Errorf("error parsing anchor version: %w", err) } // create Anchor.toml // this creates anchor workspace with cluster and wallet configured - if err := writeAnchorToml(e, filepath.Join(chain.ProgramsPath, "Anchor.toml"), anchorVersion, chain.URL, chain.KeypairPath); err != nil { + if err := WriteAnchorToml(e, filepath.Join(chain.ProgramsPath, "Anchor.toml"), anchorVersion, chain.URL, chain.KeypairPath); err != nil { return fmt.Errorf("error writing Anchor.toml: %w", err) } @@ -415,7 +415,7 @@ func getIDL(e cldf.Environment, programsPath, programID string, programName stri } // initialize IDL for a program -func idlInit(e cldf.Environment, programsPath, programID, programName string) error { +func IdlInit(e cldf.Environment, programsPath, programID, programName string) error { idlFile, err := getIDL(e, programsPath, programID, programName) if err != nil { return fmt.Errorf("error getting IDL: %w", err) @@ -423,7 +423,7 @@ func idlInit(e cldf.Environment, programsPath, programID, programName string) er e.Logger.Infow("Uploading IDL", "programName", programName) args := []string{"idl", "init", "--filepath", idlFile, programID} e.Logger.Info(args) - output, err := runCommand("anchor", args, programsPath) + output, err := RunCommand("anchor", args, programsPath) e.Logger.Debugw("IDL init output", "output", output) if err != nil { e.Logger.Debugw("IDL init error", "error", err) @@ -461,7 +461,7 @@ func writeBuffer(e cldf.Environment, programsPath, programID, programName string e.Logger.Infow("Writing IDL buffer", "programID", programID) args := []string{"idl", "write-buffer", "--filepath", idlFile, programID} e.Logger.Info(args) - output, err := runCommand("anchor", args, programsPath) + output, err := RunCommand("anchor", args, programsPath) if err != nil { return solana.PublicKey{}, fmt.Errorf("error writing IDL buffer: %w", err) } @@ -477,7 +477,7 @@ func writeBuffer(e cldf.Environment, programsPath, programID, programName string return bufferAddress, nil } -func setAuthorityIDLByCLI(e cldf.Environment, newAuthority, programsPath, programID, programName, bufferAccount string) error { +func SetAuthorityIDLByCLI(e cldf.Environment, newAuthority, programsPath, programID, programName, bufferAccount string) error { e.Logger.Infow("Setting IDL authority", "programName", programName, "newAuthority", newAuthority) args := []string{"idl", "set-authority", "-n", newAuthority, "-p", programID} if bufferAccount != "" { @@ -485,7 +485,7 @@ func setAuthorityIDLByCLI(e cldf.Environment, newAuthority, programsPath, progra args = append(args, bufferAccount) } e.Logger.Info(args) - _, err := runCommand("anchor", args, programsPath) + _, err := RunCommand("anchor", args, programsPath) if err != nil { return fmt.Errorf("error setting idl authority: %w", err) } @@ -722,7 +722,7 @@ func upgradeIDLIx(e cldf.Environment, programsPath, programID, programName strin authority := e.BlockChains.SolanaChains()[c.ChainSelector].DeployerKey.PublicKey() if c.MCMS != nil { authority = timelockSignerPDA - err = setAuthorityIDLByCLI(e, timelockSignerPDA.String(), programsPath, programID, programName, buffer.String()) + err = SetAuthorityIDLByCLI(e, timelockSignerPDA.String(), programsPath, programID, programName, buffer.String()) if err != nil { return nil, fmt.Errorf("error setting buffer authority: %w", err) } diff --git a/deployment/ccip/changeset/solana_v0_1_1/cs_ops.go b/deployment/ccip/changeset/solana_v0_1_1/cs_ops.go index f50a8580a02..399abc90b24 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/cs_ops.go +++ b/deployment/ccip/changeset/solana_v0_1_1/cs_ops.go @@ -242,7 +242,7 @@ func SetUpgradeAuthorityChangeset( e.Logger.Infow("Setting upgrade authority", "newUpgradeAuthority", config.NewUpgradeAuthority.String()) mcmsTxns := make([]mcmsTypes.Transaction, 0) for _, programID := range programs { - ixn := setUpgradeAuthority(&e, &chain, programID, currentAuthority, config.NewUpgradeAuthority, false) + ixn := SetUpgradeAuthority(&e, &chain, programID, currentAuthority, config.NewUpgradeAuthority, false) if config.MCMS == nil { if err := chain.Confirm([]solana.Instruction{ixn}); err != nil { return cldf.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err) @@ -271,8 +271,8 @@ func SetUpgradeAuthorityChangeset( return cldf.ChangesetOutput{}, nil } -// setUpgradeAuthority creates a transaction to set the upgrade authority for a program -func setUpgradeAuthority( +// SetUpgradeAuthority creates a transaction to set the upgrade authority for a program +func SetUpgradeAuthority( e *cldf.Environment, chain *cldf_solana.Chain, programID solana.PublicKey, diff --git a/deployment/ccip/changeset/solana_v0_1_1/cs_solana_token.go b/deployment/ccip/changeset/solana_v0_1_1/cs_solana_token.go index ed2ca273310..dafeff47458 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/cs_solana_token.go +++ b/deployment/ccip/changeset/solana_v0_1_1/cs_solana_token.go @@ -446,13 +446,13 @@ func UploadTokenMetadata(e cldf.Environment, cfg UploadTokenMetadataConfig) (cld chain := e.BlockChains.SolanaChains()[cfg.ChainSelector] mcmsTxs := make([]mcmsTypes.Transaction, 0) - out1, err1 := runCommand("solana", []string{"config", "set", "--url", chain.URL}, chain.ProgramsPath) + out1, err1 := RunCommand("solana", []string{"config", "set", "--url", chain.URL}, chain.ProgramsPath) e.Logger.Infow("solana config set url output", "output", out1) if err1 != nil { e.Logger.Errorw("solana config set url error", "error", err1) return cldf.ChangesetOutput{}, fmt.Errorf("error setting solana url: %w", err1) } - out2, err2 := runCommand("solana", []string{"config", "set", "--keypair", chain.KeypairPath}, chain.ProgramsPath) + out2, err2 := RunCommand("solana", []string{"config", "set", "--keypair", chain.KeypairPath}, chain.ProgramsPath) e.Logger.Infow("solana config set keypair output", "output", out2) if err2 != nil { e.Logger.Errorw("solana config set keypair error", "error", err2) @@ -468,7 +468,7 @@ func UploadTokenMetadata(e cldf.Environment, cfg UploadTokenMetadataConfig) (cld e.Logger.Infow("Uploading token metadata", "tokenPubkey", metadata.TokenPubkey.String()) args := []string{"create", "metadata", "--mint", metadata.TokenPubkey.String(), "--metadata", metadata.MetadataJSONPath} e.Logger.Info(args) - output, err := runCommand("metaboss", args, chain.ProgramsPath) + output, err := RunCommand("metaboss", args, chain.ProgramsPath) e.Logger.Infow("metaboss output", "output", output) if err != nil { e.Logger.Errorw("metaboss create error", "error", err) @@ -567,13 +567,13 @@ func DisableFreezeAuthority(e cldf.Environment, cfg DisableFreezeAuthorityConfig return cldf.ChangesetOutput{}, errors.New("chain selector is required") } chain := e.BlockChains.SolanaChains()[cfg.ChainSelector] - out1, err1 := runCommand("solana", []string{"config", "set", "--url", chain.URL}, chain.ProgramsPath) + out1, err1 := RunCommand("solana", []string{"config", "set", "--url", chain.URL}, chain.ProgramsPath) e.Logger.Infow("solana config set url output", "output", out1) if err1 != nil { e.Logger.Errorw("solana config set url error", "error", err1) return cldf.ChangesetOutput{}, fmt.Errorf("error setting solana url: %w", err1) } - out2, err2 := runCommand("solana", []string{"config", "set", "--keypair", chain.KeypairPath}, chain.ProgramsPath) + out2, err2 := RunCommand("solana", []string{"config", "set", "--keypair", chain.KeypairPath}, chain.ProgramsPath) e.Logger.Infow("solana config set keypair output", "output", out2) if err2 != nil { e.Logger.Errorw("solana config set keypair error", "error", err2) @@ -584,7 +584,7 @@ func DisableFreezeAuthority(e cldf.Environment, cfg DisableFreezeAuthorityConfig e.Logger.Infow("Disabling freeze authority", "tokenPubkey", tokenPubkey.String()) args := []string{"authorize", tokenPubkey.String(), "freeze", "--disable"} e.Logger.Info(args) - output, err := runCommand("spl-token", args, chain.ProgramsPath) + output, err := RunCommand("spl-token", args, chain.ProgramsPath) e.Logger.Debugw("spl-token output", "output", output) if err != nil { e.Logger.Debugw("spl-token authorize error", "error", err) diff --git a/deployment/ccip/changeset/solana_v0_1_1/cs_verify_contracts.go b/deployment/ccip/changeset/solana_v0_1_1/cs_verify_contracts.go index 97ffdc3af55..4cd3a3fe817 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/cs_verify_contracts.go +++ b/deployment/ccip/changeset/solana_v0_1_1/cs_verify_contracts.go @@ -69,7 +69,7 @@ func runSolanaVerifyMCMS(e cldf.Environment, "--uploader", timelockSignerPDA.String(), "--program-id", programID, } - output, err := runCommand("solana-verify", cmdArgs, chain.ProgramsPath) + output, err := RunCommand("solana-verify", cmdArgs, chain.ProgramsPath) e.Logger.Infow("remote submit-job output", "output", output) if err != nil { return fmt.Errorf("solana program verification failed: %s %w", output, err) @@ -90,7 +90,7 @@ func runSolanaVerifyMCMS(e cldf.Environment, "--uploader", timelockSignerPDA.String(), } e.Logger.Infow("export-pda-tx cmdArgs", "cmdArgs", cmdArgs) - output, err := runCommand("solana-verify", cmdArgs, ".") + output, err := RunCommand("solana-verify", cmdArgs, ".") e.Logger.Infow("export-pda-tx output", "output", output) if err != nil { return fmt.Errorf("solana program verification failed: %s %w", output, err) @@ -137,7 +137,7 @@ func runSolanaVerifyWithoutMCMS(e cldf.Environment, "--skip-prompt", } - output, err := runCommand("solana-verify", cmdArgs, ".") + output, err := RunCommand("solana-verify", cmdArgs, ".") e.Logger.Infow("verify-from-repo output", "output", output) if err != nil { return fmt.Errorf("solana program verification failed: %s %w", output, err) @@ -150,7 +150,7 @@ func runSolanaVerifyWithoutMCMS(e cldf.Environment, "--uploader", chain.DeployerKey.PublicKey().String(), "--program-id", programID, } - output, err := runCommand("solana-verify", cmdArgs, chain.ProgramsPath) + output, err := RunCommand("solana-verify", cmdArgs, chain.ProgramsPath) e.Logger.Infow("remote submit-job output", "output", output) if err != nil { return fmt.Errorf("solana program verification failed: %s %w", output, err) @@ -294,7 +294,7 @@ func setConfig(e cldf.Environment, chain cldf_solana.Chain) error { "set", "--keypair", chain.KeypairPath, } - output, err := runCommand("solana", cmdArgs, ".") + output, err := RunCommand("solana", cmdArgs, ".") e.Logger.Infow("solana config set output", "output", output) if err != nil { return fmt.Errorf("failed to set keypair during program verification: %s %w", output, err) @@ -304,7 +304,7 @@ func setConfig(e cldf.Environment, chain cldf_solana.Chain) error { "set", "--url", chain.URL, } - output, err = runCommand("solana", cmdArgs, ".") + output, err = RunCommand("solana", cmdArgs, ".") e.Logger.Infow("solana config set output", "output", output) if err != nil { return fmt.Errorf("failed to set url during program verification: %s %w", output, err) diff --git a/deployment/ccip/changeset/solana_v0_1_1/ownership_transfer_helpers.go b/deployment/ccip/changeset/solana_v0_1_1/ownership_transfer_helpers.go index 8ff7bd2f88a..9b5311ff229 100644 --- a/deployment/ccip/changeset/solana_v0_1_1/ownership_transfer_helpers.go +++ b/deployment/ccip/changeset/solana_v0_1_1/ownership_transfer_helpers.go @@ -33,13 +33,13 @@ type AcceptOwnershipFn func( authority solana.PublicKey, ) (solana.Instruction, error) -// transferAndWrapAcceptOwnership abstracts logic of: +// TransferAndWrapAcceptOwnership abstracts logic of: // - building a “transfer ownership” instruction // - confirming on-chain // - building an “accept ownership” instruction // - wrapping it in an MCMS transaction // - returning the mcms transaction for the accept ownership -func transferAndWrapAcceptOwnership( +func TransferAndWrapAcceptOwnership( buildTransfer TransferOwnershipFn, buildAccept AcceptOwnershipFn, programID solana.PublicKey, // e.g. token_pool program or router program @@ -151,7 +151,7 @@ func transferOwnershipRouter( return acceptOwnershipIx, nil } - tx, err := transferAndWrapAcceptOwnership( + tx, err := TransferAndWrapAcceptOwnership( buildTransfer, buildAccept, routerProgramID, @@ -229,7 +229,7 @@ func transferOwnershipFeeQuoter( return acceptOwnershipIx, nil } - tx, err := transferAndWrapAcceptOwnership( + tx, err := TransferAndWrapAcceptOwnership( buildTransfer, buildAccept, feeQuoterProgramID, @@ -307,7 +307,7 @@ func transferOwnershipOffRamp( return acceptOwnershipIx, nil } - tx, err := transferAndWrapAcceptOwnership( + tx, err := TransferAndWrapAcceptOwnership( buildTransfer, buildAccept, offRampProgramID, @@ -376,7 +376,7 @@ func transferOwnershipBurnMintTokenPools( return ix, nil } - tx, err := transferAndWrapAcceptOwnership( + tx, err := TransferAndWrapAcceptOwnership( buildTransfer, buildAccept, state.BurnMintTokenPools[tokenPoolMetadata], @@ -445,7 +445,7 @@ func transferOwnershipLockReleaseTokenPools( return ix, nil } - tx, err := transferAndWrapAcceptOwnership( + tx, err := TransferAndWrapAcceptOwnership( buildTransfer, buildAccept, state.LockReleaseTokenPools[tokenPoolMetadata], @@ -513,7 +513,7 @@ func transferOwnershipCCTPTokenPools( return ix, nil } - tx, err := transferAndWrapAcceptOwnership( + tx, err := TransferAndWrapAcceptOwnership( buildTransfer, buildAccept, state.CCTPTokenPool, diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/README.md b/deployment/ccip/shared/bindings/signer_registry_solana/README.md new file mode 100644 index 00000000000..d70523853b3 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/README.md @@ -0,0 +1,6 @@ +# IMPORTANT + +Despite the majority of this code being autogenerated, manual intervention was necessary to work around issues in the bindings. If you intend to regenerate the bindings, remember to: + +* Manually maintain the `program_id` to correspond to the one you intend to deploy. +* Ensure that instructions without arguments have their discriminator correctly serialized into the instruction data. diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/accounts.go b/deployment/ccip/shared/bindings/signer_registry_solana/accounts.go new file mode 100644 index 00000000000..6e796788b17 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/accounts.go @@ -0,0 +1,69 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains parsers for the accounts defined in the IDL. + +package signer_registry_solana + +import ( + "fmt" + binary "github.com/gagliardetto/binary" +) + +func ParseAnyAccount(accountData []byte) (any, error) { + decoder := binary.NewBorshDecoder(accountData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek account discriminator: %w", err) + } + switch discriminator { + case Account_Config: + value := new(Config) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal account as Config: %w", err) + } + return value, nil + case Account_Signers: + value := new(Signers) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal account as Signers: %w", err) + } + return value, nil + default: + return nil, fmt.Errorf("unknown discriminator: %s", binary.FormatDiscriminator(discriminator)) + } +} + +func ParseAccount_Config(accountData []byte) (*Config, error) { + decoder := binary.NewBorshDecoder(accountData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Account_Config { + return nil, fmt.Errorf("expected discriminator %v, got %s", Account_Config, binary.FormatDiscriminator(discriminator)) + } + acc := new(Config) + err = acc.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal account of type Config: %w", err) + } + return acc, nil +} + +func ParseAccount_Signers(accountData []byte) (*Signers, error) { + decoder := binary.NewBorshDecoder(accountData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Account_Signers { + return nil, fmt.Errorf("expected discriminator %v, got %s", Account_Signers, binary.FormatDiscriminator(discriminator)) + } + acc := new(Signers) + err = acc.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal account of type Signers: %w", err) + } + return acc, nil +} diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/constants.go b/deployment/ccip/shared/bindings/signer_registry_solana/constants.go new file mode 100644 index 00000000000..70899be1642 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/constants.go @@ -0,0 +1,9 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains constants. + +package signer_registry_solana + +// Constants defined in the IDL: + +var CONFIG_SEED = []byte{99, 111, 110, 102, 105, 103} +var SIGNERS_SEED = []byte{115, 105, 103, 110, 101, 114, 115} diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/discriminators.go b/deployment/ccip/shared/bindings/signer_registry_solana/discriminators.go new file mode 100644 index 00000000000..0037dafe9c1 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/discriminators.go @@ -0,0 +1,31 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains the discriminators for accounts and events defined in the IDL. + +package signer_registry_solana + +// Account discriminators +var ( + Account_Config = [8]byte{155, 12, 170, 224, 30, 250, 204, 130} + Account_Signers = [8]byte{252, 201, 210, 52, 28, 254, 51, 99} +) + +// Event discriminators +var ( + Event_Initialized = [8]byte{208, 213, 115, 98, 115, 82, 201, 209} + Event_NewOwnerProposed = [8]byte{232, 255, 42, 123, 189, 63, 32, 243} + Event_OwnerChanged = [8]byte{34, 223, 103, 225, 239, 231, 51, 53} + Event_SignerAdded = [8]byte{207, 55, 247, 68, 22, 112, 95, 45} + Event_SignerModified = [8]byte{56, 67, 72, 51, 67, 134, 60, 40} + Event_SignerRemoved = [8]byte{61, 29, 198, 197, 109, 190, 201, 233} +) + +// Instruction discriminators +var ( + Instruction_AcceptOwnership = [8]byte{172, 23, 43, 13, 238, 213, 85, 150} + Instruction_AddSigner = [8]byte{76, 104, 61, 51, 179, 139, 47, 222} + Instruction_Initialize = [8]byte{175, 175, 109, 31, 13, 152, 155, 237} + Instruction_PromoteSignerAddress = [8]byte{215, 125, 23, 192, 21, 59, 82, 223} + Instruction_ProposeNewOwner = [8]byte{105, 155, 71, 12, 128, 9, 232, 73} + Instruction_RemoveSigner = [8]byte{212, 32, 97, 47, 61, 67, 184, 141} + Instruction_SetSignerNewAddress = [8]byte{81, 127, 107, 53, 47, 95, 242, 251} +) diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/doc.go b/deployment/ccip/shared/bindings/signer_registry_solana/doc.go new file mode 100644 index 00000000000..0b8ba4dce79 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/doc.go @@ -0,0 +1,7 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains documentation and example usage for the generated code. + +package signer_registry_solana + +// No documentation available from the IDL. +// Please refer to the IDL source or the program documentation for more information. diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/errors.go b/deployment/ccip/shared/bindings/signer_registry_solana/errors.go new file mode 100644 index 00000000000..15b02b38206 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/errors.go @@ -0,0 +1,4 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains errors. + +package signer_registry_solana diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/events.go b/deployment/ccip/shared/bindings/signer_registry_solana/events.go new file mode 100644 index 00000000000..8af13bde518 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/events.go @@ -0,0 +1,165 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains parsers for the events defined in the IDL. + +package signer_registry_solana + +import ( + "fmt" + binary "github.com/gagliardetto/binary" +) + +func ParseAnyEvent(eventData []byte) (any, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek event discriminator: %w", err) + } + switch discriminator { + case Event_Initialized: + value := new(Initialized) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event as Initialized: %w", err) + } + return value, nil + case Event_NewOwnerProposed: + value := new(NewOwnerProposed) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event as NewOwnerProposed: %w", err) + } + return value, nil + case Event_OwnerChanged: + value := new(OwnerChanged) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event as OwnerChanged: %w", err) + } + return value, nil + case Event_SignerAdded: + value := new(SignerAdded) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event as SignerAdded: %w", err) + } + return value, nil + case Event_SignerModified: + value := new(SignerModified) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event as SignerModified: %w", err) + } + return value, nil + case Event_SignerRemoved: + value := new(SignerRemoved) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event as SignerRemoved: %w", err) + } + return value, nil + default: + return nil, fmt.Errorf("unknown discriminator: %s", binary.FormatDiscriminator(discriminator)) + } +} + +func ParseEvent_Initialized(eventData []byte) (*Initialized, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Event_Initialized { + return nil, fmt.Errorf("expected discriminator %v, got %s", Event_Initialized, binary.FormatDiscriminator(discriminator)) + } + event := new(Initialized) + err = event.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event of type Initialized: %w", err) + } + return event, nil +} + +func ParseEvent_NewOwnerProposed(eventData []byte) (*NewOwnerProposed, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Event_NewOwnerProposed { + return nil, fmt.Errorf("expected discriminator %v, got %s", Event_NewOwnerProposed, binary.FormatDiscriminator(discriminator)) + } + event := new(NewOwnerProposed) + err = event.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event of type NewOwnerProposed: %w", err) + } + return event, nil +} + +func ParseEvent_OwnerChanged(eventData []byte) (*OwnerChanged, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Event_OwnerChanged { + return nil, fmt.Errorf("expected discriminator %v, got %s", Event_OwnerChanged, binary.FormatDiscriminator(discriminator)) + } + event := new(OwnerChanged) + err = event.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event of type OwnerChanged: %w", err) + } + return event, nil +} + +func ParseEvent_SignerAdded(eventData []byte) (*SignerAdded, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Event_SignerAdded { + return nil, fmt.Errorf("expected discriminator %v, got %s", Event_SignerAdded, binary.FormatDiscriminator(discriminator)) + } + event := new(SignerAdded) + err = event.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event of type SignerAdded: %w", err) + } + return event, nil +} + +func ParseEvent_SignerModified(eventData []byte) (*SignerModified, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Event_SignerModified { + return nil, fmt.Errorf("expected discriminator %v, got %s", Event_SignerModified, binary.FormatDiscriminator(discriminator)) + } + event := new(SignerModified) + err = event.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event of type SignerModified: %w", err) + } + return event, nil +} + +func ParseEvent_SignerRemoved(eventData []byte) (*SignerRemoved, error) { + decoder := binary.NewBorshDecoder(eventData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Event_SignerRemoved { + return nil, fmt.Errorf("expected discriminator %v, got %s", Event_SignerRemoved, binary.FormatDiscriminator(discriminator)) + } + event := new(SignerRemoved) + err = event.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal event of type SignerRemoved: %w", err) + } + return event, nil +} diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/fetchers.go b/deployment/ccip/shared/bindings/signer_registry_solana/fetchers.go new file mode 100644 index 00000000000..1ebc856d39f --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/fetchers.go @@ -0,0 +1,4 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains fetcher functions. + +package signer_registry_solana diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/instructions.go b/deployment/ccip/shared/bindings/signer_registry_solana/instructions.go new file mode 100644 index 00000000000..f921e0acbd6 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/instructions.go @@ -0,0 +1,363 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains instructions. + +package signer_registry_solana + +import ( + "bytes" + "fmt" + errors "github.com/gagliardetto/anchor-go/errors" + binary "github.com/gagliardetto/binary" + solanago "github.com/gagliardetto/solana-go" +) + +// Builds a "accept_ownership" instruction. +// Accepts the ownership transfer as the proposed new owner. // // This instruction completes the two-step ownership transfer process. It must be called // by the account that was previously proposed as the new owner through the `propose_new_owner` // instruction. Once executed, the proposed owner becomes the new program owner with full // authority to manage signers and configuration. // // # Parameters // // * `ctx` - The context containing all accounts required for accepting ownership: // - `authority`: Must be the account that was proposed as the new owner in the Config. // - `config`: The mutable Config account that will be updated with the new owner. // // # Returns // // Returns `Ok(())` when ownership is successfully transferred. // // # Errors // // * `InvalidProposedOwner` - The authority is not the currently proposed owner, or // no owner has been proposed. +func NewAcceptOwnershipInstruction( + authorityAccount solanago.PublicKey, + configAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + accounts__ := solanago.AccountMetaSlice{} + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_AcceptOwnership[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + + // Add the accounts to the instruction. + { + // Account 0 "authority": Read-only, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, false, true)) + // Account 1 "config": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, true, false)) + // Account 2 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 3 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "add_signer" instruction. +// This instruction allows the program owner (configured in the `owner` field of the Config // PDA) to add a new signer to the authorized signers list. // // # Parameters // // * `ctx` - The context containing all accounts required for adding a signer: // - `authority`: Must be the current program owner as stored in the Config account. // - `config`: The Config account to verify owner authorization. // - `signers`: The mutable Signers account where the new signer will be added. // // * `signer_evm_address` - A 20-byte array representing the EVM address of the new signer. // Must not be the zero address ([0; 20]). // // # Returns // // Returns `Ok(())` when the signer is successfully added to the list. // // # Errors // // * `Unauthorized` - The authority is not the current program owner. // * `TooManySigners` - The signers list has reached its maximum capacity of 20 signers. // * `InvalidSigner` - The provided EVM address is the zero address ([0; 20]). // * `DuplicateSigner` - The provided EVM address already exists in the signers list // (either as a primary address or as a new address in key rotation). +func NewAddSignerInstruction( + // Params: + signerEvmAddressParam [20]uint8, + + // Accounts: + authorityAccount solanago.PublicKey, + configAccount solanago.PublicKey, + signersAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_AddSigner[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + { + // Serialize `signerEvmAddressParam`: + err = enc__.Encode(signerEvmAddressParam) + if err != nil { + return nil, errors.NewField("signerEvmAddressParam", err) + } + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "authority": Read-only, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, false, true)) + // Account 1 "config": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, false, false)) + // Account 2 "signers": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(signersAccount, true, false)) + // Account 3 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 4 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "initialize" instruction. +// Initializes the CCIP Signer Registry program with initial configuration and owner. // // This instruction must be called once after program deployment to set up the program's // state accounts. It creates both the Config and Signers accounts with their respective // Program Derived Addresses (PDAs). The authority account signing this transaction will // be set as the program owner. // // # Parameters // // * `ctx` - The context containing all accounts required for initialization: // - `authority`: The signer who is deploying/initializing the program. This account will // become the program owner with exclusive authority to manage signers and propose // ownership transfers. When the `init_guard` feature is enabled, this must be the // program's upgrade authority. // - `system_program`: The Solana system program for account creation. // - `config`: The Config PDA that will store the program owner and proposed owner. // - `signers`: The Signers PDA that will store the list of authorized CCIP signers. // - `program` (optional): The program account itself (when `init_guard` feature is enabled). // - `program_data` (optional): The program data account (when `init_guard` feature is enabled). // // # Returns // // Returns `Ok(())` on successful initialization. // // # Errors // // * `InvalidInitializer` - When `init_guard` feature is enabled and the authority is not // the program's upgrade authority. +func NewInitializeInstruction( + authorityAccount solanago.PublicKey, + systemProgramAccount solanago.PublicKey, + configAccount solanago.PublicKey, + signersAccount solanago.PublicKey, + programForVerificationAccount solanago.PublicKey, + programDataAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + accounts__ := solanago.AccountMetaSlice{} + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_Initialize[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + + // Add the accounts to the instruction. + { + // Account 0 "authority": Writable, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, true, true)) + // Account 1 "system_program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(systemProgramAccount, false, false)) + // Account 2 "config": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, true, false)) + // Account 3 "signers": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(signersAccount, true, false)) + // Account 4 "program_for_verification": Read-only, Non-signer, Required, Address: S1GN4jus9XzKVVnoHqfkjo1GN8bX46gjXZQwsdGBPHE + accounts__.Append(solanago.NewAccountMeta(programForVerificationAccount, false, false)) + // Account 5 "program_data": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programDataAccount, false, false)) + // Account 6 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 7 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "promote_signer_address" instruction. +// This instruction allows the program owner (configured in the `owner` field of the Config // PDA) to complete the key rotation by promoting the new address to become the primary address. // // This instruction finalizes the blue/green key rotation process by replacing the old address // with the new address that was previously set. After this operation, only the new address // will be valid for the signer, and the old address will no longer be accepted. // // This should only be called after ensuring that all in-flight messages signed with the old // key have been processed, as the old address will no longer be valid after rotation. // // # Parameters // // * `ctx` - The context containing all accounts required for rotating the address: // - `authority`: Must be the current program owner as stored in the Config account. // - `config`: The Config account to verify owner authorization. // - `signers`: The mutable Signers account where the address rotation will occur. // // * `signer_evm_address` - A 20-byte array representing the EVM address to promote. // This can be either the current primary (blue) address or the new (green) address // of the signer whose key rotation should be completed. The signer's new address // will become the primary address after this operation. // // # Returns // // Returns `Ok(())` when the address rotation is successfully completed. // // # Errors // // * `Unauthorized` - The authority is not the current program owner. // * `NoMatchingSignerFound` - The specified EVM address does not exist in the signers list // (neither as a primary address nor as a new address). // * `MissingNewAddress` - The signer does not have a new address set for rotation. // You must call `set_signer_new_address` before attempting to promote. +func NewPromoteSignerAddressInstruction( + // Params: + signerEvmAddressParam [20]uint8, + + // Accounts: + authorityAccount solanago.PublicKey, + configAccount solanago.PublicKey, + signersAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_PromoteSignerAddress[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + { + // Serialize `signerEvmAddressParam`: + err = enc__.Encode(signerEvmAddressParam) + if err != nil { + return nil, errors.NewField("signerEvmAddressParam", err) + } + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "authority": Read-only, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, false, true)) + // Account 1 "config": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, false, false)) + // Account 2 "signers": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(signersAccount, true, false)) + // Account 3 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 4 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "propose_new_owner" instruction. +// Proposes a new owner for the CCIP Signer Registry program ownership transfer. // // This instruction initiates a two-step ownership transfer process. The current owner // proposes a new owner, who must then explicitly accept the ownership using the // `accept_ownership` instruction. This two-step process prevents accidental transfers // and ensures the new owner is ready to assume responsibilities. // // # Parameters // // * `ctx` - The context containing all accounts required for proposing a new owner: // - `authority`: Must be the current program owner as stored in the Config account. // - `config`: The mutable Config account where the proposed owner will be stored. // // * `new_owner` - The public key of the proposed new owner. This account will need to // call `accept_ownership` to complete the transfer. The proposed owner can be changed // by calling this instruction again before acceptance. // // # Returns // // Returns `Ok(())` when the new owner is successfully proposed. // // # Errors // // * `Unauthorized` - The authority is not the current program owner. +func NewProposeNewOwnerInstruction( + // Params: + newOwnerParam solanago.PublicKey, + + // Accounts: + authorityAccount solanago.PublicKey, + configAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_ProposeNewOwner[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + { + // Serialize `newOwnerParam`: + err = enc__.Encode(newOwnerParam) + if err != nil { + return nil, errors.NewField("newOwnerParam", err) + } + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "authority": Read-only, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, false, true)) + // Account 1 "config": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, true, false)) + // Account 2 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 3 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "remove_signer" instruction. +// This instruction allows the program owner (configured in the `owner` field of the Config // PDA) to remove a signer from the authorized signers list. // // # Parameters // // * `ctx` - The context containing all accounts required for removing a signer: // - `authority`: Must be the current program owner as stored in the Config account. // - `config`: The Config account to verify owner authorization. // - `signers`: The mutable Signers account from which the signer will be removed. // // * `signer_evm_address` - A 20-byte array representing the EVM address of the signer to remove. // This can match either a primary address or a new address that was set for rotation. // // # Returns // // Returns `Ok(())` when the signer is successfully removed from the list. // // # Errors // // * `Unauthorized` - The authority is not the current program owner. // * `NoMatchingSignerFound` - The specified EVM address does not exist in the signers list // (neither as a primary address nor as a new address in rotation). +func NewRemoveSignerInstruction( + // Params: + signerEvmAddressParam [20]uint8, + + // Accounts: + authorityAccount solanago.PublicKey, + configAccount solanago.PublicKey, + signersAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_RemoveSigner[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + { + // Serialize `signerEvmAddressParam`: + err = enc__.Encode(signerEvmAddressParam) + if err != nil { + return nil, errors.NewField("signerEvmAddressParam", err) + } + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "authority": Read-only, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, false, true)) + // Account 1 "config": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, false, false)) + // Account 2 "signers": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(signersAccount, true, false)) + // Account 3 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 4 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "set_signer_new_address" instruction. +// This instruction allows the program owner (configured in the `owner` field of the Config // PDA) to set a new address for a signer to enable blue/green key rotation. // // This instruction initiates a key rotation process for a signer by setting a new EVM address // while keeping the old address active. This implements a blue/green deployment pattern where // both addresses remain valid during the transition period. Off-chain, the signer will start // exclusively signing with the new key, but on-chain, signatures from both addresses are // accepted until the rotation is completed. // // **Important Note on Key Re-use**: The registry allows rotating a signer back to a previously // used address. For example, an address retired at time t10 can be reinstated at time t100. // The SignerRegistry itself does not prevent key re-use. Consuming applications must account // for this behavior to prevent potential issues such as historic signatures being treated as // valid again. // // # Parameters // // * `ctx` - The context containing all accounts required for setting a new address: // - `authority`: Must be the current program owner as stored in the Config account. // - `config`: The Config account to verify owner authorization. // - `signers`: The mutable Signers account where the new address will be set. // // * `signer_evm_address` - A 20-byte array representing the EVM address of the signer // that needs key rotation. This can match either an existing primary address or a // previously set new address (green address) in the list. // // * `signer_new_evm_address` - A 20-byte array representing the new EVM address that will // replace the old address after rotation is complete. This address must not already exist // in the signers list. If this is the zero address ([0; 20]), the new EVM address will be // cleared. // // # Returns // // Returns `Ok(())` when the new address is successfully set for the signer. This is idempotent, // so it will return `Ok(())` even if the operation is a NOOP. // // # Errors // // * `Unauthorized` - The authority is not the current program owner. // * `NoMatchingSignerFound` - The signer EVM address does not exist in the signers list // (neither as a primary address nor as a new address). // * `DuplicateSigner` - The new EVM address already exists in the signers list // (either as a primary address or as another signer's new address). +func NewSetSignerNewAddressInstruction( + // Params: + signerEvmAddressParam [20]uint8, + signerNewEvmAddressParam [20]uint8, + + // Accounts: + authorityAccount solanago.PublicKey, + configAccount solanago.PublicKey, + signersAccount solanago.PublicKey, + eventAuthorityAccount solanago.PublicKey, + programAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_SetSignerNewAddress[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + { + // Serialize `signerEvmAddressParam`: + err = enc__.Encode(signerEvmAddressParam) + if err != nil { + return nil, errors.NewField("signerEvmAddressParam", err) + } + // Serialize `signerNewEvmAddressParam`: + err = enc__.Encode(signerNewEvmAddressParam) + if err != nil { + return nil, errors.NewField("signerNewEvmAddressParam", err) + } + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "authority": Read-only, Signer, Required + accounts__.Append(solanago.NewAccountMeta(authorityAccount, false, true)) + // Account 1 "config": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(configAccount, false, false)) + // Account 2 "signers": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(signersAccount, true, false)) + // Account 3 "event_authority": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(eventAuthorityAccount, false, false)) + // Account 4 "program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(programAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/program-id.go b/deployment/ccip/shared/bindings/signer_registry_solana/program-id.go new file mode 100644 index 00000000000..63c252ec6a0 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/program-id.go @@ -0,0 +1,8 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains the program ID. + +package signer_registry_solana + +import solanago "github.com/gagliardetto/solana-go" + +var ProgramID = solanago.MustPublicKeyFromBase58("S1GN4jus9XzKVVnoHqfkjo1GN8bX46gjXZQwsdGBPHE") diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/tests_test.go b/deployment/ccip/shared/bindings/signer_registry_solana/tests_test.go new file mode 100644 index 00000000000..3fbd18943bb --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/tests_test.go @@ -0,0 +1,4 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains tests. + +package signer_registry_solana diff --git a/deployment/ccip/shared/bindings/signer_registry_solana/types.go b/deployment/ccip/shared/bindings/signer_registry_solana/types.go new file mode 100644 index 00000000000..963a3619807 --- /dev/null +++ b/deployment/ccip/shared/bindings/signer_registry_solana/types.go @@ -0,0 +1,572 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. +// This file contains parsers for the types defined in the IDL. + +package signer_registry_solana + +import ( + "bytes" + "fmt" + errors "github.com/gagliardetto/anchor-go/errors" + binary "github.com/gagliardetto/binary" + solanago "github.com/gagliardetto/solana-go" +) + +type Config struct { + Owner solanago.PublicKey `json:"owner"` + ProposedOwner *solanago.PublicKey `bin:"optional" json:"proposedOwner,omitempty"` + ConfigBump uint8 `json:"configBump"` + SignersBump uint8 `json:"signersBump"` +} + +func (obj Config) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `Owner`: + err = encoder.Encode(obj.Owner) + if err != nil { + return errors.NewField("Owner", err) + } + // Serialize `ProposedOwner` (optional): + { + if obj.ProposedOwner == nil { + err = encoder.WriteOption(false) + if err != nil { + return errors.NewOption("ProposedOwner", fmt.Errorf("error while encoding optionality: %w", err)) + } + } else { + err = encoder.WriteOption(true) + if err != nil { + return errors.NewOption("ProposedOwner", fmt.Errorf("error while encoding optionality: %w", err)) + } + err = encoder.Encode(obj.ProposedOwner) + if err != nil { + return errors.NewField("ProposedOwner", err) + } + } + } + // Serialize `ConfigBump`: + err = encoder.Encode(obj.ConfigBump) + if err != nil { + return errors.NewField("ConfigBump", err) + } + // Serialize `SignersBump`: + err = encoder.Encode(obj.SignersBump) + if err != nil { + return errors.NewField("SignersBump", err) + } + return nil +} + +func (obj Config) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding Config: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *Config) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `Owner`: + err = decoder.Decode(&obj.Owner) + if err != nil { + return errors.NewField("Owner", err) + } + // Deserialize `ProposedOwner` (optional): + { + ok, err := decoder.ReadOption() + if err != nil { + return errors.NewOption("ProposedOwner", fmt.Errorf("error while reading optionality: %w", err)) + } + if ok { + err = decoder.Decode(&obj.ProposedOwner) + if err != nil { + return errors.NewField("ProposedOwner", err) + } + } + } + // Deserialize `ConfigBump`: + err = decoder.Decode(&obj.ConfigBump) + if err != nil { + return errors.NewField("ConfigBump", err) + } + // Deserialize `SignersBump`: + err = decoder.Decode(&obj.SignersBump) + if err != nil { + return errors.NewField("SignersBump", err) + } + return nil +} + +func (obj *Config) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling Config: %w", err) + } + return nil +} + +func UnmarshalConfig(buf []byte) (*Config, error) { + obj := new(Config) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type Initialized struct { + Owner solanago.PublicKey `json:"owner"` +} + +func (obj Initialized) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `Owner`: + err = encoder.Encode(obj.Owner) + if err != nil { + return errors.NewField("Owner", err) + } + return nil +} + +func (obj Initialized) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding Initialized: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *Initialized) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `Owner`: + err = decoder.Decode(&obj.Owner) + if err != nil { + return errors.NewField("Owner", err) + } + return nil +} + +func (obj *Initialized) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling Initialized: %w", err) + } + return nil +} + +func UnmarshalInitialized(buf []byte) (*Initialized, error) { + obj := new(Initialized) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type NewOwnerProposed struct { + CurrentOwner solanago.PublicKey `json:"currentOwner"` + ProposedOwner solanago.PublicKey `json:"proposedOwner"` +} + +func (obj NewOwnerProposed) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `CurrentOwner`: + err = encoder.Encode(obj.CurrentOwner) + if err != nil { + return errors.NewField("CurrentOwner", err) + } + // Serialize `ProposedOwner`: + err = encoder.Encode(obj.ProposedOwner) + if err != nil { + return errors.NewField("ProposedOwner", err) + } + return nil +} + +func (obj NewOwnerProposed) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding NewOwnerProposed: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *NewOwnerProposed) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `CurrentOwner`: + err = decoder.Decode(&obj.CurrentOwner) + if err != nil { + return errors.NewField("CurrentOwner", err) + } + // Deserialize `ProposedOwner`: + err = decoder.Decode(&obj.ProposedOwner) + if err != nil { + return errors.NewField("ProposedOwner", err) + } + return nil +} + +func (obj *NewOwnerProposed) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling NewOwnerProposed: %w", err) + } + return nil +} + +func UnmarshalNewOwnerProposed(buf []byte) (*NewOwnerProposed, error) { + obj := new(NewOwnerProposed) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type OwnerChanged struct { + OldOwner solanago.PublicKey `json:"oldOwner"` + NewOwner solanago.PublicKey `json:"newOwner"` +} + +func (obj OwnerChanged) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `OldOwner`: + err = encoder.Encode(obj.OldOwner) + if err != nil { + return errors.NewField("OldOwner", err) + } + // Serialize `NewOwner`: + err = encoder.Encode(obj.NewOwner) + if err != nil { + return errors.NewField("NewOwner", err) + } + return nil +} + +func (obj OwnerChanged) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding OwnerChanged: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *OwnerChanged) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `OldOwner`: + err = decoder.Decode(&obj.OldOwner) + if err != nil { + return errors.NewField("OldOwner", err) + } + // Deserialize `NewOwner`: + err = decoder.Decode(&obj.NewOwner) + if err != nil { + return errors.NewField("NewOwner", err) + } + return nil +} + +func (obj *OwnerChanged) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling OwnerChanged: %w", err) + } + return nil +} + +func UnmarshalOwnerChanged(buf []byte) (*OwnerChanged, error) { + obj := new(OwnerChanged) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type Signer struct { + EvmAddress [20]uint8 `json:"evmAddress"` + NewEvmAddress *[20]uint8 `bin:"optional" json:"newEvmAddress,omitempty"` +} + +func (obj Signer) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `EvmAddress`: + err = encoder.Encode(obj.EvmAddress) + if err != nil { + return errors.NewField("EvmAddress", err) + } + // Serialize `NewEvmAddress` (optional): + { + if obj.NewEvmAddress == nil { + err = encoder.WriteOption(false) + if err != nil { + return errors.NewOption("NewEvmAddress", fmt.Errorf("error while encoding optionality: %w", err)) + } + } else { + err = encoder.WriteOption(true) + if err != nil { + return errors.NewOption("NewEvmAddress", fmt.Errorf("error while encoding optionality: %w", err)) + } + err = encoder.Encode(obj.NewEvmAddress) + if err != nil { + return errors.NewField("NewEvmAddress", err) + } + } + } + return nil +} + +func (obj Signer) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding Signer: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *Signer) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `EvmAddress`: + err = decoder.Decode(&obj.EvmAddress) + if err != nil { + return errors.NewField("EvmAddress", err) + } + // Deserialize `NewEvmAddress` (optional): + { + ok, err := decoder.ReadOption() + if err != nil { + return errors.NewOption("NewEvmAddress", fmt.Errorf("error while reading optionality: %w", err)) + } + if ok { + err = decoder.Decode(&obj.NewEvmAddress) + if err != nil { + return errors.NewField("NewEvmAddress", err) + } + } + } + return nil +} + +func (obj *Signer) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling Signer: %w", err) + } + return nil +} + +func UnmarshalSigner(buf []byte) (*Signer, error) { + obj := new(Signer) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type SignerAdded struct { + Signer Signer `json:"signer"` +} + +func (obj SignerAdded) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `Signer`: + err = encoder.Encode(obj.Signer) + if err != nil { + return errors.NewField("Signer", err) + } + return nil +} + +func (obj SignerAdded) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding SignerAdded: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *SignerAdded) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `Signer`: + err = decoder.Decode(&obj.Signer) + if err != nil { + return errors.NewField("Signer", err) + } + return nil +} + +func (obj *SignerAdded) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling SignerAdded: %w", err) + } + return nil +} + +func UnmarshalSignerAdded(buf []byte) (*SignerAdded, error) { + obj := new(SignerAdded) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type SignerModified struct { + OldSigner Signer `json:"oldSigner"` + NewSigner Signer `json:"newSigner"` +} + +func (obj SignerModified) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `OldSigner`: + err = encoder.Encode(obj.OldSigner) + if err != nil { + return errors.NewField("OldSigner", err) + } + // Serialize `NewSigner`: + err = encoder.Encode(obj.NewSigner) + if err != nil { + return errors.NewField("NewSigner", err) + } + return nil +} + +func (obj SignerModified) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding SignerModified: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *SignerModified) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `OldSigner`: + err = decoder.Decode(&obj.OldSigner) + if err != nil { + return errors.NewField("OldSigner", err) + } + // Deserialize `NewSigner`: + err = decoder.Decode(&obj.NewSigner) + if err != nil { + return errors.NewField("NewSigner", err) + } + return nil +} + +func (obj *SignerModified) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling SignerModified: %w", err) + } + return nil +} + +func UnmarshalSignerModified(buf []byte) (*SignerModified, error) { + obj := new(SignerModified) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type SignerRemoved struct { + Signer Signer `json:"signer"` +} + +func (obj SignerRemoved) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `Signer`: + err = encoder.Encode(obj.Signer) + if err != nil { + return errors.NewField("Signer", err) + } + return nil +} + +func (obj SignerRemoved) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding SignerRemoved: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *SignerRemoved) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `Signer`: + err = decoder.Decode(&obj.Signer) + if err != nil { + return errors.NewField("Signer", err) + } + return nil +} + +func (obj *SignerRemoved) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling SignerRemoved: %w", err) + } + return nil +} + +func UnmarshalSignerRemoved(buf []byte) (*SignerRemoved, error) { + obj := new(SignerRemoved) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + +type Signers struct { + Signers []Signer `json:"signers"` +} + +func (obj Signers) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `Signers`: + err = encoder.Encode(obj.Signers) + if err != nil { + return errors.NewField("Signers", err) + } + return nil +} + +func (obj Signers) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding Signers: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *Signers) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `Signers`: + err = decoder.Decode(&obj.Signers) + if err != nil { + return errors.NewField("Signers", err) + } + return nil +} + +func (obj *Signers) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling Signers: %w", err) + } + return nil +} + +func UnmarshalSigners(buf []byte) (*Signers, error) { + obj := new(Signers) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} diff --git a/deployment/ccip/shared/stateview/solana/state.go b/deployment/ccip/shared/stateview/solana/state.go index f989e1d27a0..70cd333f32f 100644 --- a/deployment/ccip/shared/stateview/solana/state.go +++ b/deployment/ccip/shared/stateview/solana/state.go @@ -8,10 +8,13 @@ import ( "github.com/Masterminds/semver/v3" "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" "github.com/rs/zerolog/log" cldf_solana "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" + signer_registry "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry_solana" + solBurnMintTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v0_1_1/burnmint_token_pool" solOffRamp "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v0_1_1/ccip_offramp" solRouter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v0_1_1/ccip_router" @@ -634,6 +637,22 @@ func ValidateOwnershipSolana( if err := commonchangeset.ValidateOwnershipSolanaCommon(mcms, chain.DeployerKey.PublicKey(), timelockSignerPDA, programData.Config.Owner); err != nil { return fmt.Errorf("failed to validate ownership for cctp_token_pool: %w", err) } + case shared.SVMSignerRegistry: + configPda, _, _ := solana.FindProgramAddress([][]byte{[]byte("config")}, signer_registry.ProgramID) + data, err := GetAccountData(*e, &chain, configPda) + if err != nil { + return fmt.Errorf("failed to get config: %w", err) + } + + configAccount, err := signer_registry.ParseAccount_Config(data) + if err != nil { + return fmt.Errorf("failed to get config: %w", err) + } + fmt.Printf("%+v\n", configAccount) + + if err := commonchangeset.ValidateOwnershipSolanaCommon(mcms, chain.DeployerKey.PublicKey(), timelockSignerPDA, configAccount.Owner); err != nil { + return fmt.Errorf("failed to validate ownership for signer_registry at account %s: %w", configPda, err) + } default: return fmt.Errorf("unsupported contract type: %s", contractType) } @@ -743,3 +762,23 @@ func FindReceiverTargetAccount(receiverID solana.PublicKey) solana.PublicKey { receiverTargetAccount, _, _ := solana.FindProgramAddress([][]byte{[]byte("counter")}, receiverID) return receiverTargetAccount } + +func GetAccountData( + e cldf.Environment, + chain *cldf_solana.Chain, + account solana.PublicKey, + +) ([]byte, error) { + resp, err := chain.Client.GetAccountInfoWithOpts( + e.GetContext(), + account, + &rpc.GetAccountInfoOpts{ + Commitment: rpc.CommitmentFinalized, + DataSlice: nil, + }, + ) + if err != nil { + return nil, err + } + return resp.Value.Data.GetBinary(), nil +} diff --git a/deployment/ccip/shared/stateview/state.go b/deployment/ccip/shared/stateview/state.go index c4de2b4d2e1..2b346879f96 100644 --- a/deployment/ccip/shared/stateview/state.go +++ b/deployment/ccip/shared/stateview/state.go @@ -93,7 +93,7 @@ import ( "github.com/smartcontractkit/chainlink-evm/gethwrappers/shared/generated/initial/weth9" "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/burn_mint_with_external_minter_fast_transfer_token_pool" "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/hybrid_with_external_minter_fast_transfer_token_pool" - signer_registry "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry" + "github.com/smartcontractkit/chainlink/deployment/ccip/shared/bindings/signer_registry" ) const chainNotSupportedErr = "chain not supported" diff --git a/deployment/ccip/shared/types.go b/deployment/ccip/shared/types.go index 25ac8b2d17c..6262bffac41 100644 --- a/deployment/ccip/shared/types.go +++ b/deployment/ccip/shared/types.go @@ -76,6 +76,7 @@ var ( TokenPoolLookupTable deployment.ContractType = "TokenPoolLookupTable" CCTPTokenPool deployment.ContractType = "CCTPTokenPool" BPFUpgradeable deployment.ContractType = "BPFUpgradeable" + SVMSignerRegistry deployment.ContractType = "SVMSignerRegistry" // CLL Identifier CLLMetadata = "CLL" diff --git a/deployment/environment/memory/solana_chains.go b/deployment/environment/memory/solana_chains.go index cd111ae43f5..db8bbcd9e21 100644 --- a/deployment/environment/memory/solana_chains.go +++ b/deployment/environment/memory/solana_chains.go @@ -316,6 +316,12 @@ var SolanaProgramIDs = map[string]string{ "data_feeds_cache": "3kX63udXtYcsdj2737Wi2KGd2PhqiKPgAFAxstrjtRUa", } +// Not deployed as part of the other solana programs, as it has its unique +// repository. +var SolanaNonCcipProgramIDs = map[string]string{ + "ccip_signer_registry": "S1GN4jus9XzKVVnoHqfkjo1GN8bX46gjXZQwsdGBPHE", +} + // Populates datastore with the predeployed program addresses // pass map [programName]:ContractType of contracts to populate datastore with func PopulateDatastore(ds *datastore.MemoryAddressRefStore, contracts map[string]datastore.ContractType, version *semver.Version, qualifier string, chainSel uint64) error { diff --git a/deployment/go.mod b/deployment/go.mod index ff49cd2530c..94fa787d462 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -20,6 +20,7 @@ require ( github.com/deckarep/golang-set/v2 v2.6.0 github.com/ethereum/go-ethereum v1.16.2 github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b + github.com/gagliardetto/anchor-go v0.3.2 github.com/gagliardetto/binary v0.8.0 github.com/gagliardetto/metaplex-go v0.2.1 github.com/gagliardetto/solana-go v1.13.0 diff --git a/deployment/go.sum b/deployment/go.sum index 8753b39451f..27d492c7f07 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -394,6 +394,7 @@ github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8 github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -494,7 +495,10 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gagliardetto/anchor-go v0.3.2 h1:/nlAp3B6s4DBlNABWrQ5bA2zpsUGVDqBfwmbUUg4ChQ= +github.com/gagliardetto/anchor-go v0.3.2/go.mod h1:Jf9/DBNo6GsG6RguE4ZuJz+PZtypKg3iUpB++Oc6ynQ= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/binary v0.7.6/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -503,6 +507,7 @@ github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1: github.com/gagliardetto/metaplex-go v0.2.1 h1:NMBsgJe3I2avKZ39dfYQvXsGsr2BxUgARkA9LZ6szBg= github.com/gagliardetto/metaplex-go v0.2.1/go.mod h1:6ZLYBvlWcXktXQ/QcBJYRzKgK7Q3WgiGD7BjE7Zxpw4= github.com/gagliardetto/solana-go v1.4.0/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/solana-go v1.5.0/go.mod h1:1KFOW7mlR/TSjYFeLCYmfpSptRdNJMtpgChelKy2oU0= github.com/gagliardetto/solana-go v1.13.0 h1:uNzhjwdAdbq9xMaX2DF0MwXNMw6f8zdZ7JPBtkJG7Ig= github.com/gagliardetto/solana-go v1.13.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -1442,6 +1447,7 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA= +github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/deployment/solana_chain.go b/deployment/solana_chain.go index d9f2338a8d0..769837c937b 100644 --- a/deployment/solana_chain.go +++ b/deployment/solana_chain.go @@ -24,6 +24,7 @@ const ( KeystoneForwarderProgramName = "keystone_forwarder" CCTPTokenPoolProgramName = "cctp_token_pool" DataFeedsCacheProgramName = "data_feeds_cache" + BaseSignerRegistryProgramName = "ccip_signer_registry" ) // https://docs.google.com/document/d/1Fk76lOeyS2z2X6MokaNX_QTMFAn5wvSZvNXJluuNV1E/edit?tab=t.0#heading=h.uij286zaarkz @@ -39,6 +40,7 @@ var SolanaProgramBytes = map[string]int{ McmProgramName: 1 * 1024 * 1024, RMNRemoteProgramName: 3 * 1024 * 1024, CCTPTokenPoolProgramName: 3 * 1024 * 1024, + BaseSignerRegistryProgramName: 1 * 1024 * 1024, } // PROGRAM ID for Metaplex Metadata Program diff --git a/integration-tests/go.mod b/integration-tests/go.mod index e776d546f6a..46d2f97db2f 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -236,6 +236,7 @@ require ( github.com/ferranbt/fastssz v0.1.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gagliardetto/anchor-go v0.3.2 // indirect github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/metaplex-go v0.2.1 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index eaf1c5740c1..14827300515 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -464,6 +464,7 @@ github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8 github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -576,12 +577,15 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gagliardetto/anchor-go v0.3.2 h1:/nlAp3B6s4DBlNABWrQ5bA2zpsUGVDqBfwmbUUg4ChQ= +github.com/gagliardetto/anchor-go v0.3.2/go.mod h1:Jf9/DBNo6GsG6RguE4ZuJz+PZtypKg3iUpB++Oc6ynQ= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1:513DXpQPzeRo7d4dsCP3xO3XI8hgvruMl9njxyQeraQ= github.com/gagliardetto/metaplex-go v0.2.1 h1:NMBsgJe3I2avKZ39dfYQvXsGsr2BxUgARkA9LZ6szBg= github.com/gagliardetto/metaplex-go v0.2.1/go.mod h1:6ZLYBvlWcXktXQ/QcBJYRzKgK7Q3WgiGD7BjE7Zxpw4= github.com/gagliardetto/solana-go v1.4.0/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/solana-go v1.5.0/go.mod h1:1KFOW7mlR/TSjYFeLCYmfpSptRdNJMtpgChelKy2oU0= github.com/gagliardetto/solana-go v1.13.0 h1:uNzhjwdAdbq9xMaX2DF0MwXNMw6f8zdZ7JPBtkJG7Ig= github.com/gagliardetto/solana-go v1.13.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -1699,6 +1703,7 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA= +github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 6f2032391a9..b314d65250a 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -212,6 +212,7 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gagliardetto/anchor-go v0.3.2 // indirect github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/metaplex-go v0.2.1 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 623f99fbc69..4b64f9ed723 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -442,6 +442,7 @@ github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8 github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -555,7 +556,10 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gagliardetto/anchor-go v0.3.2 h1:/nlAp3B6s4DBlNABWrQ5bA2zpsUGVDqBfwmbUUg4ChQ= +github.com/gagliardetto/anchor-go v0.3.2/go.mod h1:Jf9/DBNo6GsG6RguE4ZuJz+PZtypKg3iUpB++Oc6ynQ= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/binary v0.7.6/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -564,6 +568,7 @@ github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1: github.com/gagliardetto/metaplex-go v0.2.1 h1:NMBsgJe3I2avKZ39dfYQvXsGsr2BxUgARkA9LZ6szBg= github.com/gagliardetto/metaplex-go v0.2.1/go.mod h1:6ZLYBvlWcXktXQ/QcBJYRzKgK7Q3WgiGD7BjE7Zxpw4= github.com/gagliardetto/solana-go v1.4.0/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/solana-go v1.5.0/go.mod h1:1KFOW7mlR/TSjYFeLCYmfpSptRdNJMtpgChelKy2oU0= github.com/gagliardetto/solana-go v1.13.0 h1:uNzhjwdAdbq9xMaX2DF0MwXNMw6f8zdZ7JPBtkJG7Ig= github.com/gagliardetto/solana-go v1.13.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -1677,6 +1682,7 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA= +github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index e5d25926632..41e60352bbc 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -226,6 +226,7 @@ require ( github.com/fvbommel/sortorder v1.1.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gagliardetto/anchor-go v0.3.2 // indirect github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/metaplex-go v0.2.1 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index 040d677ee2f..e072215b653 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -468,6 +468,7 @@ github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8 github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -601,7 +602,10 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gagliardetto/anchor-go v0.3.2 h1:/nlAp3B6s4DBlNABWrQ5bA2zpsUGVDqBfwmbUUg4ChQ= +github.com/gagliardetto/anchor-go v0.3.2/go.mod h1:Jf9/DBNo6GsG6RguE4ZuJz+PZtypKg3iUpB++Oc6ynQ= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/binary v0.7.6/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -610,6 +614,7 @@ github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1: github.com/gagliardetto/metaplex-go v0.2.1 h1:NMBsgJe3I2avKZ39dfYQvXsGsr2BxUgARkA9LZ6szBg= github.com/gagliardetto/metaplex-go v0.2.1/go.mod h1:6ZLYBvlWcXktXQ/QcBJYRzKgK7Q3WgiGD7BjE7Zxpw4= github.com/gagliardetto/solana-go v1.4.0/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/solana-go v1.5.0/go.mod h1:1KFOW7mlR/TSjYFeLCYmfpSptRdNJMtpgChelKy2oU0= github.com/gagliardetto/solana-go v1.13.0 h1:uNzhjwdAdbq9xMaX2DF0MwXNMw6f8zdZ7JPBtkJG7Ig= github.com/gagliardetto/solana-go v1.13.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -1697,6 +1702,7 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA= +github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index fd96cd7c212..8dfb71d1810 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -248,6 +248,7 @@ require ( github.com/fvbommel/sortorder v1.1.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gagliardetto/anchor-go v0.3.2 // indirect github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/metaplex-go v0.2.1 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index dd7ae902032..1f7d599025b 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -515,6 +515,7 @@ github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8 github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -665,7 +666,10 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gagliardetto/anchor-go v0.3.2 h1:/nlAp3B6s4DBlNABWrQ5bA2zpsUGVDqBfwmbUUg4ChQ= +github.com/gagliardetto/anchor-go v0.3.2/go.mod h1:Jf9/DBNo6GsG6RguE4ZuJz+PZtypKg3iUpB++Oc6ynQ= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/binary v0.7.6/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -674,6 +678,7 @@ github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1: github.com/gagliardetto/metaplex-go v0.2.1 h1:NMBsgJe3I2avKZ39dfYQvXsGsr2BxUgARkA9LZ6szBg= github.com/gagliardetto/metaplex-go v0.2.1/go.mod h1:6ZLYBvlWcXktXQ/QcBJYRzKgK7Q3WgiGD7BjE7Zxpw4= github.com/gagliardetto/solana-go v1.4.0/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/solana-go v1.5.0/go.mod h1:1KFOW7mlR/TSjYFeLCYmfpSptRdNJMtpgChelKy2oU0= github.com/gagliardetto/solana-go v1.13.0 h1:uNzhjwdAdbq9xMaX2DF0MwXNMw6f8zdZ7JPBtkJG7Ig= github.com/gagliardetto/solana-go v1.13.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -1908,6 +1913,7 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI= github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA= +github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=