Skip to content

Commit

Permalink
fix(deployment): enables multiple ocr3 contracts per chain
Browse files Browse the repository at this point in the history
  • Loading branch information
MStreet3 committed Dec 19, 2024
1 parent 1adf349 commit ae67a9e
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 7 deletions.
12 changes: 9 additions & 3 deletions deployment/address_book.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package deployment

import (
"fmt"
"log"
"strings"
"sync"

Expand Down Expand Up @@ -83,7 +84,7 @@ func NewTypeAndVersion(t ContractType, v semver.Version) TypeAndVersion {
type AddressBook interface {
Save(chainSelector uint64, address string, tv TypeAndVersion) error
Addresses() (map[uint64]map[string]TypeAndVersion, error)
AddressesForChain(chain uint64) (map[string]TypeAndVersion, error)
AddressesForChain(chain uint64, opts ...func(map[string]TypeAndVersion)) (map[string]TypeAndVersion, error)
// Allows for merging address books (e.g. new deployments with existing ones)
Merge(other AddressBook) error
Remove(ab AddressBook) error
Expand Down Expand Up @@ -149,7 +150,7 @@ func (m *AddressBookMap) Addresses() (map[uint64]map[string]TypeAndVersion, erro
return m.cloneAddresses(m.addressesByChain), nil
}

func (m *AddressBookMap) AddressesForChain(chainSelector uint64) (map[string]TypeAndVersion, error) {
func (m *AddressBookMap) AddressesForChain(chainSelector uint64, opts ...func(map[string]TypeAndVersion)) (map[string]TypeAndVersion, error) {
_, err := chainsel.GetChainIDFromSelector(chainSelector)
if err != nil {
return nil, errors.Wrapf(ErrInvalidChainSelector, "chain selector %d", chainSelector)
Expand All @@ -165,7 +166,12 @@ func (m *AddressBookMap) AddressesForChain(chainSelector uint64) (map[string]Typ
// maps are mutable and pass via a pointer
// creating a copy of the map to prevent concurrency
// read and changes outside object-bound
return maps.Clone(m.addressesByChain[chainSelector]), nil
cloned := maps.Clone(m.addressesByChain[chainSelector])
for _, opt := range opts {
log.Printf("Applying option %v", opt)

Check failure on line 171 in deployment/address_book.go

View workflow job for this annotation

GitHub Actions / GolangCI Lint (deployment)

printf: log.Printf format %v arg opt is a func value, not called (govet)
opt(cloned)
}
return cloned, nil
}

// Merge will merge the addresses from another address book into this one.
Expand Down
28 changes: 28 additions & 0 deletions deployment/address_book_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,34 @@ func TestAddressBook_Save(t *testing.T) {
})
}

func TestAddressBook_AddressesForChain(t *testing.T) {
ab := NewMemoryAddressBook()
ocr3Cap100 := NewTypeAndVersion("OCR3Capability", Version1_0_0)
copyOCR3Cap100 := NewTypeAndVersion("OCR3Capability", Version1_0_0)

addr1 := common.HexToAddress("0x1").String()
addr2 := common.HexToAddress("0x2").String()

err := ab.Save(chainsel.TEST_90000001.Selector, addr1, ocr3Cap100)
require.NoError(t, err)

err = ab.Save(chainsel.TEST_90000001.Selector, addr2, copyOCR3Cap100)
require.NoError(t, err)

addresses, err := ab.AddressesForChain(chainsel.TEST_90000001.Selector,

Check failure on line 89 in deployment/address_book_test.go

View workflow job for this annotation

GitHub Actions / GolangCI Lint (deployment)

ineffectual assignment to err (ineffassign)
func(m map[string]TypeAndVersion) {
for k, v := range m {
if v.Type == "OCR3Capability" {
if k != addr1 {
delete(m, k)
}
}
}
},
)
require.Len(t, addresses, 1)
}

func TestAddressBook_Merge(t *testing.T) {
onRamp100 := NewTypeAndVersion("OnRamp", Version1_0_0)
onRamp110 := NewTypeAndVersion("OnRamp", Version1_1_0)
Expand Down
2 changes: 2 additions & 0 deletions deployment/keystone/changeset/deploy_ocr3.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var _ deployment.ChangeSet[ConfigureOCR3Config] = ConfigureOCR3Contract
type ConfigureOCR3Config struct {
ChainSel uint64
NodeIDs []string
OCR3ContractAddr *string
OCR3Config *kslib.OracleConfig
DryRun bool
WriteGeneratedConfig io.Writer // if not nil, write the generated config to this writer as JSON [OCR2OracleConfig]
Expand All @@ -55,6 +56,7 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config)
NodeIDs: cfg.NodeIDs,
OCR3Config: cfg.OCR3Config,
DryRun: cfg.DryRun,
OCR3Addr: cfg.OCR3ContractAddr,
UseMCMS: cfg.UseMCMS(),
})
if err != nil {
Expand Down
36 changes: 33 additions & 3 deletions deployment/keystone/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ type ConfigureOCR3Config struct {
ChainSel uint64
NodeIDs []string
OCR3Config *OracleConfig
DryRun bool

// OCR3Addr is the address of the OCR3 contract to configure. If nil, and there are more than
// one deployed contracts on the chain, then the configured contract is non-deterministic.
OCR3Addr *string
DryRun bool

UseMCMS bool
}
Expand All @@ -367,9 +371,16 @@ func ConfigureOCR3ContractFromJD(env *deployment.Environment, cfg ConfigureOCR3C
if !ok {
return nil, fmt.Errorf("chain %d not found in environment", cfg.ChainSel)
}

filterFunc := identityFilterer
if cfg.OCR3Addr != nil {
filterFunc = makeOCR3CapabilityFilterer(*cfg.OCR3Addr)
}

contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{
Chains: env.Chains,
AddressBook: env.ExistingAddresses,
Chains: env.Chains,
AddressBook: env.ExistingAddresses,
AddressFilterer: filterFunc,
})
if err != nil {
return nil, fmt.Errorf("failed to get contract sets: %w", err)
Expand Down Expand Up @@ -971,3 +982,22 @@ func configureForwarder(lggr logger.Logger, chain deployment.Chain, contractSet
}
return opMap, nil
}

// makeOCR3CapabilityFilterer returns a filter func that deletes any OCR3Capability contract entries
// that do not match a given OCR3 Address. If no address is given, no filtering is done.
func makeOCR3CapabilityFilterer(ocr3Addr string) func(map[string]deployment.TypeAndVersion) {
return func(m map[string]deployment.TypeAndVersion) {
for k, v := range m {
if v.Type == OCR3Capability {
if k != ocr3Addr {
delete(m, k)
}
}
}
}
}

// identityFilterer is a filter func that does nothing
func identityFilterer(m map[string]deployment.TypeAndVersion) {
// no-op
}
10 changes: 9 additions & 1 deletion deployment/keystone/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import (
type GetContractSetsRequest struct {
Chains map[uint64]deployment.Chain
AddressBook deployment.AddressBook

// AddressFilterer is a function that filters the addresses a given address book. Filtering
// mutates the input map so the input map should be a copy of the original map.
AddressFilterer func(map[string]deployment.TypeAndVersion)
}

type GetContractSetsResponse struct {
Expand Down Expand Up @@ -62,8 +66,12 @@ func GetContractSets(lggr logger.Logger, req *GetContractSetsRequest) (*GetContr
resp := &GetContractSetsResponse{
ContractSets: make(map[uint64]ContractSet),
}
var opts []func(map[string]deployment.TypeAndVersion)
if req.AddressFilterer != nil {
opts = append(opts, req.AddressFilterer)
}
for id, chain := range req.Chains {
addrs, err := req.AddressBook.AddressesForChain(id)
addrs, err := req.AddressBook.AddressesForChain(id, opts...)
if err != nil {
return nil, fmt.Errorf("failed to get addresses for chain %d: %w", id, err)
}
Expand Down

0 comments on commit ae67a9e

Please sign in to comment.