Skip to content

Commit

Permalink
Support RMN for CCIP v2 view (#15611)
Browse files Browse the repository at this point in the history
* Support RMN for CCIP v2 view

* Write hex strings instead of bytes

* Add contract address in error message
carte7000 authored Dec 12, 2024
1 parent f6e3f68 commit 52c2db4
Showing 3 changed files with 225 additions and 0 deletions.
9 changes: 9 additions & 0 deletions deployment/ccip/changeset/state.go
Original file line number Diff line number Diff line change
@@ -161,6 +161,15 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) {
}
chainView.RMN[c.RMNRemote.Address().Hex()] = rmnView
}

if c.RMNHome != nil {
rmnHomeView, err := v1_6.GenerateRMNHomeView(c.RMNHome)
if err != nil {
return chainView, errors.Wrapf(err, "failed to generate rmn home view for rmn home %s", c.RMNHome.Address().String())
}
chainView.RMNHome[c.RMNHome.Address().Hex()] = rmnHomeView
}

if c.FeeQuoter != nil && c.Router != nil && c.TokenAdminRegistry != nil {
fqView, err := v1_6.GenerateFeeQuoterView(c.FeeQuoter, c.Router, c.TokenAdminRegistry)
if err != nil {
214 changes: 214 additions & 0 deletions deployment/ccip/view/v1_6/rmnhome.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package v1_6

import (
"encoding/hex"
"encoding/json"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/smartcontractkit/chainlink/deployment/common/view/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home"
)

type RMNHomeView struct {
types.ContractMetaData
CandidateConfig *RMNHomeVersionedConfig `json:"candidateConfig,omitempty"`
ActiveConfig *RMNHomeVersionedConfig `json:"activeConfig,omitempty"`
}

type RMNHomeVersionedConfig struct {
Version uint32 `json:"version"`
StaticConfig RMNHomeStaticConfig `json:"staticConfig"`
DynamicConfig RMNHomeDynamicConfig `json:"dynamicConfig"`
Digest [32]byte `json:"digest"`
}

func decodeHexString(hexStr string, expectedLength int) ([]byte, error) {
bytes, err := hex.DecodeString(hexStr)
if err != nil {
return nil, err
}
if len(bytes) != expectedLength {
return nil, fmt.Errorf("invalid length: expected %d, got %d", expectedLength, len(bytes))
}
return bytes, nil
}

func (c RMNHomeVersionedConfig) MarshalJSON() ([]byte, error) {
type Alias RMNHomeVersionedConfig
return json.Marshal(&struct {
Digest string `json:"digest"`
*Alias
}{
Digest: hex.EncodeToString(c.Digest[:]),
Alias: (*Alias)(&c),
})
}

func (c *RMNHomeVersionedConfig) UnmarshalJSON(data []byte) error {
type Alias RMNHomeVersionedConfig
aux := &struct {
Digest string `json:"digest"`
*Alias
}{
Alias: (*Alias)(c),
}

if err := json.Unmarshal(data, &aux); err != nil {
return err
}

digestBytes, err := decodeHexString(aux.Digest, 32)
if err != nil {
return err
}
copy(c.Digest[:], digestBytes)
return nil
}

type RMNHomeStaticConfig struct {
Nodes []RMNHomeNode `json:"nodes"`
}

type RMNHomeDynamicConfig struct {
SourceChains []RMNHomeSourceChain `json:"sourceChains"`
}

type RMNHomeSourceChain struct {
ChainSelector uint64 `json:"selector"`
F uint64 `json:"f"`
ObserverNodesBitmap *big.Int `json:"observerNodesBitmap"`
}

type RMNHomeNode struct {
PeerId [32]byte `json:"peerId"`
OffchainPublicKey [32]byte `json:"offchainPublicKey"`
}

func (n RMNHomeNode) MarshalJSON() ([]byte, error) {
type Alias RMNHomeNode
return json.Marshal(&struct {
PeerId string `json:"peerId"`
OffchainPublicKey string `json:"offchainPublicKey"`
*Alias
}{
PeerId: hex.EncodeToString(n.PeerId[:]),
OffchainPublicKey: hex.EncodeToString(n.OffchainPublicKey[:]),
Alias: (*Alias)(&n),
})
}

func (n *RMNHomeNode) UnmarshalJSON(data []byte) error {
type Alias RMNHomeNode
aux := &struct {
PeerId string `json:"peerId"`
OffchainPublicKey string `json:"offchainPublicKey"`
*Alias
}{
Alias: (*Alias)(n),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}

peerIdBytes, err := decodeHexString(aux.PeerId, 32)
if err != nil {
return err
}
copy(n.PeerId[:], peerIdBytes)

offchainPublicKeyBytes, err := decodeHexString(aux.OffchainPublicKey, 32)
if err != nil {
return err
}
copy(n.OffchainPublicKey[:], offchainPublicKeyBytes)

return nil
}

type DigestFunc func(*bind.CallOpts) ([32]byte, error)

func mapNodes(nodes []rmn_home.RMNHomeNode) []RMNHomeNode {
result := make([]RMNHomeNode, len(nodes))
for i, node := range nodes {
result[i] = RMNHomeNode{
PeerId: node.PeerId,
OffchainPublicKey: node.OffchainPublicKey,
}
}
return result
}

func mapSourceChains(chains []rmn_home.RMNHomeSourceChain) []RMNHomeSourceChain {
result := make([]RMNHomeSourceChain, len(chains))
for i, chain := range chains {
result[i] = RMNHomeSourceChain{
ChainSelector: chain.ChainSelector,
F: chain.F,
ObserverNodesBitmap: chain.ObserverNodesBitmap,
}
}
return result
}

func generateRmnHomeVersionedConfig(reader *rmn_home.RMNHome, digestFunc DigestFunc) (*RMNHomeVersionedConfig, error) {
address := reader.Address()
digest, err := digestFunc(nil)
if err != nil {
return nil, fmt.Errorf("failed to get digest for contract %s: %w", address, err)
}

if digest == [32]byte{} {
return nil, nil
}

config, err := reader.GetConfig(nil, digest)
if err != nil {
return nil, fmt.Errorf("failed to get config for contract %s: %w", address, err)
}

staticConfig := RMNHomeStaticConfig{
Nodes: mapNodes(config.VersionedConfig.StaticConfig.Nodes),
}

dynamicConfig := RMNHomeDynamicConfig{
SourceChains: mapSourceChains(config.VersionedConfig.DynamicConfig.SourceChains),
}

return &RMNHomeVersionedConfig{
Version: config.VersionedConfig.Version,
Digest: config.VersionedConfig.ConfigDigest,
StaticConfig: staticConfig,
DynamicConfig: dynamicConfig,
}, nil
}

func GenerateRMNHomeView(rmnReader *rmn_home.RMNHome) (RMNHomeView, error) {
if rmnReader == nil {
return RMNHomeView{}, nil
}

address := rmnReader.Address()

activeConfig, err := generateRmnHomeVersionedConfig(rmnReader, rmnReader.GetActiveDigest)
if err != nil {
return RMNHomeView{}, fmt.Errorf("failed to generate active config for contract %s: %w", address, err)
}

candidateConfig, err := generateRmnHomeVersionedConfig(rmnReader, rmnReader.GetCandidateDigest)
if err != nil {
return RMNHomeView{}, fmt.Errorf("failed to generate candidate config for contract %s: %w", address, err)
}

contractMetaData, err := types.NewContractMetaData(rmnReader, rmnReader.Address())
if err != nil {
return RMNHomeView{}, fmt.Errorf("failed to create contract metadata for contract %s: %w", address, err)
}

return RMNHomeView{
ContractMetaData: contractMetaData,
CandidateConfig: candidateConfig,
ActiveConfig: activeConfig,
}, nil
}
2 changes: 2 additions & 0 deletions deployment/ccip/view/view.go
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ type ChainView struct {
// v1.6
FeeQuoter map[string]v1_6.FeeQuoterView `json:"feeQuoter,omitempty"`
NonceManager map[string]v1_6.NonceManagerView `json:"nonceManager,omitempty"`
RMNHome map[string]v1_6.RMNHomeView `json:"rmnHome,omitempty"`
RMN map[string]v1_6.RMNRemoteView `json:"rmn,omitempty"`
OnRamp map[string]v1_6.OnRampView `json:"onRamp,omitempty"`
OffRamp map[string]v1_6.OffRampView `json:"offRamp,omitempty"`
@@ -46,6 +47,7 @@ func NewChain() ChainView {
// v1.6
FeeQuoter: make(map[string]v1_6.FeeQuoterView),
NonceManager: make(map[string]v1_6.NonceManagerView),
RMNHome: make(map[string]v1_6.RMNHomeView),
RMN: make(map[string]v1_6.RMNRemoteView),
OnRamp: make(map[string]v1_6.OnRampView),
OffRamp: make(map[string]v1_6.OffRampView),

0 comments on commit 52c2db4

Please sign in to comment.