Skip to content

Commit

Permalink
Allow for custom config poller onchain codec (LLO support) (smartcont…
Browse files Browse the repository at this point in the history
…ractkit#11957)

* Allow for custom config poller onchain codec (LLO support)

* Fix linter

* Remove useless pkgerrors

* remove another pkg errors

* PR comments

* Lint

* Resolve conflicts with develop
  • Loading branch information
samsondav authored Feb 13, 2024
1 parent 3a974b7 commit 2380220
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 203 deletions.
87 changes: 24 additions & 63 deletions core/services/relay/evm/config_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"database/sql"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
Expand Down Expand Up @@ -34,60 +33,9 @@ var (
)
)

var (
// ConfigSet Common to all OCR2 evm based contracts: https://github.com/smartcontractkit/libocr/blob/master/contract2/dev/OCR2Abstract.sol
ConfigSet common.Hash

defaultABI abi.ABI
)

const configSetEventName = "ConfigSet"

func init() {
var err error
abiPointer, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi()
if err != nil {
panic(err)
}
defaultABI = *abiPointer
ConfigSet = defaultABI.Events[configSetEventName].ID
}

func unpackLogData(d []byte) (*ocr2aggregator.OCR2AggregatorConfigSet, error) {
unpacked := new(ocr2aggregator.OCR2AggregatorConfigSet)
err := defaultABI.UnpackIntoInterface(unpacked, configSetEventName, d)
if err != nil {
return nil, errors.Wrap(err, "failed to unpack log data")
}
return unpacked, nil
}

func configFromLog(logData []byte) (ocrtypes.ContractConfig, error) {
unpacked, err := unpackLogData(logData)
if err != nil {
return ocrtypes.ContractConfig{}, err
}

var transmitAccounts []ocrtypes.Account
for _, addr := range unpacked.Transmitters {
transmitAccounts = append(transmitAccounts, ocrtypes.Account(addr.Hex()))
}
var signers []ocrtypes.OnchainPublicKey
for _, addr := range unpacked.Signers {
addr := addr
signers = append(signers, addr[:])
}

return ocrtypes.ContractConfig{
ConfigDigest: unpacked.ConfigDigest,
ConfigCount: unpacked.ConfigCount,
Signers: signers,
Transmitters: transmitAccounts,
F: unpacked.F,
OnchainConfig: unpacked.OnchainConfig,
OffchainConfigVersion: unpacked.OffchainConfigVersion,
OffchainConfig: unpacked.OffchainConfig,
}, nil
type LogDecoder interface {
EventSig() common.Hash
Decode(rawLog []byte) (ocrtypes.ContractConfig, error)
}

type configPoller struct {
Expand All @@ -105,18 +53,30 @@ type configPoller struct {
// contract allows us work around such restrictions.
configStoreContractAddr *common.Address
configStoreContract *ocrconfigurationstoreevmsimple.OCRConfigurationStoreEVMSimple

// Depending on the exact contract used, the raw config log may be shaped
// in different ways
ld LogDecoder
}

func configPollerFilterName(addr common.Address) string {
return logpoller.FilterName("OCR2ConfigPoller", addr.String())
}

func NewConfigPoller(lggr logger.Logger, client client.Client, destChainPoller logpoller.LogPoller, aggregatorContractAddr common.Address, configStoreAddr *common.Address) (evmRelayTypes.ConfigPoller, error) {
return newConfigPoller(lggr, client, destChainPoller, aggregatorContractAddr, configStoreAddr)
type CPConfig struct {
Client client.Client
DestinationChainPoller logpoller.LogPoller
AggregatorContractAddress common.Address
ConfigStoreAddress *common.Address
LogDecoder LogDecoder
}

func NewConfigPoller(lggr logger.Logger, cfg CPConfig) (evmRelayTypes.ConfigPoller, error) {
return newConfigPoller(lggr, cfg.Client, cfg.DestinationChainPoller, cfg.AggregatorContractAddress, cfg.ConfigStoreAddress, cfg.LogDecoder)
}

func newConfigPoller(lggr logger.Logger, client client.Client, destChainPoller logpoller.LogPoller, aggregatorContractAddr common.Address, configStoreAddr *common.Address) (*configPoller, error) {
err := destChainPoller.RegisterFilter(logpoller.Filter{Name: configPollerFilterName(aggregatorContractAddr), EventSigs: []common.Hash{ConfigSet}, Addresses: []common.Address{aggregatorContractAddr}})
func newConfigPoller(lggr logger.Logger, client client.Client, destChainPoller logpoller.LogPoller, aggregatorContractAddr common.Address, configStoreAddr *common.Address, ld LogDecoder) (*configPoller, error) {
err := destChainPoller.RegisterFilter(logpoller.Filter{Name: configPollerFilterName(aggregatorContractAddr), EventSigs: []common.Hash{ld.EventSig()}, Addresses: []common.Address{aggregatorContractAddr}})
if err != nil {
return nil, err
}
Expand All @@ -133,6 +93,7 @@ func newConfigPoller(lggr logger.Logger, client client.Client, destChainPoller l
aggregatorContractAddr: aggregatorContractAddr,
client: client,
aggregatorContract: aggregatorContract,
ld: ld,
}

if configStoreAddr != nil {
Expand Down Expand Up @@ -164,7 +125,7 @@ func (cp *configPoller) Replay(ctx context.Context, fromBlock int64) error {

// LatestConfigDetails returns the latest config details from the logs
func (cp *configPoller) LatestConfigDetails(ctx context.Context) (changedInBlock uint64, configDigest ocrtypes.ConfigDigest, err error) {
latest, err := cp.destChainLogPoller.LatestLogByEventSigWithConfs(ConfigSet, cp.aggregatorContractAddr, 1, pg.WithParentCtx(ctx))
latest, err := cp.destChainLogPoller.LatestLogByEventSigWithConfs(cp.ld.EventSig(), cp.aggregatorContractAddr, 1, pg.WithParentCtx(ctx))
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
if cp.isConfigStoreAvailable() {
Expand All @@ -176,7 +137,7 @@ func (cp *configPoller) LatestConfigDetails(ctx context.Context) (changedInBlock
}
return 0, ocrtypes.ConfigDigest{}, err
}
latestConfigSet, err := configFromLog(latest.Data)
latestConfigSet, err := cp.ld.Decode(latest.Data)
if err != nil {
return 0, ocrtypes.ConfigDigest{}, err
}
Expand All @@ -185,7 +146,7 @@ func (cp *configPoller) LatestConfigDetails(ctx context.Context) (changedInBlock

// LatestConfig returns the latest config from the logs on a certain block
func (cp *configPoller) LatestConfig(ctx context.Context, changedInBlock uint64) (ocrtypes.ContractConfig, error) {
lgs, err := cp.destChainLogPoller.Logs(int64(changedInBlock), int64(changedInBlock), ConfigSet, cp.aggregatorContractAddr, pg.WithParentCtx(ctx))
lgs, err := cp.destChainLogPoller.Logs(int64(changedInBlock), int64(changedInBlock), cp.ld.EventSig(), cp.aggregatorContractAddr, pg.WithParentCtx(ctx))
if err != nil {
return ocrtypes.ContractConfig{}, err
}
Expand All @@ -196,7 +157,7 @@ func (cp *configPoller) LatestConfig(ctx context.Context, changedInBlock uint64)
}
return ocrtypes.ContractConfig{}, fmt.Errorf("no logs found for config on contract %s (chain %s) at block %d", cp.aggregatorContractAddr.Hex(), cp.client.ConfiguredChainID().String(), changedInBlock)
}
latestConfigSet, err := configFromLog(lgs[len(lgs)-1].Data)
latestConfigSet, err := cp.ld.Decode(lgs[len(lgs)-1].Data)
if err != nil {
return ocrtypes.ContractConfig{}, err
}
Expand Down
14 changes: 8 additions & 6 deletions core/services/relay/evm/config_poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func TestConfigPoller(t *testing.T) {
var linkTokenAddress common.Address
var accessAddress common.Address

ld := OCR2AggregatorLogDecoder

{
key, err := crypto.GenerateKey()
require.NoError(t, err)
Expand Down Expand Up @@ -92,7 +94,7 @@ func TestConfigPoller(t *testing.T) {
}

t.Run("LatestConfig errors if there is no config in logs and config store is unconfigured", func(t *testing.T) {
cp, err := NewConfigPoller(lggr, ethClient, lp, ocrAddress, nil)
cp, err := NewConfigPoller(lggr, CPConfig{ethClient, lp, ocrAddress, nil, ld})
require.NoError(t, err)

_, err = cp.LatestConfig(testutils.Context(t), 0)
Expand All @@ -101,7 +103,7 @@ func TestConfigPoller(t *testing.T) {
})

t.Run("happy path (with config store)", func(t *testing.T) {
cp, err := NewConfigPoller(lggr, ethClient, lp, ocrAddress, &configStoreContractAddr)
cp, err := NewConfigPoller(lggr, CPConfig{ethClient, lp, ocrAddress, &configStoreContractAddr, ld})
require.NoError(t, err)
// Should have no config to begin with.
_, configDigest, err := cp.LatestConfigDetails(testutils.Context(t))
Expand Down Expand Up @@ -172,7 +174,7 @@ func TestConfigPoller(t *testing.T) {
mp.On("LatestLogByEventSigWithConfs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, sql.ErrNoRows)

t.Run("if callLatestConfigDetails succeeds", func(t *testing.T) {
cp, err := newConfigPoller(lggr, ethClient, mp, ocrAddress, &configStoreContractAddr)
cp, err := newConfigPoller(lggr, ethClient, mp, ocrAddress, &configStoreContractAddr, ld)
require.NoError(t, err)

t.Run("when config has not been set, returns zero values", func(t *testing.T) {
Expand Down Expand Up @@ -209,7 +211,7 @@ func TestConfigPoller(t *testing.T) {
failingClient := new(evmClientMocks.Client)
failingClient.On("ConfiguredChainID").Return(big.NewInt(42))
failingClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("something exploded"))
cp, err := newConfigPoller(lggr, failingClient, mp, ocrAddress, &configStoreContractAddr)
cp, err := newConfigPoller(lggr, failingClient, mp, ocrAddress, &configStoreContractAddr, ld)
require.NoError(t, err)

cp.configStoreContractAddr = &configStoreContractAddr
Expand Down Expand Up @@ -248,7 +250,7 @@ func TestConfigPoller(t *testing.T) {
mp.On("LatestLogByEventSigWithConfs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, sql.ErrNoRows)

t.Run("if callReadConfig succeeds", func(t *testing.T) {
cp, err := newConfigPoller(lggr, ethClient, mp, ocrAddress, &configStoreContractAddr)
cp, err := newConfigPoller(lggr, ethClient, mp, ocrAddress, &configStoreContractAddr, ld)
require.NoError(t, err)

t.Run("when config has not been set, returns error", func(t *testing.T) {
Expand Down Expand Up @@ -310,7 +312,7 @@ func TestConfigPoller(t *testing.T) {
// initial call to retrieve config store address from aggregator
return *callArgs.To == ocrAddress
}), mock.Anything).Return(nil, errors.New("something exploded")).Once()
cp, err := newConfigPoller(lggr, failingClient, mp, ocrAddress, &configStoreContractAddr)
cp, err := newConfigPoller(lggr, failingClient, mp, ocrAddress, &configStoreContractAddr, ld)
require.NoError(t, err)

_, err = cp.LatestConfig(testutils.Context(t), 0)
Expand Down
Loading

0 comments on commit 2380220

Please sign in to comment.