Skip to content

Commit

Permalink
save solana pdas in address book (#16135)
Browse files Browse the repository at this point in the history
* pdas in ab

* clean up merge

* use labels

* load config pda from state

* lint
  • Loading branch information
tt-cll authored Jan 31, 2025
1 parent 57ca0fb commit 8af9a90
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 46 deletions.
74 changes: 42 additions & 32 deletions deployment/ccip/changeset/solana/cs_chain_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strconv"

"github.com/gagliardetto/solana-go"

Expand Down Expand Up @@ -95,9 +96,8 @@ func validateRouterConfig(chain deployment.SolChain, chainState cs.SolCCIPChainS
return fmt.Errorf("router not found in existing state, deploy the router first chain %d", chain.Selector)
}
// addressing errcheck in the next PR
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)
var routerConfigAccount solRouter.Config
err := chain.GetAccountDataBorshInto(context.Background(), routerConfigPDA, &routerConfigAccount)
err := chain.GetAccountDataBorshInto(context.Background(), chainState.RouterConfigPDA, &routerConfigAccount)
if err != nil {
return fmt.Errorf("router config not found in existing state, initialize the router first %d", chain.Selector)
}
Expand Down Expand Up @@ -146,10 +146,9 @@ func (cfg AddRemoteChainToSolanaConfig) Validate(e deployment.Environment) error
if err := commoncs.ValidateOwnershipSolana(e.GetContext(), cfg.MCMS != nil, e.SolChains[chainSel].DeployerKey.PublicKey(), chainState.Timelock, chainState.Router); err != nil {
return fmt.Errorf("failed to validate ownership: %w", err)
}
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)
var routerConfigAccount solRouter.Config
// already validated that router config exists
_ = chain.GetAccountDataBorshInto(context.Background(), routerConfigPDA, &routerConfigAccount)
_ = chain.GetAccountDataBorshInto(context.Background(), chainState.RouterConfigPDA, &routerConfigAccount)

for remote := range updates {
if _, ok := supportedChains[remote]; !ok {
Expand All @@ -175,17 +174,22 @@ func AddRemoteChainToSolana(e deployment.Environment, cfg AddRemoteChainToSolana
return deployment.ChangesetOutput{}, err
}

ab := deployment.NewMemoryAddressBook()
for chainSel, updates := range cfg.UpdatesByChain {
_, err := doAddRemoteChainToSolana(e, s, chainSel, updates)
err := doAddRemoteChainToSolana(e, s, chainSel, updates, ab)
if err != nil {
return deployment.ChangesetOutput{}, err
return deployment.ChangesetOutput{AddressBook: ab}, err
}
}

return deployment.ChangesetOutput{}, nil
return deployment.ChangesetOutput{AddressBook: ab}, nil
}

func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, chainSel uint64, updates map[uint64]RemoteChainConfigSolana) (deployment.ChangesetOutput, error) {
func doAddRemoteChainToSolana(
e deployment.Environment,
s cs.CCIPOnChainState,
chainSel uint64,
updates map[uint64]RemoteChainConfigSolana,
ab deployment.AddressBook) error {
chain := e.SolChains[chainSel]
ccipRouterID := s.SolChains[chainSel].Router

Expand All @@ -195,11 +199,11 @@ func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, c
remoteChainFamily, _ := chainsel.GetSelectorFamily(remoteChainSel)
switch remoteChainFamily {
case chainsel.FamilySolana:
return deployment.ChangesetOutput{}, fmt.Errorf("support for solana chain as remote chain is not implemented yet %d", remoteChainSel)
return fmt.Errorf("support for solana chain as remote chain is not implemented yet %d", remoteChainSel)
case chainsel.FamilyEVM:
onRampAddress := s.Chains[remoteChainSel].OnRamp.Address().String()
if onRampAddress == "" {
return deployment.ChangesetOutput{}, fmt.Errorf("onramp address not found for chain %d", remoteChainSel)
return fmt.Errorf("onramp address not found for chain %d", remoteChainSel)
}
addressBytes := []byte(onRampAddress)
copy(onRampBytes[:], addressBytes)
Expand All @@ -210,7 +214,6 @@ func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, c
IsEnabled: update.EnabledAsSource,
}
// addressing errcheck in the next PR
routerConfigPDA, _, _ := solState.FindConfigPDA(ccipRouterID)
destChainStatePDA, _ := solState.FindDestChainStatePDA(remoteChainSel, ccipRouterID)
sourceChainStatePDA, _ := solState.FindSourceChainStatePDA(remoteChainSel, ccipRouterID)

Expand All @@ -220,24 +223,38 @@ func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, c
update.DestinationConfig,
sourceChainStatePDA,
destChainStatePDA,
routerConfigPDA,
s.SolChains[chainSel].RouterConfigPDA,
chain.DeployerKey.PublicKey(),
solana.SystemProgramID,
).ValidateAndBuild()

if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err)
return fmt.Errorf("failed to generate instructions: %w", err)
}

err = chain.Confirm([]solana.Instruction{instruction})

if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err)
return fmt.Errorf("failed to confirm instructions: %w", err)
}
e.Logger.Infow("Confirmed instruction", "instruction", instruction)

tv := deployment.NewTypeAndVersion(cs.RemoteDest, deployment.Version1_0_0)
remoteChainSelStr := strconv.FormatUint(remoteChainSel, 10)
tv.AddLabel(remoteChainSelStr)
err = ab.Save(chainSel, destChainStatePDA.String(), tv)
if err != nil {
return fmt.Errorf("failed to save dest chain state to address book: %w", err)
}

tv = deployment.NewTypeAndVersion(cs.RemoteSource, deployment.Version1_0_0)
tv.AddLabel(remoteChainSelStr)
err = ab.Save(chainSel, sourceChainStatePDA.String(), tv)
if err != nil {
return fmt.Errorf("failed to save source chain state to address book: %w", err)
}
}

return deployment.ChangesetOutput{}, nil
return nil
}

// SET OCR3 CONFIG
Expand Down Expand Up @@ -282,10 +299,8 @@ func SetOCR3ConfigSolana(e deployment.Environment, cfg cs.SetOCR3OffRampConfig)
// TODO: check if ocr3 has already been set
// set, err := isOCR3ConfigSetSolana(e.Logger, e.Chains[remote], state.Chains[remote].OffRamp, args)
var instructions []solana.Instruction
ccipRouterID := solChains[remote].Router
// addressing errcheck in the next PR
routerConfigPDA, _, _ := solState.FindConfigPDA(ccipRouterID)
routerStatePDA, _, _ := solState.FindStatePDA(ccipRouterID)
routerConfigPDA := solChains[remote].RouterConfigPDA
routerStatePDA := solChains[remote].RouterStatePDA
for _, arg := range args {
instruction, err := solRouter.NewSetOcrConfigInstruction(
arg.OCRPluginType,
Expand Down Expand Up @@ -566,7 +581,6 @@ func AddBillingToken(e deployment.Environment, cfg BillingTokenConfig) (deployme

// verified
tokenprogramID, _ := GetTokenProgramID(cfg.TokenProgramName)
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)
billingConfigPDA, _, _ := solState.FindFeeBillingTokenConfigPDA(tokenPubKey, chainState.Router)

// addressing errcheck in the next PR
Expand All @@ -575,7 +589,7 @@ func AddBillingToken(e deployment.Environment, cfg BillingTokenConfig) (deployme

ixConfig, cerr := solRouter.NewAddBillingTokenConfigInstruction(
cfg.Config,
routerConfigPDA,
chainState.RouterConfigPDA,
billingConfigPDA,
tokenprogramID,
tokenPubKey,
Expand Down Expand Up @@ -639,13 +653,12 @@ func AddBillingTokenForRemoteChain(e deployment.Environment, cfg BillingTokenFor
tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey)
// verified
remoteBillingPDA, _, _ := solState.FindCcipTokenpoolBillingPDA(cfg.RemoteChainSelector, tokenPubKey, chainState.Router)
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)

ix, err := solRouter.NewSetTokenBillingInstruction(
cfg.RemoteChainSelector,
tokenPubKey,
cfg.Config,
routerConfigPDA,
chainState.RouterConfigPDA,
remoteBillingPDA,
chain.DeployerKey.PublicKey(),
solana.SystemProgramID,
Expand Down Expand Up @@ -716,7 +729,6 @@ func RegisterTokenAdminRegistry(e deployment.Environment, cfg RegisterTokenAdmin
tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey)

// verified
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)
tokenAdminRegistryPDA, _, _ := solState.FindTokenAdminRegistryPDA(tokenPubKey, chainState.Router)

var instruction *solRouter.Instruction
Expand All @@ -728,7 +740,7 @@ func RegisterTokenAdminRegistry(e deployment.Environment, cfg RegisterTokenAdmin
instruction, err = solRouter.NewRegisterTokenAdminRegistryViaGetCcipAdminInstruction(
tokenPubKey,
tokenAdminRegistryAdmin, // admin of the tokenAdminRegistry PDA
routerConfigPDA,
chainState.RouterConfigPDA,
tokenAdminRegistryPDA, // this gets created
chain.DeployerKey.PublicKey(), // (ccip admin)
solana.SystemProgramID,
Expand All @@ -739,7 +751,7 @@ func RegisterTokenAdminRegistry(e deployment.Environment, cfg RegisterTokenAdmin
case ViaOwnerInstruction:
// the token mint authority signs and makes itself the authority of the tokenAdminRegistry PDA
instruction, err = solRouter.NewRegisterTokenAdminRegistryViaOwnerInstruction(
routerConfigPDA,
chainState.RouterConfigPDA,
tokenAdminRegistryPDA, // this gets created
tokenPubKey,
chain.DeployerKey.PublicKey(), // (token mint authority) becomes the authority of the tokenAdminRegistry PDA
Expand Down Expand Up @@ -819,15 +831,14 @@ func TransferAdminRoleTokenAdminRegistry(e deployment.Environment, cfg TransferA

// verified
tokenAdminRegistryPDA, _, _ := solState.FindTokenAdminRegistryPDA(tokenPubKey, chainState.Router)
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)

currentRegistryAdminPrivateKey := solana.MustPrivateKeyFromBase58(cfg.CurrentRegistryAdminPrivateKey)
newRegistryAdminPubKey := solana.MustPublicKeyFromBase58(cfg.NewRegistryAdminPublicKey)

ix1, err := solRouter.NewTransferAdminRoleTokenAdminRegistryInstruction(
tokenPubKey,
newRegistryAdminPubKey,
routerConfigPDA,
chainState.RouterConfigPDA,
tokenAdminRegistryPDA,
currentRegistryAdminPrivateKey.PublicKey(), // as we are assuming this is the default authority for everything in the beginning
).ValidateAndBuild()
Expand Down Expand Up @@ -892,11 +903,10 @@ func AcceptAdminRoleTokenAdminRegistry(e deployment.Environment, cfg AcceptAdmin

// verified
tokenAdminRegistryPDA, _, _ := solState.FindTokenAdminRegistryPDA(tokenPubKey, chainState.Router)
routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router)

ix1, err := solRouter.NewAcceptAdminRoleTokenAdminRegistryInstruction(
tokenPubKey,
routerConfigPDA,
chainState.RouterConfigPDA,
tokenAdminRegistryPDA,
newRegistryAdminPrivateKey.PublicKey(),
).ValidateAndBuild()
Expand Down
7 changes: 3 additions & 4 deletions deployment/ccip/changeset/solana/cs_chain_contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestAddRemoteChain(t *testing.T) {
state, err := changeset.LoadOnchainState(tenv.Env)
require.NoError(t, err)

_, err = commonchangeset.ApplyChangesets(t, tenv.Env, nil, []commonchangeset.ChangesetApplication{
tenv.Env, err = commonchangeset.ApplyChangesets(t, tenv.Env, nil, []commonchangeset.ChangesetApplication{
{
Changeset: commonchangeset.WrapChangeSet(changeset.UpdateOnRampsDestsChangeset),
Config: changeset.UpdateOnRampDestsConfig{
Expand Down Expand Up @@ -80,16 +80,15 @@ func TestAddRemoteChain(t *testing.T) {

state, err = changeset.LoadOnchainStateSolana(tenv.Env)
require.NoError(t, err)

var sourceChainStateAccount solRouter.SourceChain
evmSourceChainStatePDA, _ := solState.FindSourceChainStatePDA(evmChain, state.SolChains[solChain].Router)
evmSourceChainStatePDA := state.SolChains[solChain].SourceChainStatePDAs[evmChain]
err = tenv.Env.SolChains[solChain].GetAccountDataBorshInto(ctx, evmSourceChainStatePDA, &sourceChainStateAccount)
require.NoError(t, err)
require.Equal(t, uint64(1), sourceChainStateAccount.State.MinSeqNr)
require.True(t, sourceChainStateAccount.Config.IsEnabled)

var destChainStateAccount solRouter.DestChain
evmDestChainStatePDA, _ := solState.FindDestChainStatePDA(evmChain, state.SolChains[solChain].Router)
evmDestChainStatePDA := state.SolChains[solChain].DestChainStatePDAs[evmChain]
err = tenv.Env.SolChains[solChain].GetAccountDataBorshInto(ctx, evmDestChainStatePDA, &destChainStateAccount)
require.NoError(t, err)
}
Expand Down
60 changes: 52 additions & 8 deletions deployment/ccip/changeset/solana_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package changeset
import (
"errors"
"fmt"
"strconv"

"github.com/gagliardetto/solana-go"

solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state"

"github.com/smartcontractkit/chainlink/deployment"
commontypes "github.com/smartcontractkit/chainlink/deployment/common/types"
)
Expand All @@ -16,6 +19,9 @@ var (
Receiver deployment.ContractType = "Receiver"
SPL2022Tokens deployment.ContractType = "SPL2022Tokens"
WSOL deployment.ContractType = "WSOL"
// for PDAs from AddRemoteChainToSolana
RemoteSource deployment.ContractType = "RemoteSource"
RemoteDest deployment.ContractType = "RemoteDest"
)

// SolChainState holds a Go binding for all the currently deployed CCIP programs
Expand All @@ -29,6 +35,11 @@ type SolCCIPChainState struct {
SPL2022Tokens []solana.PublicKey
TokenPool solana.PublicKey
WSOL solana.PublicKey
// PDAs to avoid redundant lookups
RouterStatePDA solana.PublicKey
RouterConfigPDA solana.PublicKey
SourceChainStatePDAs map[uint64]solana.PublicKey
DestChainStatePDAs map[uint64]solana.PublicKey
}

func LoadOnchainStateSolana(e deployment.Environment) (CCIPOnChainState, error) {
Expand All @@ -55,28 +66,61 @@ func LoadOnchainStateSolana(e deployment.Environment) (CCIPOnChainState, error)

// LoadChainStateSolana Loads all state for a SolChain into state
func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deployment.TypeAndVersion) (SolCCIPChainState, error) {
var state SolCCIPChainState
state := SolCCIPChainState{
SourceChainStatePDAs: make(map[uint64]solana.PublicKey),
DestChainStatePDAs: make(map[uint64]solana.PublicKey),
}
var spl2022Tokens []solana.PublicKey
for address, tvStr := range addresses {
switch tvStr.String() {
case deployment.NewTypeAndVersion(commontypes.LinkToken, deployment.Version1_0_0).String():
switch tvStr.Type {
case commontypes.LinkToken:
pub := solana.MustPublicKeyFromBase58(address)
state.LinkToken = pub
case deployment.NewTypeAndVersion(Router, deployment.Version1_0_0).String():
case Router:
pub := solana.MustPublicKeyFromBase58(address)
state.Router = pub
case deployment.NewTypeAndVersion(AddressLookupTable, deployment.Version1_0_0).String():
routerStatePDA, _, err := solState.FindStatePDA(state.Router)
if err != nil {
return state, err
}
state.RouterStatePDA = routerStatePDA
routerConfigPDA, _, err := solState.FindConfigPDA(state.Router)
if err != nil {
return state, err
}
state.RouterConfigPDA = routerConfigPDA
case AddressLookupTable:
pub := solana.MustPublicKeyFromBase58(address)
state.AddressLookupTable = pub
case deployment.NewTypeAndVersion(Receiver, deployment.Version1_0_0).String():
case Receiver:
pub := solana.MustPublicKeyFromBase58(address)
state.Receiver = pub
case deployment.NewTypeAndVersion(SPL2022Tokens, deployment.Version1_0_0).String():
case SPL2022Tokens:
pub := solana.MustPublicKeyFromBase58(address)
spl2022Tokens = append(spl2022Tokens, pub)
case deployment.NewTypeAndVersion(TokenPool, deployment.Version1_0_0).String():
case TokenPool:
pub := solana.MustPublicKeyFromBase58(address)
state.TokenPool = pub
case RemoteSource:
pub := solana.MustPublicKeyFromBase58(address)
// Labels should only have one entry
for selStr := range tvStr.Labels {
selector, err := strconv.ParseUint(selStr, 10, 64)
if err != nil {
return state, err
}
state.SourceChainStatePDAs[selector] = pub
}
case RemoteDest:
pub := solana.MustPublicKeyFromBase58(address)
// Labels should only have one entry
for selStr := range tvStr.Labels {
selector, err := strconv.ParseUint(selStr, 10, 64)
if err != nil {
return state, err
}
state.DestChainStatePDAs[selector] = pub
}
default:
return state, fmt.Errorf("unknown contract %s", tvStr)
}
Expand Down
5 changes: 3 additions & 2 deletions deployment/ccip/changeset/testhelpers/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1383,14 +1383,15 @@ func ValidateSolanaState(t *testing.T, e deployment.Environment, solChainSelecto
// Validate addresses
require.False(t, chainState.LinkToken.IsZero(), "Link token address is zero for chain %d", sel)
require.False(t, chainState.Router.IsZero(), "Router address is zero for chain %d", sel)
require.False(t, chainState.RouterConfigPDA.IsZero(), "RouterConfigPDA is zero for chain %d", sel)
require.False(t, chainState.RouterStatePDA.IsZero(), "RouterStatePDA is zero for chain %d", sel)
require.False(t, chainState.AddressLookupTable.IsZero(), "Address lookup table is zero for chain %d", sel)

// Get router config
var routerConfigAccount solRouter.Config
configPDA, _, _ := solState.FindConfigPDA(chainState.Router)

// Check if account exists first
err = e.SolChains[sel].GetAccountDataBorshInto(testcontext.Get(t), configPDA, &routerConfigAccount)
err = e.SolChains[sel].GetAccountDataBorshInto(testcontext.Get(t), chainState.RouterConfigPDA, &routerConfigAccount)
require.NoError(t, err, "Failed to deserialize router config for chain %d", sel)
}
}
Expand Down

0 comments on commit 8af9a90

Please sign in to comment.