Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solana ccip verified builds #16711

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 120 additions & 13 deletions deployment/ccip/changeset/solana/cs_build_solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"regexp"

"github.com/smartcontractkit/chainlink/deployment"
ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
csState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state"
"github.com/smartcontractkit/chainlink/deployment/common/types"
)

Expand Down Expand Up @@ -137,14 +139,21 @@
}

// Build the project with Anchor
func buildProject(e deployment.Environment, testRouter bool) error {
func buildProject(e deployment.Environment, testRouter bool, verifiedBuild bool) error {
solanaDir := filepath.Join(cloneDir, anchorDir, "..")
e.Logger.Debugw("Building project", "solanaDir", solanaDir)
cmd := "make"
args := []string{"docker-build-contracts"}
if testRouter {
args = append(args, "ANCHOR_BUILD_ARGS=-p ccip_router")
if verifiedBuild {
cmd = "solana-verify"
args = []string{"build"}
solanaDir = filepath.Join(cloneDir, anchorDir)
} else {

Check failure on line 151 in deployment/ccip/changeset/solana/cs_build_solana.go

View workflow job for this annotation

GitHub Actions / GolangCI Lint (deployment)

elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
if testRouter {
args = append(args, "ANCHOR_BUILD_ARGS=-p ccip_router")
}
}
output, err := runCommand("make", args, solanaDir)
output, err := runCommand(cmd, args, solanaDir)
if err != nil {
return fmt.Errorf("anchor build failed: %s %w", output, err)
}
Expand All @@ -159,8 +168,27 @@
CreateDestinationDir bool
// Forces re-clone of git directory. Useful for forcing regeneration of keys
CleanGitDir bool
ReplaceKeys bool
UpgradeKeys map[deployment.ContractType]string
TestRouter bool
// https://solana.com/developers/guides/advanced/verified-builds
VerifiedBuild bool
}

type VerifyBuildConfig struct {
GitCommitSha string
ChainSelector uint64
VerifyFeeQuoter bool
VerifyRouter bool
VerifyOffRamp bool
VerifyRMNRemote bool
VerifyBurnMintTokenPool bool
VerifyLockReleaseTokenPool bool
VerifyAccessController bool
VerifyMCM bool
VerifyTimelock bool
RemoteVerification bool
MCMSSolana *MCMSConfigSolana
}

func filterRouterFiles(files []os.DirEntry) ([]os.DirEntry, error) {
Expand All @@ -184,19 +212,21 @@
return fmt.Errorf("error cloning repo: %w", err)
}

// Replace keys in Rust files using anchor keys sync
if err := replaceKeys(e); err != nil {
return fmt.Errorf("error replacing keys: %w", err)
}
if config.ReplaceKeys {
// Replace keys in Rust files using anchor keys sync
if err := replaceKeys(e); err != nil {
return fmt.Errorf("error replacing keys: %w", err)
}

// Replace keys in Rust files for upgrade by replacing the declare_id!() macro explicitly
// We need to do this so the keys will match the existing deployed program
if err := replaceKeysForUpgrade(e, config.UpgradeKeys); err != nil {
return fmt.Errorf("error replacing keys for upgrade: %w", err)
// Replace keys in Rust files for upgrade by replacing the declare_id!() macro explicitly
// We need to do this so the keys will match the existing deployed program
if err := replaceKeysForUpgrade(e, config.UpgradeKeys); err != nil {
return fmt.Errorf("error replacing keys for upgrade: %w", err)
}
}

// Build the project with Anchor
if err := buildProject(e, config.TestRouter); err != nil {
if err := buildProject(e, config.TestRouter, config.VerifiedBuild); err != nil {
return fmt.Errorf("error building project: %w", err)
}

Expand Down Expand Up @@ -242,3 +272,80 @@
}
return nil
}

func runSolanaVerify(networkURL, programID, libraryName, commitHash, mountPath string, remote bool) error {
cmdArgs := []string{
"verify-from-repo",
"-u", networkURL,
"--program-id", programID,
"--library-name", libraryName,
repoURL,
"--commit-hash", commitHash,
"--mount-path", mountPath,
}

// Add --remote flag if remote verification is enabled
if remote {
cmdArgs = append(cmdArgs, "--remote")
}

output, err := runCommand("solana-verify", cmdArgs, ".")
fmt.Println(output)
if err != nil {
return fmt.Errorf("solana program verification failed: %s %w", output, err)
}
return nil
}

func VerifyBuild(e deployment.Environment, cfg VerifyBuildConfig) (deployment.ChangesetOutput, error) {
chain := e.SolChains[cfg.ChainSelector]
state, _ := ccipChangeset.LoadOnchainState(e)
chainState := state.SolChains[cfg.ChainSelector]

addresses, err := e.ExistingAddresses.AddressesForChain(cfg.ChainSelector)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to get existing addresses: %w", err)
}
mcmState, err := csState.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load onchain state: %w", err)
}

verifications := []struct {
name string
programID string
programLib string
enabled bool
}{
{"Fee Quoter", chainState.FeeQuoter.String(), deployment.FeeQuoterProgramName, cfg.VerifyFeeQuoter},
{"Router", chainState.Router.String(), deployment.RouterProgramName, cfg.VerifyRouter},
{"OffRamp", chainState.OffRamp.String(), deployment.OffRampProgramName, cfg.VerifyOffRamp},
{"RMN Remote", chainState.RMNRemote.String(), deployment.RMNRemoteProgramName, cfg.VerifyRMNRemote},
{"Burn Mint Token Pool", chainState.BurnMintTokenPool.String(), deployment.BurnMintTokenPoolProgramName, cfg.VerifyBurnMintTokenPool},
{"Lock Release Token Pool", chainState.LockReleaseTokenPool.String(), deployment.LockReleaseTokenPoolProgramName, cfg.VerifyLockReleaseTokenPool},
{"Access Controller", mcmState.AccessControllerProgram.String(), deployment.AccessControllerProgramName, cfg.VerifyAccessController},
{"MCM", mcmState.McmProgram.String(), deployment.McmProgramName, cfg.VerifyMCM},
{"Timelock", mcmState.TimelockProgram.String(), deployment.TimelockProgramName, cfg.VerifyTimelock},
}

for _, v := range verifications {
if !v.enabled {
continue
}

e.Logger.Debugw(fmt.Sprintf("Verifying %s", v.name))

Check failure on line 336 in deployment/ccip/changeset/solana/cs_build_solana.go

View workflow job for this annotation

GitHub Actions / GolangCI Lint (deployment)

fmt.Sprintf can be replaced with string concatenation (perfsprint)
err := runSolanaVerify(
chain.URL,
v.programID,
v.programLib,
cfg.GitCommitSha,
anchorDir,
cfg.RemoteVerification,
)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("error verifying %s: %w", v.name, err)
}
}

return deployment.ChangesetOutput{}, nil
}
13 changes: 11 additions & 2 deletions deployment/ccip/changeset/solana/cs_deploy_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

const (
OldSha = "e5f38e1c557eda4bc4a0436b69646a534ae16d39"
OldSha = "9713793a350fa06d4303d29c23e9b096539c189c"
NewSha = "ed22f75fe8de58634a46a137b386d4c508fde14f"
)

Expand Down Expand Up @@ -94,6 +94,7 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) {
GitCommitSha: OldSha,
DestinationDir: e.SolChains[solChainSelectors[0]].ProgramsPath,
CleanDestinationDir: true,
VerifiedBuild: true,
}
require.NoError(t, err)
}
Expand Down Expand Up @@ -153,6 +154,15 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) {

// deploy the contracts
e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{
// try verification
commonchangeset.Configure(
deployment.CreateLegacyChangeSet(ccipChangesetSolana.VerifyBuild),
ccipChangesetSolana.VerifyBuildConfig{
ChainSelector: solChainSelectors[0],
GitCommitSha: OldSha,
VerifyFeeQuoter: true,
},
),
// upgrade authority
commonchangeset.Configure(
deployment.CreateLegacyChangeSet(ccipChangesetSolana.SetUpgradeAuthorityChangeset),
Expand Down Expand Up @@ -184,7 +194,6 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) {
GitCommitSha: NewSha,
DestinationDir: e.SolChains[solChainSelectors[0]].ProgramsPath,
CleanDestinationDir: true,
CleanGitDir: true,
UpgradeKeys: map[deployment.ContractType]string{
ccipChangeset.Router: state.SolChains[solChainSelectors[0]].Router.String(),
ccipChangeset.FeeQuoter: state.SolChains[solChainSelectors[0]].FeeQuoter.String(),
Expand Down
Loading