Skip to content

Commit

Permalink
fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
infiloop2 committed Sep 25, 2023
1 parent 65a8d05 commit e4e7d2a
Show file tree
Hide file tree
Showing 8 changed files with 541 additions and 104 deletions.
50 changes: 45 additions & 5 deletions core/internal/features/ocr2/features_ocr2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import (
"github.com/onsi/gomega"
"github.com/smartcontractkit/libocr/commontypes"
"github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator"
testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator"
confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper"
ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"

"github.com/smartcontractkit/libocr/bigbigendian"
testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator"
confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper"
ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types"

"github.com/smartcontractkit/chainlink/v2/core/assets"
"github.com/smartcontractkit/chainlink/v2/core/bridges"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders"
Expand Down Expand Up @@ -238,12 +240,19 @@ func TestIntegration_OCR2(t *testing.T) {
require.NoError(t, err)
blockBeforeConfig, err := b.BlockByNumber(testutils.Context(t), nil)
require.NoError(t, err)
signers, transmitters, threshold, onchainConfig, encodedConfigVersion, encodedConfig, err := confighelper2.ContractSetConfigArgsForEthereumIntegrationTest(
signers, transmitters, threshold, _, encodedConfigVersion, encodedConfig, err := confighelper2.ContractSetConfigArgsForEthereumIntegrationTest(
oracles,
1,
1000000000/100, // threshold PPB
)
require.NoError(t, err)

minAnswer, maxAnswer := new(big.Int), new(big.Int)
minAnswer.Exp(big.NewInt(-2), big.NewInt(191), nil)
maxAnswer.Exp(big.NewInt(2), big.NewInt(191), nil)
maxAnswer.Sub(maxAnswer, big.NewInt(1))

onchainConfig := generateDefaultOCR2OnchainConfig(t, minAnswer, maxAnswer)
lggr.Debugw("Setting Config on Oracle Contract",
"signers", signers,
"transmitters", transmitters,
Expand Down Expand Up @@ -506,13 +515,20 @@ func TestIntegration_OCR2_ForwarderFlow(t *testing.T) {
require.NoError(t, err)
blockBeforeConfig, err := b.BlockByNumber(testutils.Context(t), nil)
require.NoError(t, err)
signers, effectiveTransmitters, threshold, onchainConfig, encodedConfigVersion, encodedConfig, err := confighelper2.ContractSetConfigArgsForEthereumIntegrationTest(
signers, effectiveTransmitters, threshold, _, encodedConfigVersion, encodedConfig, err := confighelper2.ContractSetConfigArgsForEthereumIntegrationTest(
oracles,
1,
1000000000/100, // threshold PPB
)
require.NoError(t, err)

minAnswer, maxAnswer := new(big.Int), new(big.Int)
minAnswer.Exp(big.NewInt(-2), big.NewInt(191), nil)
maxAnswer.Exp(big.NewInt(2), big.NewInt(191), nil)
maxAnswer.Sub(maxAnswer, big.NewInt(1))

onchainConfig := generateDefaultOCR2OnchainConfig(t, minAnswer, maxAnswer)

lggr.Debugw("Setting Config on Oracle Contract",
"signers", signers,
"transmitters", transmitters,
Expand Down Expand Up @@ -722,4 +738,28 @@ juelsPerFeeCoinSource = """
assert.Equal(t, digestAndEpoch.Epoch, epoch)
}

func generateDefaultOCR2OnchainConfig(t *testing.T, minValue *big.Int, maxValue *big.Int) []byte {
serializedConfig := make([]byte, 0)

s1, err := bigbigendian.SerializeSigned(1, big.NewInt(1)) //version
if err != nil {
t.Fatal(err)
}
serializedConfig = append(serializedConfig, s1...)

s2, err := bigbigendian.SerializeSigned(24, minValue) //min
if err != nil {
t.Fatal(err)
}
serializedConfig = append(serializedConfig, s2...)

s3, err := bigbigendian.SerializeSigned(24, maxValue) //max
if err != nil {
t.Fatal(err)
}
serializedConfig = append(serializedConfig, s3...)

return serializedConfig
}

func ptr[T any](v T) *T { return &v }
143 changes: 127 additions & 16 deletions core/services/relay/evm/config_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,41 @@ package evm
import (
"context"
"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"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator"
"github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple"
ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/pg"
evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/utils"
)

// ConfigSet Common to all OCR2 evm based contracts: https://github.com/smartcontractkit/libocr/blob/master/contract2/dev/OCR2Abstract.sol
var ConfigSet common.Hash
var (
failedRPCContractCalls = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "ocr2_failed_rpc_contract_calls",
Help: "Running count of failed RPC contract calls by chain/contract",
},
[]string{"chainID", "contractAddress"},
)
)

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

var defaultABI abi.ABI
defaultABI abi.ABI
)

const configSetEventName = "ConfigSet"

Expand Down Expand Up @@ -50,7 +68,7 @@ func configFromLog(logData []byte) (ocrtypes.ContractConfig, error) {

var transmitAccounts []ocrtypes.Account
for _, addr := range unpacked.Transmitters {
transmitAccounts = append(transmitAccounts, ocrtypes.Account(addr.String()))
transmitAccounts = append(transmitAccounts, ocrtypes.Account(addr.Hex()))
}
var signers []ocrtypes.OnchainPublicKey
for _, addr := range unpacked.Signers {
Expand All @@ -71,36 +89,63 @@ func configFromLog(logData []byte) (ocrtypes.ContractConfig, error) {
}

type configPoller struct {
utils.StartStopOnce

lggr logger.Logger
filterName string
destChainLogPoller logpoller.LogPoller
addr common.Address
client client.Client

aggregatorContractAddr common.Address
aggregatorContract *ocr2aggregator.OCR2Aggregator

// Some chains "manage" state bloat by deleting older logs. The ConfigStore
// contract allows us work around such restrictions.
configStoreContractAddr *common.Address
configStoreContract *ocrconfigurationstoreevmsimple.OCRConfigurationStoreEVMSimple
}

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

func NewConfigPoller(lggr logger.Logger, destChainPoller logpoller.LogPoller, addr common.Address) (evmRelayTypes.ConfigPoller, error) {
err := destChainPoller.RegisterFilter(logpoller.Filter{Name: configPollerFilterName(addr), EventSigs: []common.Hash{ConfigSet}, Addresses: []common.Address{addr}})
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)
}

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}})
if err != nil {
return nil, err
}

aggregatorContract, err := ocr2aggregator.NewOCR2Aggregator(aggregatorContractAddr, client)
if err != nil {
return nil, err
}

cp := &configPoller{
lggr: lggr,
filterName: configPollerFilterName(addr),
destChainLogPoller: destChainPoller,
addr: addr,
lggr: lggr,
filterName: configPollerFilterName(aggregatorContractAddr),
destChainLogPoller: destChainPoller,
aggregatorContractAddr: aggregatorContractAddr,
client: client,
aggregatorContract: aggregatorContract,
}

if configStoreAddr != nil {
cp.configStoreContractAddr = configStoreAddr
cp.configStoreContract, err = ocrconfigurationstoreevmsimple.NewOCRConfigurationStoreEVMSimple(*configStoreAddr, client)
if err != nil {
return nil, err
}
}

return cp, nil
}

// Start noop method
func (cp *configPoller) Start() {}

// Close noop method
func (cp *configPoller) Close() error {
return nil
}
Expand All @@ -117,10 +162,14 @@ 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.addr, 1, pg.WithParentCtx(ctx))
latest, err := cp.destChainLogPoller.LatestLogByEventSigWithConfs(ConfigSet, cp.aggregatorContractAddr, 1, pg.WithParentCtx(ctx))
if err != nil {
// If contract is not configured, we will not have the log.
if errors.Is(err, sql.ErrNoRows) {
if cp.isConfigStoreAvailable() {
// Fallback to RPC call in case logs have been pruned and configStoreContract is available
return cp.callLatestConfigDetails(ctx)
}
// log not found means return zero config digest
return 0, ocrtypes.ConfigDigest{}, nil
}
return 0, ocrtypes.ConfigDigest{}, err
Expand All @@ -134,10 +183,17 @@ 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.addr, pg.WithParentCtx(ctx))
lgs, err := cp.destChainLogPoller.Logs(int64(changedInBlock), int64(changedInBlock), ConfigSet, cp.aggregatorContractAddr, pg.WithParentCtx(ctx))
if err != nil {
return ocrtypes.ContractConfig{}, err
}
if len(lgs) == 0 {
if cp.isConfigStoreAvailable() {
// Fallback to RPC call in case logs have been pruned
return cp.callReadConfigFromStore(ctx)
}
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)
if err != nil {
return ocrtypes.ContractConfig{}, err
Expand All @@ -157,3 +213,58 @@ func (cp *configPoller) LatestBlockHeight(ctx context.Context) (blockHeight uint
}
return uint64(latest), nil
}

func (cp *configPoller) isConfigStoreAvailable() bool {
return cp.configStoreContract != nil
}

// RPC call for latest config details
func (cp *configPoller) callLatestConfigDetails(ctx context.Context) (changedInBlock uint64, configDigest ocrtypes.ConfigDigest, err error) {
details, err := cp.aggregatorContract.LatestConfigDetails(&bind.CallOpts{
Context: ctx,
})
if err != nil {
failedRPCContractCalls.WithLabelValues(cp.client.ConfiguredChainID().String(), cp.aggregatorContractAddr.Hex()).Inc()
}
return uint64(details.BlockNumber), details.ConfigDigest, err
}

// RPC call to read config from config store contract
func (cp *configPoller) callReadConfigFromStore(ctx context.Context) (cfg ocrtypes.ContractConfig, err error) {
_, configDigest, err := cp.LatestConfigDetails(ctx)
if err != nil {
failedRPCContractCalls.WithLabelValues(cp.client.ConfiguredChainID().String(), cp.aggregatorContractAddr.Hex()).Inc()
return cfg, fmt.Errorf("failed to get latest config details: %w", err)
}
if configDigest == (ocrtypes.ConfigDigest{}) {
return cfg, fmt.Errorf("config details missing while trying to lookup config in store; no logs found for contract %s (chain %s)", cp.aggregatorContractAddr.Hex(), cp.client.ConfiguredChainID().String())
}

storedConfig, err := cp.configStoreContract.ReadConfig(&bind.CallOpts{
Context: ctx,
}, configDigest)
if err != nil {
failedRPCContractCalls.WithLabelValues(cp.client.ConfiguredChainID().String(), cp.configStoreContractAddr.Hex()).Inc()
return cfg, fmt.Errorf("failed to read config from config store contract: %w", err)
}

signers := make([]ocrtypes.OnchainPublicKey, len(storedConfig.Signers))
for i := range signers {
signers[i] = storedConfig.Signers[i].Bytes()
}
transmitters := make([]ocrtypes.Account, len(storedConfig.Transmitters))
for i := range transmitters {
transmitters[i] = ocrtypes.Account(storedConfig.Transmitters[i].Hex())
}

return ocrtypes.ContractConfig{
ConfigDigest: configDigest,
ConfigCount: uint64(storedConfig.ConfigCount),
Signers: signers,
Transmitters: transmitters,
F: storedConfig.F,
OnchainConfig: storedConfig.OnchainConfig,
OffchainConfigVersion: storedConfig.OffchainConfigVersion,
OffchainConfig: storedConfig.OffchainConfig,
}, err
}
Loading

0 comments on commit e4e7d2a

Please sign in to comment.