From 947dfc2d0b7ecfda785ff15f400bd3ae6ae970d6 Mon Sep 17 00:00:00 2001 From: pavel-raykov Date: Thu, 21 May 2026 11:02:16 +0200 Subject: [PATCH 1/4] Minor. --- .../channel_definition_cache_factory.go | 45 - .../channel_definition_cache_factory_test.go | 60 - .../onchain_channel_definition_cache.go | 983 ---------- .../onchain_channel_definition_cache_test.go | 1625 ----------------- .../static_channel_definitions_cache.go | 57 - core/services/llo/cleanup.go | 5 +- core/services/llo/cleanup_test.go | 3 +- core/services/llo/orm.go | 68 - core/services/llo/orm_test.go | 232 --- ...annel_definition_cache_integration_test.go | 6 +- core/services/relay/dummy/relayer.go | 2 +- core/services/relay/evm/evm.go | 4 +- core/services/relay/evm/llo_provider.go | 2 +- 13 files changed, 12 insertions(+), 3080 deletions(-) delete mode 100644 core/services/llo/channeldefinitions/channel_definition_cache_factory.go delete mode 100644 core/services/llo/channeldefinitions/channel_definition_cache_factory_test.go delete mode 100644 core/services/llo/channeldefinitions/onchain_channel_definition_cache.go delete mode 100644 core/services/llo/channeldefinitions/onchain_channel_definition_cache_test.go delete mode 100644 core/services/llo/channeldefinitions/static_channel_definitions_cache.go delete mode 100644 core/services/llo/orm.go delete mode 100644 core/services/llo/orm_test.go diff --git a/core/services/llo/channeldefinitions/channel_definition_cache_factory.go b/core/services/llo/channeldefinitions/channel_definition_cache_factory.go deleted file mode 100644 index 0de0f5e4685..00000000000 --- a/core/services/llo/channeldefinitions/channel_definition_cache_factory.go +++ /dev/null @@ -1,45 +0,0 @@ -package channeldefinitions - -import ( - "net/http" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - - lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/config" - "github.com/smartcontractkit/chainlink-evm/pkg/logpoller" -) - -type ChannelDefinitionCacheFactory interface { - NewCache(cfg lloconfig.PluginConfig) (llotypes.ChannelDefinitionCache, error) -} - -var _ ChannelDefinitionCacheFactory = &channelDefinitionCacheFactory{} - -func NewChannelDefinitionCacheFactory(lggr logger.Logger, orm ChannelDefinitionCacheORM, lp logpoller.LogPoller, client *http.Client) ChannelDefinitionCacheFactory { - return &channelDefinitionCacheFactory{ - lggr, - orm, - lp, - client, - } -} - -type channelDefinitionCacheFactory struct { - lggr logger.Logger - orm ChannelDefinitionCacheORM - lp logpoller.LogPoller - client *http.Client -} - -func (f *channelDefinitionCacheFactory) NewCache(cfg lloconfig.PluginConfig) (llotypes.ChannelDefinitionCache, error) { - if cfg.ChannelDefinitions != "" { - return NewStaticChannelDefinitionCache(f.lggr, cfg.ChannelDefinitions) - } - - addr := cfg.ChannelDefinitionsContractAddress - fromBlock := cfg.ChannelDefinitionsContractFromBlock - donID := cfg.DonID - - return NewChannelDefinitionCache(f.lggr, f.orm, f.client, f.lp, addr, donID, fromBlock), nil -} diff --git a/core/services/llo/channeldefinitions/channel_definition_cache_factory_test.go b/core/services/llo/channeldefinitions/channel_definition_cache_factory_test.go deleted file mode 100644 index 1fc13abd01d..00000000000 --- a/core/services/llo/channeldefinitions/channel_definition_cache_factory_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package channeldefinitions - -import ( - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - - lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/config" -) - -func Test_ChannelDefinitionCacheFactory(t *testing.T) { - lggr := logger.Test(t) - cdcFactory := NewChannelDefinitionCacheFactory(lggr, nil, nil, nil) - - t.Run("NewCache", func(t *testing.T) { - t.Run("when ChannelDefinitions is present, returns static cache", func(t *testing.T) { - _, err := cdcFactory.NewCache(lloconfig.PluginConfig{ChannelDefinitions: "..."}) - require.EqualError(t, err, "failed to unmarshal static channel definitions: invalid character '.' looking for beginning of value") - - cdc, err := cdcFactory.NewCache(lloconfig.PluginConfig{ChannelDefinitions: "{}"}) - require.NoError(t, err) - require.IsType(t, &staticCDC{}, cdc) - }) - t.Run("when ChannelDefinitions is not present, returns dynamic cache", func(t *testing.T) { - cdc, err := cdcFactory.NewCache(lloconfig.PluginConfig{ - ChannelDefinitionsContractAddress: common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - DonID: 1, - }) - require.NoError(t, err) - require.IsType(t, &channelDefinitionCache{}, cdc) - - // creates another one if you try to do it again with the same addr/donID - cdc, err = cdcFactory.NewCache(lloconfig.PluginConfig{ - ChannelDefinitionsContractAddress: common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - DonID: 1, - }) - require.NoError(t, err) - require.IsType(t, &channelDefinitionCache{}, cdc) - - // is fine if you do it again with different addr - cdc, err = cdcFactory.NewCache(lloconfig.PluginConfig{ - ChannelDefinitionsContractAddress: common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - DonID: 1, - }) - require.NoError(t, err) - require.IsType(t, &channelDefinitionCache{}, cdc) - - // is fine if you do it again with different don ID - cdc, err = cdcFactory.NewCache(lloconfig.PluginConfig{ - ChannelDefinitionsContractAddress: common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - DonID: 2, - }) - require.NoError(t, err) - require.IsType(t, &channelDefinitionCache{}, cdc) - }) - }) -} diff --git a/core/services/llo/channeldefinitions/onchain_channel_definition_cache.go b/core/services/llo/channeldefinitions/onchain_channel_definition_cache.go deleted file mode 100644 index cb4b0b32884..00000000000 --- a/core/services/llo/channeldefinitions/onchain_channel_definition_cache.go +++ /dev/null @@ -1,983 +0,0 @@ -package channeldefinitions - -import ( - "bytes" - "context" - "database/sql" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "maps" - "math/big" - "net/http" - "net/url" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/jpillora/backoff" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "golang.org/x/crypto/sha3" - - clhttp "github.com/smartcontractkit/chainlink-common/pkg/http" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - "github.com/smartcontractkit/chainlink-common/pkg/types/query" - "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" - "github.com/smartcontractkit/chainlink-data-streams/llo/types" - "github.com/smartcontractkit/chainlink-evm/gethwrappers/llo-feeds/generated/channel_config_store" - "github.com/smartcontractkit/chainlink-evm/pkg/logpoller" -) - -const ( - // MaxChannelDefinitionsFileSize is a sanity limit to avoid OOM for a - // maliciously large file. It should be much larger than any real expected - // channel definitions file. - MaxChannelDefinitionsFileSize = 25 * 1024 * 1024 // 25MB - // How often we query logpoller for new logs - defaultLogPollInterval = 1 * time.Second - // dbPersistLoopInterval is the interval at which we check for failed persistence and attempt to save again - dbPersistLoopInterval = 1 * time.Second - // defaultFetchTimeout is the default timeout for fetching channel definitions. - defaultFetchTimeout = 15 * time.Second - // fetchRetryTimeout is the timeout for retrying to fetch channel definitions. - fetchRetryTimeout = 4 * defaultFetchTimeout - - // MaxChannelsPerAdder is the maximum number of channels allowed per adder source. This limit - // is enforced based on existing channels from the same source in currentDefinitions plus new - // channels being added incrementally. The limit check occurs during processing, not on the - // total file size. - MaxChannelsPerAdder = 100 - - // newChannelDefinitionEventName is the ABI event name for NewChannelDefinition events. - newChannelDefinitionEventName = "NewChannelDefinition" - // channelDefinitionAddedEventName is the ABI event name for ChannelDefinitionAdded events. - channelDefinitionAddedEventName = "ChannelDefinitionAdded" - - // SourceOwner represents the owner source for channel definitions, which has full authority. - // This defaults to 0 as the SourceOwner has no adder ID and is the contract owner. - // Only NewChannelDefinition events are marked as SourceOwner. - SourceOwner uint32 = 0 - - // SingleChannelDefinitionsFormat is the format of the channel definitions for a single source. - SingleChannelDefinitionsFormat uint32 = 0 - - // MultiChannelDefinitionsFormat is the format of the channel definitions for multiple sources. - MultiChannelDefinitionsFormat uint32 = 1 -) - -var ( - // channelConfigStoreABI is the parsed ABI for the ChannelConfigStore contract. - channelConfigStoreABI abi.ABI - // NewChannelDefinition is the topic hash for the NewChannelDefinition event. - NewChannelDefinition = (channel_config_store.ChannelConfigStoreNewChannelDefinition{}).Topic() - // ChannelDefinitionAdded is the topic hash for the ChannelDefinitionAdded event. - ChannelDefinitionAdded = (channel_config_store.ChannelConfigStoreChannelDefinitionAdded{}).Topic() - // NoLimitSortAsc is a query configuration that sorts results by sequence in ascending order with no limit. - NoLimitSortAsc = query.NewLimitAndSort(query.Limit{}, query.NewSortBySequence(query.Asc)) - - channelDefinitionCacheCount = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Namespace: "llo", - Subsystem: "channeldefinitions", - Name: "channel_definition_cache_count", - Help: "Current count of channel definitions in the cache", - }, - []string{"source"}, - ) -) - -func init() { - var err error - channelConfigStoreABI, err = abi.JSON(strings.NewReader(channel_config_store.ChannelConfigStoreABI)) - if err != nil { - panic(err) - } -} - -type ChannelDefinitionCacheORM interface { - LoadChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (pd *types.PersistedDefinitions, err error) - StoreChannelDefinitions(ctx context.Context, addr common.Address, donID, version uint32, dfns json.RawMessage, blockNum int64, format uint32) (err error) - CleanupChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) error -} - -var _ llotypes.ChannelDefinitionCache = &channelDefinitionCache{} - -// LogPoller is an interface for querying blockchain logs. It provides methods to get the latest block, -// filter logs by expressions, and manage log filters. -type LogPoller interface { - LatestBlock(ctx context.Context) (logpoller.Block, error) - FilteredLogs(ctx context.Context, filter []query.Expression, limitAndSort query.LimitAndSort, queryName string) ([]logpoller.Log, error) - RegisterFilter(ctx context.Context, filter logpoller.Filter) error - UnregisterFilter(ctx context.Context, filterName string) error -} - -// Option is a function type for configuring channelDefinitionCache options. -type Option func(*channelDefinitionCache) - -// WithLogPollInterval returns an Option that sets the log polling interval for the cache. -func WithLogPollInterval(d time.Duration) Option { - return func(c *channelDefinitionCache) { - c.logPollInterval = d - } -} - -// Definitions holds the in-memory state of channel definitions for a channel definition cache. -// It tracks the latest block number processed, the version (for owner sources), and -// source definitions keyed by source ID. -type Definitions struct { - LastBlockNum int64 // The latest block number from which channel definitions were processed - Version uint32 // The version number from the owner source (only updated for SourceOwner) - Sources map[uint32]types.SourceDefinition // Channel definitions grouped by source ID -} - -// channelDefinitionCache maintains an in-memory cache of channel definitions fetched from on-chain -// events and external URLs. It polls the blockchain for new channel definition events, fetches -// definitions from URLs, verifies SHA hashes, merges definitions from multiple sources according -// to authority rules, and persists source definitions (map[uint32]types.SourceDefinition) to the database. -type channelDefinitionCache struct { - services.StateMachine - - orm ChannelDefinitionCacheORM - client HTTPClient - httpLimit int64 - - filterName string - lp LogPoller - logPollInterval time.Duration - addr common.Address - donID uint32 - donIDTopic common.Hash - ownerFilterExprs []query.Expression - adderFilterExprs []query.Expression - lggr logger.SugaredLogger - initialBlockNum int64 - - fetchTriggerCh chan types.Trigger - - definitionsMu sync.RWMutex - definitions Definitions - - persistMu sync.RWMutex - persistedBlockNum int64 - - wg sync.WaitGroup - chStop services.StopChan -} - -// HTTPClient is an interface for making HTTP requests. It matches the standard library's -// http.Client interface. -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} - -// NewChannelDefinitionCache creates a new channel definition cache that monitors on-chain events -// for channel definition updates. It configures log polling filters for both owner and adder events, -// sets up the initial state, and applies any provided options. The cache must be started via Start() -// before it begins polling and fetching definitions. -func NewChannelDefinitionCache(lggr logger.Logger, orm ChannelDefinitionCacheORM, client HTTPClient, lp logpoller.LogPoller, addr common.Address, donID uint32, fromBlock int64, options ...Option) llotypes.ChannelDefinitionCache { - cdc := &channelDefinitionCache{ - orm: orm, - client: client, - httpLimit: MaxChannelDefinitionsFileSize, - filterName: ChannelDefinitionCacheFilterName(addr, donID), - lp: lp, - logPollInterval: defaultLogPollInterval, - addr: addr, - donID: donID, - donIDTopic: common.BigToHash(big.NewInt(int64(donID))), - lggr: logger.Sugared(lggr).Named("ChannelDefinitionCache").With("addr", addr, "fromBlock", fromBlock), - fetchTriggerCh: make(chan types.Trigger, 1), - initialBlockNum: fromBlock, - chStop: make(chan struct{}), - definitions: Definitions{ - Sources: make(map[uint32]types.SourceDefinition), - }, - } - - cdc.ownerFilterExprs = []query.Expression{ - logpoller.NewAddressFilter(addr), - logpoller.NewEventSigFilter(NewChannelDefinition), - logpoller.NewEventByTopicFilter(1, []logpoller.HashedValueComparator{ - {Values: []common.Hash{cdc.donIDTopic}, Operator: primitives.Eq}, - }), - // Optimize for fast pickup of new channel definitions. - // On Arbitrum, finalization can take a long time. - query.Confidence(primitives.Unconfirmed), - } - - cdc.adderFilterExprs = []query.Expression{ - logpoller.NewAddressFilter(addr), - logpoller.NewEventSigFilter(ChannelDefinitionAdded), - logpoller.NewEventByTopicFilter(1, []logpoller.HashedValueComparator{ - {Values: []common.Hash{cdc.donIDTopic}, Operator: primitives.Eq}, - }), - // Optimize for fast pickup of new channel definitions. - // On Arbitrum, finalization can take a long time. - query.Confidence(primitives.Unconfirmed), - } - - for _, option := range options { - option(cdc) - } - return cdc -} - -// Start initializes the channel definition cache by loading persisted state from the database, -// registering logpoller filters, and launching three concurrent asynchronous loops: -// 1. pollChainLoop: Periodically queries logpoller for new channel definition events -// 2. fetchLatestLoop: Receives fetch triggers and coordinates fetching definitions from URLs -// 3. persistLoop: Periodically persists the in-memory source definitions to the database -// All loops run until the cache is stopped via Close(). -func (c *channelDefinitionCache) Start(ctx context.Context) error { - return c.StartOnce("ChannelDefinitionCache", func() (err error) { - err = c.lp.RegisterFilter(ctx, logpoller.Filter{ - Name: c.filterName, - EventSigs: []common.Hash{NewChannelDefinition, ChannelDefinitionAdded}, - Topic2: []common.Hash{c.donIDTopic}, - Addresses: []common.Address{c.addr}, - }) - - if err != nil { - return err - } - - var pd *types.PersistedDefinitions - if pd, err = c.orm.LoadChannelDefinitions(ctx, c.addr, c.donID); err != nil { - return err - } - - c.definitions.Sources = make(map[uint32]types.SourceDefinition) - if pd != nil { - if pd.Format == MultiChannelDefinitionsFormat { - var sources map[uint32]types.SourceDefinition - if sources, err = decodePersistedSourceDefinitions(pd.Definitions); err != nil { - return err - } - c.definitions.Sources = sources - } - c.definitions.Version = pd.Version - c.definitions.LastBlockNum = pd.BlockNum - c.persistedBlockNum = pd.BlockNum - if pd.BlockNum+1 > c.initialBlockNum { - c.initialBlockNum = pd.BlockNum - } - } - - c.lggr.Infow("started channel definition cache", "definitions", c.definitions, "initialBlockNum", c.initialBlockNum, "persistedBlockNum", c.persistedBlockNum, "definitionsVersion", c.definitions.Version) - - c.wg.Add(3) - // We have three concurrent loops - // 1. Poll chain for new logs - // 2. Fetch latest definitions from URL and verify SHA, according to latest log - // 3. Persist definitions to database - go c.pollChainLoop() - go c.fetchLatestLoop() - go c.persistLoop() - return nil - }) -} - -// blockNumFromUint64 converts a uint64 block number to int64. -// This is safe as block numbers are well within int64 range. -func blockNumFromUint64(blockNum uint64) int64 { - //nolint:gosec // disable G115 - return int64(blockNum) -} - -// unpackOwnerLog unpacks and validates an owner log from logpoller. -// Returns the unpacked log and an error if unpacking or validation fails. -func (c *channelDefinitionCache) unpackOwnerLog(log logpoller.Log) (*channel_config_store.ChannelConfigStoreNewChannelDefinition, error) { - if log.EventSig != NewChannelDefinition { - return nil, fmt.Errorf("log event signature mismatch: expected %x, got %x", NewChannelDefinition, log.EventSig) - } - - unpacked := new(channel_config_store.ChannelConfigStoreNewChannelDefinition) - err := channelConfigStoreABI.UnpackIntoInterface(unpacked, newChannelDefinitionEventName, log.Data) - if err != nil { - return nil, fmt.Errorf("failed to unpack log data: %w", err) - } - - if len(log.Topics) < 2 { - return nil, fmt.Errorf("log missing expected topics: got %d, expected at least 2", len(log.Topics)) - } - - unpacked.DonId = new(big.Int).SetBytes(log.Topics[1]) - //nolint:gosec // disable G115 - unpacked.Raw.BlockNumber = uint64(log.BlockNumber) - - // Validate donID matches - if unpacked.DonId.Cmp(big.NewInt(int64(c.donID))) != 0 { - return nil, fmt.Errorf("donID mismatch: expected %d, got %s", c.donID, unpacked.DonId.String()) - } - - return unpacked, nil -} - -// unpackAdderLog unpacks and validates an adder log from logpoller. -// Returns the unpacked log and an error if unpacking or validation fails. -func (c *channelDefinitionCache) unpackAdderLog(log logpoller.Log) (*channel_config_store.ChannelConfigStoreChannelDefinitionAdded, error) { - if log.EventSig != ChannelDefinitionAdded { - return nil, fmt.Errorf("log event signature mismatch: expected %x, got %x", ChannelDefinitionAdded, log.EventSig) - } - - unpacked := new(channel_config_store.ChannelConfigStoreChannelDefinitionAdded) - err := channelConfigStoreABI.UnpackIntoInterface(unpacked, channelDefinitionAddedEventName, log.Data) - if err != nil { - return nil, fmt.Errorf("failed to unpack adder log data: %w", err) - } - - if len(log.Topics) < 3 { - return nil, fmt.Errorf("adder log missing expected topics: got %d, expected at least 3", len(log.Topics)) - } - - unpacked.DonId = new(big.Int).SetBytes(log.Topics[1]) - //nolint:gosec // disable G115 - unpacked.ChannelAdderId = uint32(new(big.Int).SetBytes(log.Topics[2]).Uint64()) - //nolint:gosec // disable G115 - unpacked.Raw.BlockNumber = uint64(log.BlockNumber) - - // Validate donID matches - if unpacked.DonId.Cmp(big.NewInt(int64(c.donID))) != 0 { - return nil, fmt.Errorf("donID mismatch: expected %d, got %s", c.donID, unpacked.DonId.String()) - } - - return unpacked, nil -} - -// buildFilterExprs builds filter expressions by appending block range filters to base expressions. -func buildFilterExprs(baseExprs []query.Expression, fromBlock, toBlock int64) []query.Expression { - exprs := make([]query.Expression, 0, len(baseExprs)+2) - exprs = append(exprs, baseExprs...) - exprs = append(exprs, - query.Block(strconv.FormatInt(fromBlock, 10), primitives.Gte), - query.Block(strconv.FormatInt(toBlock, 10), primitives.Lte), - ) - return exprs -} - -// pollChainLoop is an asynchronous goroutine that periodically polls logpoller for new channel -// definition events (both owner and adder events). It processes logs sequentially by block number, -// unpacks them into fetch triggers, and sends triggers to the fetch channel for asynchronous -// processing. The loop runs until the cache is stopped, with failures logged and retried on -// the next polling interval. -func (c *channelDefinitionCache) pollChainLoop() { - defer c.wg.Done() - - ctx, cancel := c.chStop.NewCtx() - defer cancel() - - pollT := services.NewTicker(c.logPollInterval) - defer pollT.Stop() - - for { - select { - case <-c.chStop: - return - case <-pollT.C: - // failures will be tried again on the next tick - if err := c.readLogs(ctx); err != nil { - c.lggr.Errorw("Failed to fetch channel definitions from chain", "err", err) - continue - } - } - } -} - -// readLogs queries logpoller for new channel definition events within the block range from -// the last processed block to the latest available block. It fetches adder events -// (ChannelDefinitionAdded) and owner events (NewChannelDefinition) separately, each sorted -// individually by block number (ascending), and processes them separately by passing each -// batch to processLogs for unpacking and trigger generation. -func (c *channelDefinitionCache) readLogs(ctx context.Context) (err error) { - latestBlock, err := c.lp.LatestBlock(ctx) - if errors.Is(err, sql.ErrNoRows) { - c.lggr.Debug("Logpoller has no logs yet, skipping poll") - return nil - } else if err != nil { - return err - } - - toBlock := latestBlock.BlockNumber - fromBlock := c.scanFromBlockNum() - if toBlock <= fromBlock { - return nil - } - - exprs := buildFilterExprs(c.adderFilterExprs, fromBlock, toBlock) - logs, err := c.lp.FilteredLogs(ctx, exprs, NoLimitSortAsc, "ChannelDefinitionCachePoller - NewAdderChannelDefinition") - if err != nil { - return err - } - c.lggr.Debugw("read adder logs", "fromBlock", fromBlock, "toBlock", toBlock, "logsCount", len(logs)) - c.processLogs(logs) - - exprs = buildFilterExprs(c.ownerFilterExprs, fromBlock, toBlock) - logs, err = c.lp.FilteredLogs(ctx, exprs, NoLimitSortAsc, "ChannelDefinitionCachePoller - NewOwnerChannelDefinition") - if err != nil { - return err - } - c.lggr.Debugw("read owner logs", "fromBlock", fromBlock, "toBlock", toBlock, "logsCount", len(logs)) - c.processLogs(logs) - - return nil -} - -// scanFromBlockNum returns the next block number to scan from, ensuring no gaps between -// persisted and in-memory state. -// It returns the max between the in-memory definitions block number and the initial block number. -func (c *channelDefinitionCache) scanFromBlockNum() int64 { - c.definitionsMu.RLock() - defer c.definitionsMu.RUnlock() - return max(c.definitions.LastBlockNum, c.initialBlockNum) -} - -// processLogs unpacks channel definition logs into fetch triggers by extracting URL, SHA hash, -// block number, and source information. It validates logs and handles unpacking errors gracefully, -// continuing to process remaining logs even if individual logs fail. Valid triggers are sent to -// the fetch channel for asynchronous processing by fetchLatestLoop. -func (c *channelDefinitionCache) processLogs(logs []logpoller.Log) { - for _, log := range logs { - var trigger types.Trigger - switch log.EventSig { - case NewChannelDefinition: - unpacked, err := c.unpackOwnerLog(log) - if err != nil { - // Log warning but continue processing other logs - c.lggr.Warnw("Failed to unpack owner log", "err", err, "blockNumber", log.BlockNumber) - continue - } - trigger = types.Trigger{ - Source: SourceOwner, - URL: unpacked.Url, - SHA: unpacked.Sha, - LogIndex: log.LogIndex, - BlockNum: blockNumFromUint64(unpacked.Raw.BlockNumber), - Version: unpacked.Version, - TxHash: log.TxHash, - } - case ChannelDefinitionAdded: - unpacked, err := c.unpackAdderLog(log) - if err != nil { - // Log warning but continue processing other logs - c.lggr.Warnw("Failed to unpack adder log", "err", err, "blockNumber", log.BlockNumber) - continue - } - trigger = types.Trigger{ - Source: unpacked.ChannelAdderId, - URL: unpacked.Url, - SHA: unpacked.Sha, - LogIndex: log.LogIndex, - BlockNum: blockNumFromUint64(unpacked.Raw.BlockNumber), - TxHash: log.TxHash, - } - default: - c.lggr.Warnw("Unknown log event signature", - "blockNumber", log.BlockNumber, "eventSig", log.EventSig, "logHash", log.TxHash.Hex()) - continue - } - - c.lggr.Debugw("Got new logs", "source", trigger.Source, "url", trigger.URL, "sha", hex.EncodeToString(trigger.SHA[:]), "blockNum", trigger.BlockNum) - select { - case c.fetchTriggerCh <- trigger: - case <-c.chStop: - return - } - } -} - -type chOpts struct { - FeedID common.Hash `json:"feedID"` -} - -// extractFeedID attempts to extract the FeedID from channel options JSON. -// Returns the FeedID if found, or an empty hash if not found or if parsing fails. -func extractFeedID(opts llotypes.ChannelOpts) common.Hash { - if len(opts) == 0 { - return common.Hash{} - } - - var optsJSON chOpts - if err := json.Unmarshal(opts, &optsJSON); err != nil { - // If unmarshaling fails, return empty hash (not all channel types have FeedID) - return common.Hash{} - } - return optsJSON.FeedID -} - -// buildFeedIDMap extracts FeedIDs from channel definitions and builds a map -// from FeedID to channel ID for collision detection. -func buildFeedIDMap(definitions llotypes.ChannelDefinitions) map[common.Hash]uint32 { - feedIDToChannelID := make(map[common.Hash]uint32) - for channelID, def := range definitions { - feedID := extractFeedID(def.Opts) - if feedID != (common.Hash{}) { - feedIDToChannelID[feedID] = channelID - } - } - return feedIDToChannelID -} - -// mergeDefinitions reconciles new channel definitions with the current set according to source -// authority rules. Owner definitions (SourceOwner) have full authority: they can add, update, or -// tombstone (delete) channels. Non-tombstoned channels missing from newDefinitions are preserved; -// channels must be explicitly tombstoned to be removed. Previously tombstoned channels that are -// omitted from the owner's newDefinitions are dropped (fully removed) from currentDefinitions. -// Adder definitions (non-owner sources) have limited authority: they can only add new channels -// and cannot overwrite or tombstone existing ones. -// -// Adder limits are enforced: -// - MaxChannelsPerAdder: The limit is enforced based on existing channels from the same source -// in currentDefinitions plus new channels being added incrementally. The check occurs before -// each new channel addition. Existing channels that are already in currentDefinitions are -// skipped and do not count toward new additions. -// -// FeedID uniqueness is enforced: -// - All channels must have unique FeedIDs in their options. If a new channel has a FeedID that -// collides with an existing channel, the new channel is logged and skipped (not added). -func (c *channelDefinitionCache) mergeDefinitions(source uint32, currentDefinitions llotypes.ChannelDefinitions, newDefinitions llotypes.ChannelDefinitions, feedIDToChannelID map[common.Hash]uint32) { - // Count the number of channels for adder sources in the current definitions - var numberOfChannels uint32 - if source > SourceOwner { - for _, def := range currentDefinitions { - if def.Source == source { - numberOfChannels++ - } - } - } - - // process new definitions in a deterministic order - channelIDs := make([]llotypes.ChannelID, 0, len(newDefinitions)) - for channelID := range newDefinitions { - channelIDs = append(channelIDs, channelID) - } - sort.Slice(channelIDs, func(i, j int) bool { - return channelIDs[i] < channelIDs[j] - }) - - for _, channelID := range channelIDs { - def := newDefinitions[channelID] - - // Check for FeedID collision before adding the channel - newFeedID := extractFeedID(def.Opts) - if newFeedID != (common.Hash{}) { - if existingChannelID, exists := feedIDToChannelID[newFeedID]; exists && existingChannelID != channelID { - c.lggr.Warnw("feedID collision detected, skipping channel definition", - "channelID", channelID, "feedID", newFeedID.Hex(), "existingChannelID", existingChannelID, "source", source) - continue - } - } - - switch { - case source == SourceOwner: - currentDefinitions[channelID] = def - - // Update FeedID map after adding the channel - if newFeedID != (common.Hash{}) { - feedIDToChannelID[newFeedID] = channelID - } - - case source > SourceOwner: - if def.Tombstone { - c.lggr.Debugw("invalid channel tombstone, cannot be added by source", - "channelID", channelID, "source", source) - continue - } - - if existing, exists := currentDefinitions[channelID]; exists { - if existing.Source != def.Source { - c.lggr.Debugw("channel adder conflict, skipping definition", - "channelID", channelID, "existingSourceID", existing.Source, "newSourceID", def.Source) - } - // Adders do not overwrite existing definitions, they can only add new ones - continue - } - - // stop processing new definitions if the adder limit is exceeded - if numberOfChannels >= MaxChannelsPerAdder { - c.lggr.Warnw("adder limit exceeded, skipping remaining definitions for source", - "source", source, "numberOfChannels", numberOfChannels, "max", MaxChannelsPerAdder) - return - } - - currentDefinitions[channelID] = def - numberOfChannels++ - // Update FeedID map after adding the channel - if newFeedID != (common.Hash{}) { - feedIDToChannelID[newFeedID] = channelID - } - - default: - c.lggr.Warnw("undefined source, skipping definition", - "channelID", channelID, "source", source) - continue - } - } - - // Drop previously tombstoned channels that the owner has omitted from newDefinitions - // Only tombstoned channels are allowed to be dropped by the owner to eventually remove them from the OCR state. - if source == SourceOwner { - for channelID, def := range currentDefinitions { - if def.Tombstone { - if _, exists := newDefinitions[channelID]; !exists { - delete(currentDefinitions, channelID) - feedID := extractFeedID(def.Opts) - if feedID != (common.Hash{}) { - delete(feedIDToChannelID, feedID) - } - } - } - } - } -} - -// fetchLatestLoop is an asynchronous goroutine that receives fetch triggers from the poll chain -// loop via a channel. It coordinates fetching channel definitions from URLs, verifying SHA hashes, -// and storing them in c.definitions.Sources (the source definitions map). -// It spawns a separate goroutine (fetchLoop) for each trigger. -func (c *channelDefinitionCache) fetchLatestLoop() { - defer c.wg.Done() - - var trigger types.Trigger - for { - select { - case trigger = <-c.fetchTriggerCh: - c.wg.Add(1) - go c.fetchLoop(trigger) - - case <-c.chStop: - return - } - } -} - -// fetchLoop is a retry goroutine spawned when an initial fetch attempt fails in fetchLatestLoop. -// It uses exponential backoff to retry fetching channel definitions until either the fetch succeeds, -// fetchRetryTimeout is reached or the cache is stopped (context cache shutdown). -// This isolates retry logic from the main fetch loop, allowing it to continue processing new triggers -// while retries occur in the background. -func (c *channelDefinitionCache) fetchLoop(trigger types.Trigger) { - defer c.wg.Done() - var err error - b := newHTTPFetchBackoff() - - ctx, cancel := c.chStop.CtxWithTimeout(fetchRetryTimeout) - defer cancel() - - if err = c.fetchAndSetChannelDefinitions(ctx, trigger); err == nil { - return - } - c.lggr.Warnw("Error while fetching channel definitions", "donID", - c.donID, "err", err, "source", trigger.Source, "attempt", b.Attempt()) - - for { - select { - case <-ctx.Done(): - return - case <-time.After(b.Duration()): - if err := c.fetchAndSetChannelDefinitions(ctx, trigger); err != nil { - c.lggr.Warnw("Error while fetching channel definitions", "donID", - c.donID, "err", err, "source", trigger.Source, "attempt", b.Attempt()) - continue - } - return - } - } -} - -// fetchAndSetChannelDefinitions orchestrates fetching and storing channel definitions from a trigger. -// It checks that the trigger block number is newer than the current state to avoid processing stale -// events, fetches definitions from the URL and verifies the SHA hash, then stores them in -// c.definitions.Sources keyed by source ID. It also updates c.definitions.LastBlockNum and, for owner -// sources, c.definitions.Version. The actual merging of source definitions happens later when -// Definitions() is called. -// -// Returns an error if fetching, SHA verification, or JSON decoding fails. Note that adder limit -// checks occur during merging in Definitions(), where violations are handled by logging warnings -// and stopping processing for that source, not by returning errors. -func (c *channelDefinitionCache) fetchAndSetChannelDefinitions(ctx context.Context, trigger types.Trigger) error { - defs, err := c.fetchChannelDefinitions(ctx, trigger) - if err != nil { - return fmt.Errorf("failed to fetch channel definitions: %w", err) - } - - c.definitionsMu.Lock() - defer c.definitionsMu.Unlock() - if sourceDef, exists := c.definitions.Sources[trigger.Source]; exists { - switch { - // don't process a trigger with an earlier block number - case trigger.BlockNum < sourceDef.Trigger.BlockNum: - return nil - - // don't process a trigger with the same block number and an earlier log index - case trigger.BlockNum == sourceDef.Trigger.BlockNum && trigger.LogIndex <= sourceDef.Trigger.LogIndex: - return nil - } - } - - c.definitions.Sources[trigger.Source] = types.SourceDefinition{ - Trigger: trigger, - Definitions: defs, - } - - if trigger.Source == SourceOwner { - c.definitions.Version = trigger.Version - } - - if trigger.BlockNum > c.definitions.LastBlockNum { - c.definitions.LastBlockNum = trigger.BlockNum - } - - c.lggr.Infow("Set channel definitions for source", - "source", trigger.Source, "blockNum", trigger.BlockNum, "url", trigger.URL, "sha", hex.EncodeToString(trigger.SHA[:])) - - return nil -} - -// fetchChannelDefinitions fetches channel definitions from the URL specified in the trigger, -// verifies the response SHA3 hash matches the expected hash from the on-chain event, decodes -// the JSON response, and annotates each definition with its source identifier. Returns an -// error if the URL is invalid, the HTTP request fails, the hash verification fails, or the -// JSON cannot be decoded. -func (c *channelDefinitionCache) fetchChannelDefinitions(ctx context.Context, trigger types.Trigger) (llotypes.ChannelDefinitions, error) { - u, err := url.ParseRequestURI(trigger.URL) - if err != nil { - return nil, fmt.Errorf("failed to parse URL %s: %w", trigger.URL, err) - } - - ctx, cancel := context.WithTimeout(ctx, defaultFetchTimeout) - defer cancel() - - request, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil) - if err != nil { - return nil, fmt.Errorf("failed to create HTTP request for channel definitions URL %s: %w", trigger.URL, err) - } - request.Header.Set("Content-Type", "application/json") - - httpRequest := clhttp.Request{ - Client: c.client, - Request: request, - Config: clhttp.RequestConfig{SizeLimit: c.httpLimit}, - Logger: c.lggr.Named("HTTPRequest").With("url", trigger.URL, "expectedSHA", hex.EncodeToString(trigger.SHA[:])), - } - - reader, statusCode, _, err := httpRequest.SendRequestReader() - if err != nil { - return nil, fmt.Errorf("failed to make HTTP request to channel definitions URL %s: %w", trigger.URL, err) - } - - if statusCode >= 400 { - // NOTE: Truncate the returned body here as we don't want to spam the - // logs with potentially huge messages - body := http.MaxBytesReader(nil, reader, 1024) - defer body.Close() - bodyBytes, err := io.ReadAll(body) - if err != nil { - return nil, fmt.Errorf("HTTP error from channel definitions URL %s (status %d): failed to read response body: %w (partial body: %s)", trigger.URL, statusCode, err, bodyBytes) - } - return nil, fmt.Errorf("HTTP error from channel definitions URL %s (status %d): %s", trigger.URL, statusCode, string(bodyBytes)) - } - defer reader.Close() - - var buf bytes.Buffer - // Use a teeReader to avoid excessive copying - teeReader := io.TeeReader(reader, &buf) - - hash := sha3.New256() - // Stream the data directly into the hash and copy to buf as we go - if _, err := io.Copy(hash, teeReader); err != nil { - return nil, fmt.Errorf("failed to read channel definitions response body from %s: %w", trigger.URL, err) - } - - actualSha := hash.Sum(nil) - if !bytes.Equal(trigger.SHA[:], actualSha) { - return nil, fmt.Errorf("SHA3 mismatch for channel definitions from %s: expected %s, got %x", trigger.URL, hex.EncodeToString(trigger.SHA[:]), actualSha) - } - - var cd llotypes.ChannelDefinitions - decoder := json.NewDecoder(&buf) - if err := decoder.Decode(&cd); err != nil { - return nil, fmt.Errorf("failed to decode channel definitions JSON from %s: %w", trigger.URL, err) - } - - // Annotate each definition with its source identifier. - for channelID, def := range cd { - def.Source = trigger.Source - cd[channelID] = def - } - - return cd, nil -} - -// persist atomically writes the in-memory source definitions (c.definitions.Sources) to the database. -// Returns the memory and persisted block numbers along with any error that occurred during persistence. -func (c *channelDefinitionCache) persist(ctx context.Context) (int64, int64, error) { - c.persistMu.Lock() - defer c.persistMu.Unlock() - - c.definitionsMu.RLock() - definitions := maps.Clone(c.definitions.Sources) - definitionsBlockNum := c.definitions.LastBlockNum - definitionsVersion := c.definitions.Version - c.definitionsMu.RUnlock() - - if c.persistedBlockNum >= definitionsBlockNum { - return definitionsBlockNum, c.persistedBlockNum, nil - } - - definitionsJSON, err := json.Marshal(definitions) - if err != nil { - return definitionsBlockNum, c.persistedBlockNum, fmt.Errorf("failed to marshal definitions: %w", err) - } - - err = c.orm.StoreChannelDefinitions(ctx, c.addr, c.donID, definitionsVersion, - definitionsJSON, definitionsBlockNum, MultiChannelDefinitionsFormat) - if err != nil { - return definitionsBlockNum, c.persistedBlockNum, fmt.Errorf("failed to store definitions: %w", err) - } - - c.persistedBlockNum = definitionsBlockNum - return definitionsBlockNum, c.persistedBlockNum, nil -} - -// persistLoop is an asynchronous goroutine that periodically persists the in-memory source definitions to the database. -func (c *channelDefinitionCache) persistLoop() { - defer c.wg.Done() - ctx, cancel := c.chStop.NewCtx() - defer cancel() - - for { - select { - case <-time.After(dbPersistLoopInterval): - if memoryVersion, persistedVersion, err := c.persist(ctx); err != nil { - c.lggr.Warnw("Failed to persist channel definitions", "err", err, "memoryVersion", memoryVersion, - "persistedVersion", persistedVersion) - } - case <-c.chStop: - // Try one final persist with a short-ish timeout, then return - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - if memoryVersion, persistedVersion, err := c.persist(ctx); err != nil { - c.lggr.Errorw("Failed to persist channel definitions on shutdown", - "err", err, "memoryVersion", memoryVersion, "persistedVersion", persistedVersion) - } - return - } - } -} - -// Close stops the channel definition cache by canceling all contexts, closing the stop channel, -// and waiting for all goroutines to finish. It implements the services.Service interface. -func (c *channelDefinitionCache) Close() error { - return c.StopOnce("ChannelDefinitionCache", func() error { - // Cancel all contexts by closing the stop channel and wait for all goroutines to finish - close(c.chStop) - c.wg.Wait() - return nil - }) -} - -// HealthReport returns a health report map containing the cache's health status. -// It implements the services.Service interface. -func (c *channelDefinitionCache) HealthReport() map[string]error { - report := map[string]error{c.Name(): c.Healthy()} - return report -} - -// Name returns the name of the channel definition cache service. -// It implements the services.Service interface. -func (c *channelDefinitionCache) Name() string { return c.lggr.Name() } - -// Definitions merges all source definitions stored in c.definitions.Sources with the provided previous -// outcome definitions and returns the merged result. It starts with a clone of the prev parameter, -// applying source authority rules and adder limits. If adder limit violations occur, warnings are -// logged and processing stops for that source, but processing continues with other sources. After merging all -// sources, it does not update any in-memory fields (merging is read-only). Persistence of source definitions -// happens separately via the persistLoop goroutine, not directly triggered by this method. -// This is the main method that performs the actual reconciliation of channel definitions from -// multiple sources with the previous outcome definitions. -func (c *channelDefinitionCache) Definitions(prev llotypes.ChannelDefinitions) llotypes.ChannelDefinitions { - c.definitionsMu.RLock() - defer c.definitionsMu.RUnlock() - - channelDefinitionCacheCount. - WithLabelValues("previous_outcome").Set(float64(len(prev))) - - // nothing to merge - if len(c.definitions.Sources) == 0 { - return prev - } - - merged := maps.Clone(prev) - if merged == nil { - merged = make(llotypes.ChannelDefinitions) - } - - src := make([]types.SourceDefinition, 0, len(c.definitions.Sources)) - for _, sourceDefinition := range c.definitions.Sources { - src = append(src, sourceDefinition) - } - - // process definitions deterministically - sort.Slice(src, func(i, j int) bool { - if src[i].Trigger.BlockNum == src[j].Trigger.BlockNum { - return src[i].Trigger.LogIndex < src[j].Trigger.LogIndex - } - return src[i].Trigger.BlockNum < src[j].Trigger.BlockNum - }) - - feedIDToChannelID := buildFeedIDMap(merged) - for _, sourceDefinition := range src { - channelDefinitionCacheCount. - WithLabelValues(strconv.Itoa(int(sourceDefinition.Trigger.Source))).Set(float64(len(sourceDefinition.Definitions))) - c.lggr.Debugw("merging definitions", "source", sourceDefinition.Trigger.Source) - c.mergeDefinitions(sourceDefinition.Trigger.Source, merged, sourceDefinition.Definitions, feedIDToChannelID) - } - - c.lggr.Debugw("returning merged definitions", "definitions", merged) - return merged -} - -func decodePersistedSourceDefinitions(definitionsJSON json.RawMessage) (map[uint32]types.SourceDefinition, error) { - var sources map[uint32]types.SourceDefinition - if err := json.Unmarshal(definitionsJSON, &sources); err != nil { - return nil, fmt.Errorf("failed to unmarshal persisted definitions: %w", err) - } - - // Ensure that if we had a channel definition set before - // all nodes are migrated to the new SourceOwner format, - // we migrate the channel definition to the new SourceOwner format. - legacyKey := uint32(1) - for sourceID, sourceDefinition := range sources { - if sourceID == legacyKey { - sourceDefinition.Trigger.Source = SourceOwner - for channelID, def := range sourceDefinition.Definitions { - def.Source = SourceOwner - sourceDefinition.Definitions[channelID] = def - } - delete(sources, 1) - sources[SourceOwner] = sourceDefinition - } - } - - return sources, nil -} - -func newHTTPFetchBackoff() backoff.Backoff { - return backoff.Backoff{ - Min: 100 * time.Millisecond, - Max: 15 * time.Second, - Jitter: true, - } -} - -func ChannelDefinitionCacheFilterName(addr common.Address, donID uint32) string { - return logpoller.FilterName("OCR3 LLO ChannelDefinitionCachePoller", addr.String(), strconv.FormatUint(uint64(donID), 10)) -} diff --git a/core/services/llo/channeldefinitions/onchain_channel_definition_cache_test.go b/core/services/llo/channeldefinitions/onchain_channel_definition_cache_test.go deleted file mode 100644 index 997797fa593..00000000000 --- a/core/services/llo/channeldefinitions/onchain_channel_definition_cache_test.go +++ /dev/null @@ -1,1625 +0,0 @@ -package channeldefinitions - -import ( - "bytes" - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "io" - "math/big" - "math/rand" - "net/http" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - "github.com/smartcontractkit/chainlink-common/pkg/types/query" - "github.com/smartcontractkit/chainlink-data-streams/llo/types" - "github.com/smartcontractkit/chainlink-evm/pkg/logpoller" - "github.com/smartcontractkit/chainlink-evm/pkg/testutils" -) - -type mockLogPoller struct { - latestBlock logpoller.Block - latestBlockErr error - filteredLogs []logpoller.Log - adderLogs []logpoller.Log - ownerLogs []logpoller.Log - filteredLogsErr error - - unregisteredFilterNames []string -} - -func (m *mockLogPoller) RegisterFilter(ctx context.Context, filter logpoller.Filter) error { - return nil -} -func (m *mockLogPoller) LatestBlock(ctx context.Context) (logpoller.Block, error) { - return m.latestBlock, m.latestBlockErr -} -func (m *mockLogPoller) FilteredLogs(ctx context.Context, filter []query.Expression, limitAndSort query.LimitAndSort, queryName string) ([]logpoller.Log, error) { - // Return different logs based on query name to simulate separate adder/owner queries - if queryName == "ChannelDefinitionCachePoller - NewAdderChannelDefinition" { - if len(m.adderLogs) > 0 { - return m.adderLogs, m.filteredLogsErr - } - return m.filteredLogs, m.filteredLogsErr - } - if queryName == "ChannelDefinitionCachePoller - NewOwnerChannelDefinition" { - if len(m.ownerLogs) > 0 { - return m.ownerLogs, m.filteredLogsErr - } - return m.filteredLogs, m.filteredLogsErr - } - return m.filteredLogs, m.filteredLogsErr -} -func (m *mockLogPoller) UnregisterFilter(ctx context.Context, name string) error { - m.unregisteredFilterNames = append(m.unregisteredFilterNames, name) - return nil -} - -var _ HTTPClient = &mockHTTPClient{} - -type mockHTTPClient struct { - resp *http.Response - err error -} - -func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) { - return m.resp, m.err -} - -var _ ChannelDefinitionCacheORM = &mockCDCORM{} - -type mockCDCORM struct { - err error - - lastPersistedAddr common.Address - lastPersistedDonID uint32 - lastPersistedVersion uint32 - lastPersistedDfns map[uint32]types.SourceDefinition - lastPersistedBlockNum int64 - lastPersistedFormat uint32 -} - -func (m *mockCDCORM) LoadChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (pd *types.PersistedDefinitions, err error) { - panic("not implemented") -} -func (m *mockCDCORM) StoreChannelDefinitions(ctx context.Context, addr common.Address, donID, version uint32, dfns json.RawMessage, blockNum int64, format uint32) (err error) { - m.lastPersistedAddr = addr - m.lastPersistedDonID = donID - m.lastPersistedVersion = version - m.lastPersistedBlockNum = blockNum - m.lastPersistedFormat = format - // Unmarshal the json.RawMessage to store in lastPersistedDfns for test assertions - if err := json.Unmarshal(dfns, &m.lastPersistedDfns); err != nil { - return err - } - return m.err -} - -func (m *mockCDCORM) CleanupChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (err error) { - panic("not implemented") -} - -func makeLog(t *testing.T, donID, version uint32, url string, sha [32]byte) logpoller.Log { - data := makeLogData(t, donID, version, url, sha) - return logpoller.Log{EventSig: NewChannelDefinition, Topics: [][]byte{NewChannelDefinition[:], makeDonIDTopic(donID)}, Data: data, BlockNumber: int64(version) + 1000} -} - -func makeLogData(t *testing.T, donID, version uint32, url string, sha [32]byte) []byte { - event := channelConfigStoreABI.Events[newChannelDefinitionEventName] - // donID is indexed - // version, url, sha - data, err := event.Inputs.NonIndexed().Pack(version, url, sha) - require.NoError(t, err) - return data -} - -func makeAdderLog(t *testing.T, donID, adderID uint32, url string, sha [32]byte, blockNumber int64) logpoller.Log { - data := makeAdderLogData(t, donID, adderID, url, sha) - return logpoller.Log{EventSig: ChannelDefinitionAdded, Topics: [][]byte{ChannelDefinitionAdded[:], makeDonIDTopic(donID), makeDonIDTopic(adderID)}, Data: data, BlockNumber: blockNumber} -} - -func makeAdderLogData(t *testing.T, donID, adderID uint32, url string, sha [32]byte) []byte { - event := channelConfigStoreABI.Events[channelDefinitionAddedEventName] - // donID and adderID are indexed (in Topics) - // url, sha are non-indexed (in Data) - data, err := event.Inputs.NonIndexed().Pack(url, sha) - require.NoError(t, err) - return data -} - -func makeDonIDTopic(donID uint32) []byte { - return common.BigToHash(big.NewInt(int64(donID))).Bytes() -} - -// drainChannel drains all values from a channel -func drainChannel[T any](ch chan T) { - for { - select { - case <-ch: - default: - return - } - } -} - -// collectTriggers collects all available triggers from a channel up to maxCount -func collectTriggers(ch chan types.Trigger, maxCount int) []types.Trigger { - triggers := make([]types.Trigger, 0, maxCount) - for i := 0; i < maxCount; i++ { - select { - case trigger := <-ch: - triggers = append(triggers, trigger) - default: - return triggers - } - } - return triggers -} - -// makeChannelDefinition creates a simple channel definition for testing -func makeChannelDefinition(channelID uint32, source uint32) llotypes.ChannelDefinition { - return llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: channelID, Aggregator: llotypes.AggregatorMedian}}, - Source: source, - Tombstone: false, - } -} - -// makeChannelDefinitionWithFeedID creates a channel definition with a FeedID in options for testing -func makeChannelDefinitionWithFeedID(channelID uint32, source uint32, feedID common.Hash) llotypes.ChannelDefinition { - optsJSON := fmt.Sprintf(`{"feedId":"%s"}`, feedID.Hex()) - return llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: channelID, Aggregator: llotypes.AggregatorMedian}}, - Source: source, - Tombstone: false, - Opts: llotypes.ChannelOpts(optsJSON), - } -} - -// addChannelDefinitions adds channel definitions to the given map for a range of channel IDs -func addChannelDefinitions(defs llotypes.ChannelDefinitions, startID, endID uint32, source uint32) { - for i := startID; i <= endID; i++ { - defs[i] = makeChannelDefinition(i, source) - } -} - -func Test_ChannelDefinitionCache(t *testing.T) { - donID := rand.Uint32() - - t.Run("Definitions", func(t *testing.T) { - // NOTE: this is covered more thoroughly in the integration tests - prev := llotypes.ChannelDefinitions(map[llotypes.ChannelID]llotypes.ChannelDefinition{ - 1: { - ReportFormat: llotypes.ReportFormat(43), - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, - Opts: llotypes.ChannelOpts{1, 2, 3}, - Source: SourceOwner, - }, - }) - - // Test that Definitions() returns prev when sourceDefinitions is empty - cdc := &channelDefinitionCache{ - lggr: logger.TestSugared(t), - definitions: Definitions{ - Sources: make(map[uint32]types.SourceDefinition), - }, - orm: &mockCDCORM{}, // Required for persist() call in Definitions() - } - - result := cdc.Definitions(prev) - require.Equal(t, prev, result) - - // Test merging from sourceDefinitions - adderID := uint32(100) - sourceDefs := llotypes.ChannelDefinitions{ - 2: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 2, Aggregator: llotypes.AggregatorMedian}}, - Source: adderID, - }, - } - cdc.definitions.Sources[adderID] = types.SourceDefinition{ - Trigger: types.Trigger{ - Source: adderID, - BlockNum: 1000, - }, - Definitions: sourceDefs, - } - - result = cdc.Definitions(prev) - // Should contain both prev channel 1 and adder channel 2 - require.Contains(t, result, llotypes.ChannelID(1)) - require.Contains(t, result, llotypes.ChannelID(2)) - require.Equal(t, SourceOwner, result[1].Source) - require.Equal(t, adderID, result[2].Source) - - // Test tombstone removal - tombstoneDefs := llotypes.ChannelDefinitions{ - 1: { - ReportFormat: llotypes.ReportFormat(43), - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, - Opts: llotypes.ChannelOpts{1, 2, 3}, - Source: SourceOwner, - Tombstone: false, - }, - 3: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 3, Aggregator: llotypes.AggregatorMedian}}, - Source: SourceOwner, - Tombstone: true, - }, - } - cdc.definitions.Sources[SourceOwner] = types.SourceDefinition{ - Trigger: types.Trigger{ - Source: SourceOwner, - BlockNum: 2000, - }, - Definitions: tombstoneDefs, - } - - result = cdc.Definitions(prev) - // Tombstoned channel should be kept in definitions with Tombstone: true - require.Contains(t, result, llotypes.ChannelID(3)) - require.True(t, result[3].Tombstone, "channel 3 should be tombstoned") - // Channels 1 and 2 should still be present - require.Contains(t, result, llotypes.ChannelID(1)) - require.Contains(t, result, llotypes.ChannelID(2)) - }) - - t.Run("readLogs", func(t *testing.T) { - lp := &mockLogPoller{latestBlockErr: sql.ErrNoRows} - fetchTriggerCh := make(chan types.Trigger, 100) - cdc := &channelDefinitionCache{ - donID: donID, - lp: lp, - lggr: logger.TestSugared(t), - fetchTriggerCh: fetchTriggerCh, - definitions: Definitions{ - Sources: make(map[uint32]types.SourceDefinition), - }, - } - - t.Run("skips if logpoller has no blocks", func(t *testing.T) { - ctx := t.Context() - err := cdc.readLogs(ctx) - require.NoError(t, err) - }) - t.Run("returns error on LatestBlock failure", func(t *testing.T) { - ctx := t.Context() - lp.latestBlockErr = errors.New("test error") - - err := cdc.readLogs(ctx) - require.EqualError(t, err, "test error") - }) - t.Run("does nothing if LatestBlock older or the same as current channel definitions block", func(t *testing.T) { - ctx := t.Context() - lp.latestBlockErr = nil - lp.latestBlock = logpoller.Block{BlockNumber: 42} - cdc.definitions.LastBlockNum = 43 - - err := cdc.readLogs(ctx) - require.NoError(t, err) - }) - t.Run("returns error if FilteredLogs fails", func(t *testing.T) { - ctx := t.Context() - cdc.definitions.LastBlockNum = 0 - lp.filteredLogsErr = errors.New("test error 2") - - err := cdc.readLogs(ctx) - require.EqualError(t, err, "test error 2") - }) - t.Run("ignores logs with different topic", func(t *testing.T) { - ctx := t.Context() - lp.filteredLogsErr = nil - // Set logs with different event signature (not NewChannelDefinition or ChannelDefinitionAdded) - lp.ownerLogs = []logpoller.Log{{EventSig: common.Hash{1, 2, 3, 4}}} - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - }) - t.Run("logs warning and continues if log is malformed", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - lp.latestBlockErr = nil - lp.filteredLogsErr = nil - // Set malformed owner log (has correct event sig but missing data) - lp.ownerLogs = []logpoller.Log{{EventSig: NewChannelDefinition}} - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err, "should not return error for malformed log, should log warning and continue") - // Should not send trigger for malformed log - select { - case <-fetchTriggerCh: - t.Fatal("should not send trigger for malformed log") - default: - // Expected - no trigger - } - }) - t.Run("sends trigger on channel if FilteredLogs returns new event with a later version", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - lp.latestBlockErr = nil - lp.filteredLogsErr = nil - // Set owner logs - lp.ownerLogs = []logpoller.Log{makeLog(t, donID, uint32(43), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4})} - // Set empty adder logs - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - - // Check that fetch trigger was sent - select { - case trigger := <-fetchTriggerCh: - require.Equal(t, SourceOwner, trigger.Source) - require.Equal(t, uint32(43), trigger.Version) - require.Equal(t, "http://example.com/xxx.json", trigger.URL) - require.Equal(t, [32]byte{1, 2, 3, 4}, trigger.SHA) - default: - t.Fatal("expected fetch trigger signal in channel") - } - }) - t.Run("sends triggers for all logs", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - lp.filteredLogsErr = nil - // Set owner logs (readLogs calls FilteredLogs for owner logs) - lp.ownerLogs = []logpoller.Log{ - makeLog(t, donID, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), - makeLog(t, donID, uint32(43), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), - } - // Set empty adder logs - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - // Should receive triggers for both owner logs - triggers := collectTriggers(fetchTriggerCh, 4) - require.Len(t, triggers, 2, "expected 2 triggers") - // Find the trigger with version 43 (latest) - var found43 bool - for _, trigger := range triggers { - if trigger.Version == 43 { - found43 = true - break - } - } - require.True(t, found43, "expected trigger with version 43") - }) - t.Run("in case of multiple logs, sends triggers for all", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - lp.filteredLogsErr = nil - // Set owner logs (readLogs calls FilteredLogs for owner logs) - lp.ownerLogs = []logpoller.Log{ - makeLog(t, donID, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), - makeLog(t, donID, uint32(45), "http://example.com/xxx2.json", [32]byte{2, 2, 3, 4}), - makeLog(t, donID, uint32(44), "http://example.com/xxx3.json", [32]byte{3, 2, 3, 4}), - makeLog(t, donID, uint32(43), "http://example.com/xxx4.json", [32]byte{4, 2, 3, 4}), - } - // Set empty adder logs - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - - // Check that fetch triggers were sent for all owner logs - triggers := collectTriggers(fetchTriggerCh, 8) - require.Len(t, triggers, 4, "expected 4 triggers") - // Find the trigger with version 45 (latest) - var latestTrigger *types.Trigger - for i := range triggers { - if triggers[i].Version == 45 { - latestTrigger = &triggers[i] - break - } - } - require.NotNil(t, latestTrigger, "expected trigger with version 45") - require.Equal(t, "http://example.com/xxx2.json", latestTrigger.URL) - require.Equal(t, [32]byte{2, 2, 3, 4}, latestTrigger.SHA) - }) - t.Run("ignores logs with incorrect don ID", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - lp.filteredLogsErr = nil - // Set owner logs with wrong donID - lp.ownerLogs = []logpoller.Log{ - makeLog(t, donID+1, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), - } - // Set empty adder logs - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - - // Check that no fetch trigger was sent - select { - case trigger := <-fetchTriggerCh: - t.Fatalf("did not expect fetch trigger signal for log with wrong donID, got: %+v", trigger) - default: - // No signal, as expected - } - }) - t.Run("ignores logs with wrong number of topics", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - lp.filteredLogsErr = nil - lg := makeLog(t, donID, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}) - lg.Topics = lg.Topics[:1] - // Set owner log with wrong number of topics - lp.ownerLogs = []logpoller.Log{lg} - // Set empty adder logs - lp.adderLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - - // Check that no fetch trigger was sent - select { - case trigger := <-fetchTriggerCh: - t.Fatalf("did not expect fetch trigger signal for log with missing topics, got: %+v", trigger) - default: - // No signal, as expected - } - }) - t.Run("reads adder logs and sends triggers", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - lp.filteredLogsErr = nil - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - adderID1 := uint32(100) - adderID2 := uint32(200) - // Set adder logs (readLogs calls FilteredLogs for adder logs first) - lp.adderLogs = []logpoller.Log{ - makeAdderLog(t, donID, adderID1, "http://example.com/adder1.json", [32]byte{1, 1, 1, 1}, 1500), - makeAdderLog(t, donID, adderID2, "http://example.com/adder2.json", [32]byte{2, 2, 2, 2}, 1600), - } - // Set empty owner logs - lp.ownerLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - - // Check that fetch triggers were sent for both adders - triggers := collectTriggers(fetchTriggerCh, 2) - require.Len(t, triggers, 2, "expected 2 triggers") - // Verify adder triggers - for _, trigger := range triggers { - require.NotEqual(t, SourceOwner, trigger.Source, "should not be owner") - require.True(t, trigger.Source == adderID1 || trigger.Source == adderID2, "should be one of the adder IDs") - if trigger.Source == adderID1 { - require.Equal(t, "http://example.com/adder1.json", trigger.URL) - require.Equal(t, [32]byte{1, 1, 1, 1}, trigger.SHA) - } else { - require.Equal(t, "http://example.com/adder2.json", trigger.URL) - require.Equal(t, [32]byte{2, 2, 2, 2}, trigger.SHA) - } - } - }) - t.Run("reads both owner and adder logs in one call", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - lp.filteredLogsErr = nil - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - adderID := uint32(100) - // Set both adder and owner logs - lp.adderLogs = []logpoller.Log{ - makeAdderLog(t, donID, adderID, "http://example.com/adder.json", [32]byte{6, 6, 6, 6}, 1500), - } - lp.ownerLogs = []logpoller.Log{ - makeLog(t, donID, uint32(50), "http://example.com/owner.json", [32]byte{5, 5, 5, 5}), - } - - err := cdc.readLogs(ctx) - require.NoError(t, err) - - // Should have triggers for both adder and owner logs - triggers := collectTriggers(fetchTriggerCh, 2) - require.Len(t, triggers, 2, "expected 2 triggers (one adder, one owner)") - // Verify we have both types - var foundOwner, foundAdder bool - for _, trigger := range triggers { - switch trigger.Source { - case SourceOwner: - foundOwner = true - require.Equal(t, uint32(50), trigger.Version) - require.Equal(t, "http://example.com/owner.json", trigger.URL) - case adderID: - foundAdder = true - require.Equal(t, "http://example.com/adder.json", trigger.URL) - require.Equal(t, [32]byte{6, 6, 6, 6}, trigger.SHA) - } - } - require.True(t, foundOwner, "expected owner trigger") - require.True(t, foundAdder, "expected adder trigger") - }) - t.Run("ignores adder logs with incorrect don ID", func(t *testing.T) { - ctx := t.Context() - // Drain any existing triggers - drainChannel(fetchTriggerCh) - lp.filteredLogsErr = nil - lp.latestBlock = logpoller.Block{BlockNumber: 2000} - cdc.definitions.LastBlockNum = 0 - cdc.initialBlockNum = 0 - adderID := uint32(100) - // Set adder logs with wrong donID - lp.adderLogs = []logpoller.Log{ - makeAdderLog(t, donID+1, adderID, "http://example.com/adder.json", [32]byte{1, 1, 1, 1}, 1500), - } - // Set empty owner logs - lp.ownerLogs = []logpoller.Log{} - - err := cdc.readLogs(ctx) - require.NoError(t, err) - // Should not send trigger for wrong donID - select { - case trigger := <-fetchTriggerCh: - t.Fatalf("did not expect fetch trigger signal for log with wrong donID, got: %+v", trigger) - default: - // No signal, as expected - } - }) - }) - - t.Run("fetchChannelDefinitions", func(t *testing.T) { - c := &mockHTTPClient{} - cdc := &channelDefinitionCache{ - lggr: logger.TestSugared(t), - client: c, - httpLimit: 2048, - } - - t.Run("invalid URL returns error", func(t *testing.T) { - ctx := t.Context() - // Set up mock to return error for invalid URL scheme - c.err = errors.New("unsupported protocol scheme") - c.resp = nil - - // Use a URL with invalid scheme that will fail at HTTP client level - // This avoids panic from URL parsing in the HTTP library - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://[::1", - SHA: [32]byte{}, - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - // The error could be from URL parsing or HTTP client - both are acceptable - require.Error(t, err) - }) - - t.Run("networking error while making request returns error", func(t *testing.T) { - ctx := t.Context() - c.resp = nil - c.err = errors.New("http request failed") - - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte{}, - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.Contains(t, err.Error(), "failed to make HTTP request to channel definitions URL") - require.Contains(t, err.Error(), "http request failed") - }) - - t.Run("server returns 500 returns error", func(t *testing.T) { - ctx := t.Context() - c.err = nil - c.resp = &http.Response{StatusCode: 500, Body: io.NopCloser(bytes.NewReader([]byte{1, 2, 3}))} - - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte{}, - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.Contains(t, err.Error(), "HTTP error from channel definitions URL http://example.com/definitions.json (status 500)") - require.Contains(t, err.Error(), "\x01\x02\x03") - }) - - var largeBody = make([]byte, 2048) - for i := range largeBody { - largeBody[i] = 'a' - } - - t.Run("server returns 404 returns error (and does not log entirety of huge response body)", func(t *testing.T) { - ctx := t.Context() - c.err = nil - c.resp = &http.Response{StatusCode: 404, Body: io.NopCloser(bytes.NewReader(largeBody))} - - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte{}, - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.Contains(t, err.Error(), "HTTP error from channel definitions URL http://example.com/definitions.json (status 404)") - require.Contains(t, err.Error(), "failed to read response body") - require.Contains(t, err.Error(), "http: request body too large") - }) - - var hugeBody = make([]byte, 8096) - c.resp = &http.Response{Body: io.NopCloser(bytes.NewReader(hugeBody))} - - t.Run("server returns body that is too large", func(t *testing.T) { - ctx := t.Context() - c.err = nil - c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader(hugeBody))} - - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte{}, - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.Contains(t, err.Error(), "failed to read channel definitions response body from") - require.Contains(t, err.Error(), "http: request body too large") - }) - - t.Run("server returns invalid JSON returns error", func(t *testing.T) { - ctx := t.Context() - c.err = nil - c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte{1, 2, 3}))} - - expectedSha := common.HexToHash("0xfd1780a6fc9ee0dab26ceb4b3941ab03e66ccd970d1db91612c66df4515b0a0a") - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte(expectedSha), - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.Contains(t, err.Error(), "failed to decode channel definitions JSON from") - require.Contains(t, err.Error(), "invalid character '\\x01' looking for beginning of value") - }) - - t.Run("SHA mismatch returns error", func(t *testing.T) { - ctx := t.Context() - c.err = nil - c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(`{"foo":"bar"}`)))} - - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte{}, - BlockNum: 0, - Version: 0, - } - _, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.Contains(t, err.Error(), "SHA3 mismatch for channel definitions from") - require.Contains(t, err.Error(), "expected 0000000000000000000000000000000000000000000000000000000000000000") - require.Contains(t, err.Error(), "got 4d3304d0d87c27a031cbb6bdf95da79b7b4552c3d0bef2e5a94f50810121e1e0") - }) - - t.Run("valid JSON matching SHA returns channel definitions", func(t *testing.T) { - ctx := t.Context() - chainSelector := 4949039107694359620 // arbitrum mainnet - feedID := [32]byte{00, 03, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} - expirationWindow := 3600 - multiplier := big.NewInt(1e18) - baseUSDFee := 10 - valid := fmt.Sprintf(` -{ - "42": { - "reportFormat": %d, - "chainSelector": %d, - "streams": [{"streamId": 52, "aggregator": %d}, {"streamId": 53, "aggregator": %d}, {"streamId": 55, "aggregator": %d}], - "opts": { - "feedId": "0x%x", - "expirationWindow": %d, - "multiplier": "%s", - "baseUSDFee": "%d" - } - } -}`, llotypes.ReportFormatEVMPremiumLegacy, chainSelector, llotypes.AggregatorMedian, llotypes.AggregatorMedian, llotypes.AggregatorQuote, feedID, expirationWindow, multiplier.String(), baseUSDFee) - - c.err = nil - c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(valid)))} - - expectedSha := common.HexToHash("0x367bbc75f7b6c9fc66a98ea99f837ea7ac4a3c2d6a9ee284de018bd02c41b52d") - trigger := types.Trigger{ - Source: SourceOwner, - URL: "http://example.com/definitions.json", - SHA: [32]byte(expectedSha), - BlockNum: 0, - Version: 0, - } - cd, err := cdc.fetchChannelDefinitions(ctx, trigger) - require.NoError(t, err) - expectedDef := llotypes.ChannelDefinition{ - ReportFormat: 0x1, - Streams: []llotypes.Stream{{StreamID: 0x34, Aggregator: 0x1}, {StreamID: 0x35, Aggregator: 0x1}, {StreamID: 0x37, Aggregator: 0x3}}, - Opts: llotypes.ChannelOpts{0x7b, 0x22, 0x62, 0x61, 0x73, 0x65, 0x55, 0x53, 0x44, 0x46, 0x65, 0x65, 0x22, 0x3a, 0x22, 0x31, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x22, 0x3a, 0x33, 0x36, 0x30, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x65, 0x64, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30, 0x33, 0x36, 0x62, 0x34, 0x61, 0x61, 0x37, 0x65, 0x35, 0x37, 0x63, 0x61, 0x37, 0x62, 0x36, 0x38, 0x61, 0x65, 0x31, 0x62, 0x66, 0x34, 0x35, 0x36, 0x35, 0x33, 0x66, 0x35, 0x36, 0x62, 0x36, 0x35, 0x36, 0x66, 0x64, 0x33, 0x61, 0x61, 0x33, 0x33, 0x35, 0x65, 0x66, 0x37, 0x66, 0x61, 0x65, 0x36, 0x39, 0x36, 0x62, 0x36, 0x36, 0x33, 0x66, 0x31, 0x62, 0x38, 0x34, 0x37, 0x32, 0x22, 0x2c, 0x22, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d}, - Source: SourceOwner, - } - require.Equal(t, llotypes.ChannelDefinitions{0x2a: expectedDef}, cd) - }) - }) - - t.Run("persist", func(t *testing.T) { - definitions := llotypes.ChannelDefinitions{ - 1: { - ReportFormat: llotypes.ReportFormatEVMPremiumLegacy, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, - Opts: llotypes.ChannelOpts(`{"foo":"bar"}`), - }, - } - cdc := &channelDefinitionCache{ - lggr: logger.TestSugared(t), - orm: nil, - addr: testutils.NewAddress(), - donID: donID, - definitions: Definitions{ - LastBlockNum: 142, - }, - } - - t.Run("persists current definitions", func(t *testing.T) { - ctx := t.Context() - orm := &mockCDCORM{} - cdc.orm = orm - cdc.definitions.Version = 42 - cdc.persistedBlockNum = 141 - cdc.definitions.LastBlockNum = 142 - cdc.definitions.Sources = map[uint32]types.SourceDefinition{ - SourceOwner: { - Trigger: types.Trigger{ - Source: SourceOwner, - BlockNum: 142, - Version: 42, - }, - Definitions: definitions, - }, - } - - // persist() always persists c.definitions (no comparison logic) - memoryBlockNum, persistedBlockNum, err := cdc.persist(ctx) - require.NoError(t, err) - require.Equal(t, int64(142), memoryBlockNum) - require.Equal(t, int64(142), persistedBlockNum) - require.Equal(t, int64(142), cdc.persistedBlockNum) - require.Equal(t, cdc.definitions.Sources, orm.lastPersistedDfns) - }) - - orm := &mockCDCORM{} - cdc.orm = orm - - t.Run("returns error on db failure and does not update persisted block number", func(t *testing.T) { - ctx := t.Context() - cdc.persistedBlockNum = 141 - cdc.definitions.Version = 43 - cdc.definitions.LastBlockNum = 143 - cdc.definitions.Sources = map[uint32]types.SourceDefinition{ - SourceOwner: { - Trigger: types.Trigger{ - Source: SourceOwner, - BlockNum: 143, - Version: 43, - }, - Definitions: definitions, - }, - } - orm.err = errors.New("test error") - - // persist() always persists c.definitions - memoryBlockNum, persistedBlockNum, err := cdc.persist(ctx) - require.Contains(t, err.Error(), "test error") - require.Equal(t, int64(143), memoryBlockNum) - require.Equal(t, int64(141), persistedBlockNum) - require.Equal(t, int64(141), cdc.persistedBlockNum) - }) - - t.Run("updates persisted block number on success", func(t *testing.T) { - ctx := t.Context() - cdc.definitions.Version = 43 - cdc.definitions.LastBlockNum = 143 - cdc.definitions.Sources = map[uint32]types.SourceDefinition{ - SourceOwner: { - Trigger: types.Trigger{ - Source: SourceOwner, - BlockNum: 143, - Version: 43, - }, - Definitions: definitions, - }, - } - cdc.persistedBlockNum = 141 - orm.err = nil - - // persist() always persists c.definitions - memoryBlockNum, persistedBlockNum, err := cdc.persist(ctx) - require.NoError(t, err) - require.Equal(t, int64(143), memoryBlockNum) - require.Equal(t, int64(143), persistedBlockNum) - require.Equal(t, int64(143), cdc.persistedBlockNum) - - require.Equal(t, cdc.addr, orm.lastPersistedAddr) - require.Equal(t, cdc.donID, orm.lastPersistedDonID) - require.Equal(t, cdc.definitions.Version, orm.lastPersistedVersion) - require.Equal(t, cdc.definitions.Sources, orm.lastPersistedDfns) - require.Equal(t, cdc.definitions.LastBlockNum, orm.lastPersistedBlockNum) - }) - }) - - t.Run("adder limits", func(t *testing.T) { - cdc := &channelDefinitionCache{ - lggr: logger.TestSugared(t), - } - - adderID := uint32(100) - - t.Run("rejects adder definition file with more than MaxChannelsPerAdder channels", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Create a new definition file with MaxChannelsPerAdder + 1 channels - // The limit is enforced based on existing channels plus new channels being added - // When trying to add the (MaxChannelsPerAdder+1)th channel, numberOfChannels will be MaxChannelsPerAdder - addChannelDefinitions(newDefinitions, 1, uint32(MaxChannelsPerAdder+1), adderID) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(adderID, currentDefinitions, newDefinitions, feedIDToChannelID) - // The implementation stops processing at MaxChannelsPerAdder and doesn't return an error - // Verify that only MaxChannelsPerAdder channels were added - require.LessOrEqual(t, len(currentDefinitions), MaxChannelsPerAdder, "should not exceed MaxChannelsPerAdder") - // Count channels from this adder source - adderChannelCount := uint32(0) - for _, def := range currentDefinitions { - if def.Source == adderID { - adderChannelCount++ - } - } - require.Equal(t, MaxChannelsPerAdder, int(adderChannelCount), "should have exactly MaxChannelsPerAdder channels from this adder") - }) - - t.Run("allows adder definition file with channels up to MaxChannelsPerAdder when most are existing", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Pre-populate with 90 existing channels (MaxChannelsPerAdder - 10) - // This tests that existing channels + new channels can total up to MaxChannelsPerAdder - existingEnd := uint32(90) - addChannelDefinitions(currentDefinitions, 1, existingEnd, adderID) - // Include these existing channels in the new definition file (they'll be skipped) - addChannelDefinitions(newDefinitions, 1, existingEnd, adderID) - - // Add 9 new channels to reach exactly MaxChannelsPerAdder total in the file - addChannelDefinitions(newDefinitions, existingEnd+1, existingEnd+9, adderID) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(adderID, currentDefinitions, newDefinitions, feedIDToChannelID) - // Should have 90 existing + 9 new = 99 (below MaxChannelsPerAdder) - require.Len(t, currentDefinitions, 99) - }) - - t.Run("owner definitions are not subject to adder limits", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Owner can add any number of channels (not subject to MaxChannelsPerAdder limit) - addChannelDefinitions(newDefinitions, 1, 20, SourceOwner) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - require.Len(t, currentDefinitions, 20) - }) - - t.Run("owner can have more than MaxChannelsPerAdder channels", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Owner can have more than MaxChannelsPerAdder channels - addChannelDefinitions(newDefinitions, 1, uint32(MaxChannelsPerAdder+10), SourceOwner) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - require.Len(t, currentDefinitions, MaxChannelsPerAdder+10) - }) - }) - - t.Run("owner removal", func(t *testing.T) { - cdc := &channelDefinitionCache{ - lggr: logger.TestSugared(t), - } - - t.Run("does not remove owner-defined channels missing from new definitions", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Set up current definitions with owner-defined channels 1, 2, 3, 4, 5 - addChannelDefinitions(currentDefinitions, 1, 5, SourceOwner) - - // New definitions only include channels 1, 3, 5 (missing 2 and 4) - newDefinitions[1] = makeChannelDefinition(1, SourceOwner) - newDefinitions[3] = makeChannelDefinition(3, SourceOwner) - newDefinitions[5] = makeChannelDefinition(5, SourceOwner) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channels 1, 3, 5 should be present (updated from newDefinitions) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Contains(t, currentDefinitions, llotypes.ChannelID(3)) - require.Contains(t, currentDefinitions, llotypes.ChannelID(5)) - - // Channels 2 and 4 should remain (not removed, just not in newDefinitions) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2)) - require.Contains(t, currentDefinitions, llotypes.ChannelID(4)) - - // Result should contain all 5 channels (2 and 4 remain from currentDefinitions) - require.Len(t, currentDefinitions, 5) - }) - - t.Run("preserves non-owner channels when owner updates definitions", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - adderID := uint32(100) - - // Set up current definitions with owner channels 1, 2 and adder channel 10 - addChannelDefinitions(currentDefinitions, 1, 2, SourceOwner) - currentDefinitions[10] = makeChannelDefinition(10, adderID) - - // New owner definitions only include channel 1 (missing channel 2) - newDefinitions[1] = makeChannelDefinition(1, SourceOwner) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Owner channel 1 should be present (updated from newDefinitions) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Equal(t, SourceOwner, currentDefinitions[1].Source) - - // Owner channel 2 should remain (not removed, just not in newDefinitions) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2)) - require.Equal(t, SourceOwner, currentDefinitions[2].Source) - - // Adder channel 10 should be preserved - require.Contains(t, currentDefinitions, llotypes.ChannelID(10)) - require.Equal(t, adderID, currentDefinitions[10].Source) - - // Result should contain channel 1 (owner, updated), channel 2 (owner, preserved), and channel 10 (adder) - require.Len(t, currentDefinitions, 3) - }) - - t.Run("owner removal only happens when source is SourceOwner", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - adderID := uint32(200) - - // Set up current definitions with owner-defined channels 1, 2 - addChannelDefinitions(currentDefinitions, 1, 2, SourceOwner) - - // New definitions from adder only includes channel 1 - newDefinitions[1] = makeChannelDefinition(1, adderID) - - // When source is an adder (not SourceOwner), owner channels should NOT be removed - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(adderID, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Owner channel 1 should still be present (adder can't overwrite it) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Equal(t, SourceOwner, currentDefinitions[1].Source, "channel 1 should still have owner source") - - // Owner channel 2 should still be present (not removed because source is not SourceOwner) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2)) - require.Equal(t, SourceOwner, currentDefinitions[2].Source) - - // Result should contain both owner channels (adder's attempt to add channel 1 is ignored) - require.Len(t, currentDefinitions, 2) - }) - - t.Run("owner can tombstone owner channels", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - adderID := uint32(300) - - // Set up current definitions with owner channel 1 and adder channel 2 - currentDefinitions[1] = makeChannelDefinition(1, SourceOwner) - currentDefinitions[2] = makeChannelDefinition(2, adderID) - - // Owner tries to tombstone owner channel 1 (should succeed) - newDefinitions[1] = llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}}, - Source: SourceOwner, - Tombstone: true, - } - - // Owner tries to tombstone adder channel 2 (should succeed) - newDefinitions[2] = llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatJSON, - Source: SourceOwner, - Tombstone: true, - } - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Result should contain both channels - require.Len(t, currentDefinitions, 2) - - // Owner channel 1 should be present (tombstone succeeded) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Equal(t, SourceOwner, currentDefinitions[1].Source) - require.True(t, currentDefinitions[1].Tombstone, "channel 1 should be tombstoned") - - // Adder channel 2 should be kept in definitions with Tombstone: true (tombstone succeeded) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2)) - require.True(t, currentDefinitions[2].Tombstone, "channel 2 should be tombstoned") - }) - - t.Run("owner drops tombstoned channels omitted from new definitions", func(t *testing.T) { - feedID1 := common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") - feedID2 := common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") - feedID3 := common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333") - - currentDefinitions := llotypes.ChannelDefinitions{ - 1: makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1), - 2: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 2, Aggregator: llotypes.AggregatorMedian}}, - Opts: []byte(fmt.Sprintf(`{"feedId":"%s"}`, feedID2.Hex())), - Source: SourceOwner, - Tombstone: true, - }, - 3: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 3, Aggregator: llotypes.AggregatorMedian}}, - Opts: []byte(fmt.Sprintf(`{"feedId":"%s"}`, feedID3.Hex())), - Source: SourceOwner, - Tombstone: true, - }, - } - - // Owner omits tombstoned channels 2 and 3, keeps channel 1 - newDefinitions := llotypes.ChannelDefinitions{ - 1: makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1), - } - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - require.Contains(t, feedIDToChannelID, feedID2) - require.Contains(t, feedIDToChannelID, feedID3) - - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - require.Len(t, currentDefinitions, 1) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.NotContains(t, currentDefinitions, llotypes.ChannelID(2), "tombstoned channel 2 should be dropped") - require.NotContains(t, currentDefinitions, llotypes.ChannelID(3), "tombstoned channel 3 should be dropped") - - require.NotContains(t, feedIDToChannelID, feedID2, "feedID for dropped channel 2 should be removed") - require.NotContains(t, feedIDToChannelID, feedID3, "feedID for dropped channel 3 should be removed") - require.Contains(t, feedIDToChannelID, feedID1, "feedID for kept channel 1 should remain") - }) - - t.Run("owner keeps tombstoned channels still present in new definitions", func(t *testing.T) { - currentDefinitions := llotypes.ChannelDefinitions{ - 1: makeChannelDefinition(1, SourceOwner), - 2: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 2, Aggregator: llotypes.AggregatorMedian}}, - Source: SourceOwner, - Tombstone: true, - }, - } - - // Owner still includes tombstoned channel 2 in newDefinitions - newDefinitions := llotypes.ChannelDefinitions{ - 1: makeChannelDefinition(1, SourceOwner), - 2: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 2, Aggregator: llotypes.AggregatorMedian}}, - Source: SourceOwner, - Tombstone: true, - }, - } - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - require.Len(t, currentDefinitions, 2) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2), "tombstoned channel 2 should be kept when present in newDefinitions") - require.True(t, currentDefinitions[2].Tombstone) - }) - - t.Run("non-owner source does not drop tombstoned channels", func(t *testing.T) { - adderID := uint32(400) - - currentDefinitions := llotypes.ChannelDefinitions{ - 1: makeChannelDefinition(1, SourceOwner), - 2: { - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 2, Aggregator: llotypes.AggregatorMedian}}, - Source: SourceOwner, - Tombstone: true, - }, - } - - newDefinitions := llotypes.ChannelDefinitions{ - 3: makeChannelDefinition(3, adderID), - } - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(adderID, currentDefinitions, newDefinitions, feedIDToChannelID) - - require.Len(t, currentDefinitions, 3) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2), "tombstoned channel should not be dropped by non-owner source") - require.True(t, currentDefinitions[2].Tombstone) - }) - }) - - t.Run("feedID uniqueness", func(t *testing.T) { - cdc := &channelDefinitionCache{ - lggr: logger.TestSugared(t), - } - - adderID := uint32(100) - feedID1 := common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") - feedID2 := common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") - - t.Run("skips new channel with colliding FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - - // New channel 2 with same feedID1 (collision) - newDefinitions[2] = makeChannelDefinitionWithFeedID(2, SourceOwner, feedID1) - - // New channel 3 with unique feedID2 (should be added) - newDefinitions[3] = makeChannelDefinitionWithFeedID(3, SourceOwner, feedID2) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channel 1 should be present (existing) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - // Channel 2 should NOT be present (collision, skipped) - require.NotContains(t, currentDefinitions, llotypes.ChannelID(2)) - // Channel 3 should be present (unique FeedID) - require.Contains(t, currentDefinitions, llotypes.ChannelID(3)) - require.Len(t, currentDefinitions, 2) - }) - - t.Run("allows owner to update same channel with same FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - - // Owner updates channel 1 with same feedID1 (should be allowed) - updatedDef := makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - updatedDef.Streams = []llotypes.Stream{{StreamID: 999, Aggregator: llotypes.AggregatorMedian}} - newDefinitions[1] = updatedDef - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channel 1 should be present and updated - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Equal(t, uint32(999), currentDefinitions[1].Streams[0].StreamID) - require.Len(t, currentDefinitions, 1) - }) - - t.Run("skips owner update to same channel with colliding FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - // Existing channel 2 with feedID2 - currentDefinitions[2] = makeChannelDefinitionWithFeedID(2, SourceOwner, feedID2) - - // Owner tries to update channel 1 with feedID2 (collides with channel 2) - newDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID2) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channel 1 should still have feedID1 (update was skipped) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Equal(t, feedID1, extractFeedID(currentDefinitions[1].Opts)) - // Channel 2 should still be present - require.Contains(t, currentDefinitions, llotypes.ChannelID(2)) - require.Len(t, currentDefinitions, 2) - }) - - t.Run("skips adder channel with colliding FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing owner channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - - // Adder tries to add channel 2 with same feedID1 (collision) - newDefinitions[2] = makeChannelDefinitionWithFeedID(2, adderID, feedID1) - - // Adder tries to add channel 3 with unique feedID2 (should be added) - newDefinitions[3] = makeChannelDefinitionWithFeedID(3, adderID, feedID2) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(adderID, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channel 1 should be present (existing) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - // Channel 2 should NOT be present (collision, skipped) - require.NotContains(t, currentDefinitions, llotypes.ChannelID(2)) - // Channel 3 should be present (unique FeedID) - require.Contains(t, currentDefinitions, llotypes.ChannelID(3)) - require.Len(t, currentDefinitions, 2) - }) - - t.Run("allows owner to update channel with new unique FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - - // Owner updates channel 1 with new unique feedID2 (should be allowed) - newDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID2) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channel 1 should be present with new feedID2 - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Equal(t, feedID2, extractFeedID(currentDefinitions[1].Opts)) - require.Len(t, currentDefinitions, 1) - }) - - t.Run("skips multiple channels with same colliding FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - - // Multiple new channels with same colliding feedID1 - newDefinitions[2] = makeChannelDefinitionWithFeedID(2, SourceOwner, feedID1) - newDefinitions[3] = makeChannelDefinitionWithFeedID(3, SourceOwner, feedID1) - newDefinitions[4] = makeChannelDefinitionWithFeedID(4, SourceOwner, feedID1) - - // One channel with unique feedID2 - newDefinitions[5] = makeChannelDefinitionWithFeedID(5, SourceOwner, feedID2) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // Channel 1 should be present (existing) - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - // Channels 2, 3, 4 should NOT be present (all collided) - require.NotContains(t, currentDefinitions, llotypes.ChannelID(2)) - require.NotContains(t, currentDefinitions, llotypes.ChannelID(3)) - require.NotContains(t, currentDefinitions, llotypes.ChannelID(4)) - // Channel 5 should be present (unique FeedID) - require.Contains(t, currentDefinitions, llotypes.ChannelID(5)) - require.Len(t, currentDefinitions, 2) - }) - - t.Run("allows channels without FeedID", func(t *testing.T) { - currentDefinitions := make(llotypes.ChannelDefinitions) - newDefinitions := make(llotypes.ChannelDefinitions) - - // Existing channel 1 with feedID1 - currentDefinitions[1] = makeChannelDefinitionWithFeedID(1, SourceOwner, feedID1) - - // New channel 2 without FeedID (should be allowed) - newDefinitions[2] = makeChannelDefinition(2, SourceOwner) - - // New channel 3 with unique feedID2 (should be allowed) - newDefinitions[3] = makeChannelDefinitionWithFeedID(3, SourceOwner, feedID2) - - feedIDToChannelID := buildFeedIDMap(currentDefinitions) - cdc.mergeDefinitions(SourceOwner, currentDefinitions, newDefinitions, feedIDToChannelID) - - // All channels should be present - require.Contains(t, currentDefinitions, llotypes.ChannelID(1)) - require.Contains(t, currentDefinitions, llotypes.ChannelID(2)) - require.Contains(t, currentDefinitions, llotypes.ChannelID(3)) - require.Len(t, currentDefinitions, 3) - }) - }) -} - -func Test_filterName(t *testing.T) { - s := ChannelDefinitionCacheFilterName(common.Address{1, 2, 3}, 654) - require.Equal(t, "OCR3 LLO ChannelDefinitionCachePoller - 0x0102030000000000000000000000000000000000:654", s) -} - -func Test_decodePersistedSourceDefinitions(t *testing.T) { - t.Run("successful unmarshaling without key 1", func(t *testing.T) { - source2 := uint32(2) - source3 := uint32(3) - - definitions := map[uint32]types.SourceDefinition{ - source2: { - Trigger: types.Trigger{ - Source: source2, - URL: "http://example.com/source2.json", - SHA: [32]byte{1, 2, 3}, - BlockNum: 1000, - Version: 0, - }, - Definitions: llotypes.ChannelDefinitions{ - 10: makeChannelDefinition(10, source2), - 11: makeChannelDefinition(11, source2), - }, - }, - source3: { - Trigger: types.Trigger{ - Source: source3, - URL: "http://example.com/source3.json", - SHA: [32]byte{4, 5, 6}, - BlockNum: 2000, - Version: 0, - }, - Definitions: llotypes.ChannelDefinitions{ - 20: makeChannelDefinition(20, source3), - }, - }, - } - - definitionsJSON, err := json.Marshal(definitions) - require.NoError(t, err) - - result, err := decodePersistedSourceDefinitions(definitionsJSON) - require.NoError(t, err) - require.Len(t, result, 2) - require.Contains(t, result, source2) - require.Contains(t, result, source3) - require.Equal(t, source2, result[source2].Trigger.Source) - require.Equal(t, source3, result[source3].Trigger.Source) - require.Equal(t, source2, result[source2].Definitions[10].Source) - require.Equal(t, source3, result[source3].Definitions[20].Source) - }) - - t.Run("successful unmarshaling with key 1 (migration case)", func(t *testing.T) { - legacyKey := uint32(1) - channelID1 := llotypes.ChannelID(100) - channelID2 := llotypes.ChannelID(200) - - definitions := map[uint32]types.SourceDefinition{ - legacyKey: { - Trigger: types.Trigger{ - Source: legacyKey, // This should be migrated to SourceOwner - URL: "http://example.com/owner.json", - SHA: [32]byte{7, 8, 9}, - BlockNum: 3000, - Version: 42, - }, - Definitions: llotypes.ChannelDefinitions{ - channelID1: makeChannelDefinition(channelID1, legacyKey), - channelID2: makeChannelDefinition(channelID2, legacyKey), - }, - }, - } - - definitionsJSON, err := json.Marshal(definitions) - require.NoError(t, err) - - result, err := decodePersistedSourceDefinitions(definitionsJSON) - require.NoError(t, err) - require.Len(t, result, 1) - require.NotContains(t, result, legacyKey, "legacy key 1 should not be present") - require.Contains(t, result, SourceOwner, "SourceOwner key should be present") - - sourceDef := result[SourceOwner] - require.Equal(t, SourceOwner, sourceDef.Trigger.Source, "Trigger.Source should be migrated to SourceOwner") - require.Equal(t, uint32(42), sourceDef.Trigger.Version) - require.Equal(t, "http://example.com/owner.json", sourceDef.Trigger.URL) - - require.Equal(t, SourceOwner, sourceDef.Definitions[channelID1].Source, "Channel definition Source should be migrated to SourceOwner") - require.Equal(t, SourceOwner, sourceDef.Definitions[channelID2].Source, "Channel definition Source should be migrated to SourceOwner") - }) - - t.Run("invalid JSON", func(t *testing.T) { - testCases := []struct { - name string - jsonData string - expectedErrMsg string - }{ - { - name: "empty string", - jsonData: "", - expectedErrMsg: "failed to unmarshal persisted definitions", - }, - { - name: "invalid JSON syntax", - jsonData: `{"1": {invalid json}`, - expectedErrMsg: "failed to unmarshal persisted definitions", - }, - { - name: "malformed JSON", - jsonData: `{not valid json}`, - expectedErrMsg: "failed to unmarshal persisted definitions", - }, - { - name: "wrong type", - jsonData: `"not an object"`, - expectedErrMsg: "failed to unmarshal persisted definitions", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - result, err := decodePersistedSourceDefinitions(json.RawMessage(tc.jsonData)) - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), tc.expectedErrMsg) - }) - } - }) - - t.Run("empty JSON object", func(t *testing.T) { - definitionsJSON := json.RawMessage(`{}`) - - result, err := decodePersistedSourceDefinitions(definitionsJSON) - require.NoError(t, err) - require.NotNil(t, result) - require.Empty(t, result) - }) - - t.Run("multiple sources including key 1", func(t *testing.T) { - legacyKey := uint32(1) - source2 := uint32(2) - source3 := uint32(3) - - definitions := map[uint32]types.SourceDefinition{ - legacyKey: { - Trigger: types.Trigger{ - Source: legacyKey, - URL: "http://example.com/owner.json", - SHA: [32]byte{1, 1, 1}, - BlockNum: 1000, - Version: 10, - }, - Definitions: llotypes.ChannelDefinitions{ - 1: makeChannelDefinition(1, legacyKey), - }, - }, - source2: { - Trigger: types.Trigger{ - Source: source2, - URL: "http://example.com/source2.json", - SHA: [32]byte{2, 2, 2}, - BlockNum: 2000, - Version: 0, - }, - Definitions: llotypes.ChannelDefinitions{ - 2: makeChannelDefinition(2, source2), - }, - }, - source3: { - Trigger: types.Trigger{ - Source: source3, - URL: "http://example.com/source3.json", - SHA: [32]byte{3, 3, 3}, - BlockNum: 3000, - Version: 0, - }, - Definitions: llotypes.ChannelDefinitions{ - 3: makeChannelDefinition(3, source3), - }, - }, - } - - definitionsJSON, err := json.Marshal(definitions) - require.NoError(t, err) - - result, err := decodePersistedSourceDefinitions(definitionsJSON) - require.NoError(t, err) - require.Len(t, result, 3, "should have SourceOwner, source2, and source3") - require.NotContains(t, result, legacyKey, "legacy key 1 should not be present") - require.Contains(t, result, SourceOwner, "SourceOwner should be present") - require.Contains(t, result, source2, "source2 should be present") - require.Contains(t, result, source3, "source3 should be present") - - // Verify SourceOwner migration - require.Equal(t, SourceOwner, result[SourceOwner].Trigger.Source) - require.Equal(t, SourceOwner, result[SourceOwner].Definitions[1].Source) - - // Verify other sources are unchanged - require.Equal(t, source2, result[source2].Trigger.Source) - require.Equal(t, source2, result[source2].Definitions[2].Source) - require.Equal(t, source3, result[source3].Trigger.Source) - require.Equal(t, source3, result[source3].Definitions[3].Source) - }) - - t.Run("key 1 with definitions having different source values", func(t *testing.T) { - legacyKey := uint32(1) - source2 := uint32(2) - source3 := uint32(3) - - // Create definitions where key 1 has definitions with different source values - // This tests that all definitions under key 1 get migrated to SourceOwner - def1 := makeChannelDefinition(100, legacyKey) - def2 := makeChannelDefinition(200, source2) // Different source - def3 := makeChannelDefinition(300, source3) // Different source - - definitions := map[uint32]types.SourceDefinition{ - legacyKey: { - Trigger: types.Trigger{ - Source: legacyKey, - URL: "http://example.com/owner.json", - SHA: [32]byte{9, 9, 9}, - BlockNum: 4000, - Version: 50, - }, - Definitions: llotypes.ChannelDefinitions{ - 100: def1, - 200: def2, // Has source2, should be migrated to SourceOwner - 300: def3, // Has source3, should be migrated to SourceOwner - }, - }, - } - - definitionsJSON, err := json.Marshal(definitions) - require.NoError(t, err) - - result, err := decodePersistedSourceDefinitions(definitionsJSON) - require.NoError(t, err) - require.Len(t, result, 1) - require.Contains(t, result, SourceOwner) - - sourceDef := result[SourceOwner] - require.Equal(t, SourceOwner, sourceDef.Trigger.Source) - - // All definitions should have SourceOwner regardless of their original source - require.Equal(t, SourceOwner, sourceDef.Definitions[100].Source, "definition 100 should be migrated to SourceOwner") - require.Equal(t, SourceOwner, sourceDef.Definitions[200].Source, "definition 200 should be migrated to SourceOwner") - require.Equal(t, SourceOwner, sourceDef.Definitions[300].Source, "definition 300 should be migrated to SourceOwner") - }) -} diff --git a/core/services/llo/channeldefinitions/static_channel_definitions_cache.go b/core/services/llo/channeldefinitions/static_channel_definitions_cache.go deleted file mode 100644 index 8def75e8563..00000000000 --- a/core/services/llo/channeldefinitions/static_channel_definitions_cache.go +++ /dev/null @@ -1,57 +0,0 @@ -package channeldefinitions - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" -) - -// A CDC that loads a static JSON of channel definitions; useful for -// benchmarking and testing - -var _ llotypes.ChannelDefinitionCache = &staticCDC{} - -type staticCDC struct { - services.StateMachine - lggr logger.Logger - - definitions llotypes.ChannelDefinitions -} - -func NewStaticChannelDefinitionCache(lggr logger.Logger, dfnstr string) (llotypes.ChannelDefinitionCache, error) { - var definitions llotypes.ChannelDefinitions - if err := json.Unmarshal([]byte(dfnstr), &definitions); err != nil { - return nil, fmt.Errorf("failed to unmarshal static channel definitions: %w", err) - } - return &staticCDC{services.StateMachine{}, logger.Named(lggr, "StaticChannelDefinitionCache"), definitions}, nil -} - -func (s *staticCDC) Start(context.Context) error { - return s.StartOnce("StaticChannelDefinitionCache", func() error { - s.lggr.Infow("Initialized static channel definition cache", "definitions", s.definitions) - return nil - }) -} - -func (s *staticCDC) Close() error { - return s.StopOnce("StaticChannelDefinitionCache", func() error { - return nil - }) -} - -func (s *staticCDC) Definitions(prev llotypes.ChannelDefinitions) llotypes.ChannelDefinitions { - return s.definitions -} - -func (s *staticCDC) HealthReport() map[string]error { - report := map[string]error{s.Name(): s.Healthy()} - return report -} - -func (s *staticCDC) Name() string { - return s.lggr.Name() -} diff --git a/core/services/llo/cleanup.go b/core/services/llo/cleanup.go index 1366795467a..1e86ada4ca7 100644 --- a/core/services/llo/cleanup.go +++ b/core/services/llo/cleanup.go @@ -11,7 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" + "github.com/smartcontractkit/chainlink-evm/pkg/llo" + "github.com/smartcontractkit/chainlink-evm/pkg/llo/channeldefinitions" ) type LogPoller interface { @@ -23,7 +24,7 @@ func Cleanup(ctx context.Context, lp LogPoller, addr common.Address, donID uint3 if err := lp.UnregisterFilter(ctx, channeldefinitions.ChannelDefinitionCacheFilterName(addr, donID)); err != nil { return fmt.Errorf("failed to unregister filter: %w", err) } - orm := NewChainScopedORM(ds, chainSelector) + orm := llo.NewChainScopedORM(ds, chainSelector) if err := orm.CleanupChannelDefinitions(ctx, addr, donID); err != nil { return fmt.Errorf("failed to cleanup channel definitions: %w", err) } diff --git a/core/services/llo/cleanup_test.go b/core/services/llo/cleanup_test.go index bf1856cdb04..10f586e24e6 100644 --- a/core/services/llo/cleanup_test.go +++ b/core/services/llo/cleanup_test.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + "github.com/smartcontractkit/chainlink-evm/pkg/llo" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" ) @@ -66,7 +67,7 @@ func Test_Cleanup(t *testing.T) { chainSelector := uint64(3) // add some channel definitions - cdcorm := NewChainScopedORM(ds, chainSelector) + cdcorm := llo.NewChainScopedORM(ds, chainSelector) { err := cdcorm.StoreChannelDefinitions(ctx, addr1, donID1, 1, json.RawMessage(`{}`), 1, 1) require.NoError(t, err) diff --git a/core/services/llo/orm.go b/core/services/llo/orm.go deleted file mode 100644 index 49767752067..00000000000 --- a/core/services/llo/orm.go +++ /dev/null @@ -1,68 +0,0 @@ -package llo - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" - - "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - "github.com/smartcontractkit/chainlink-data-streams/llo/types" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" -) - -type ChainScopedORM interface { - channeldefinitions.ChannelDefinitionCacheORM -} - -var _ ChainScopedORM = &chainScopedORM{} - -type chainScopedORM struct { - ds sqlutil.DataSource - chainSelector uint64 -} - -func NewChainScopedORM(ds sqlutil.DataSource, chainSelector uint64) ChainScopedORM { - return &chainScopedORM{ds, chainSelector} -} - -func (o *chainScopedORM) LoadChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (pd *types.PersistedDefinitions, err error) { - pd = new(types.PersistedDefinitions) - err = o.ds.GetContext(ctx, pd, "SELECT * FROM channel_definitions WHERE chain_selector = $1 AND addr = $2 AND don_id = $3", o.chainSelector, addr, donID) - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } else if err != nil { - return nil, fmt.Errorf("failed to LoadChannelDefinitions; %w", err) - } - - return pd, nil -} - -// StoreChannelDefinitions will store a ChannelDefinitions list for a given chain_selector, addr, don_id -// It updates if the new version is greater than the existing record OR if the block number has changed -// (indicating definitions were updated even if version hasn't progressed) -func (o *chainScopedORM) StoreChannelDefinitions(ctx context.Context, addr common.Address, donID, version uint32, dfns json.RawMessage, blockNum int64, format uint32) error { - _, err := o.ds.ExecContext(ctx, ` -INSERT INTO channel_definitions (chain_selector, addr, don_id, definitions, block_num, version, updated_at, format) -VALUES ($1, $2, $3, $4, $5, $6, NOW(), $7) -ON CONFLICT (chain_selector, addr, don_id) DO UPDATE -SET definitions = $4, block_num = $5, version = $6, updated_at = NOW(), format = $7 -WHERE EXCLUDED.don_id = channel_definitions.don_id AND EXCLUDED.chain_selector = channel_definitions.chain_selector -AND (EXCLUDED.version >= channel_definitions.version OR EXCLUDED.block_num >= channel_definitions.block_num)`, - o.chainSelector, addr, donID, dfns, blockNum, version, format) - if err != nil { - return fmt.Errorf("StoreChannelDefinitions failed: %w", err) - } - return nil -} - -func (o *chainScopedORM) CleanupChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) error { - _, err := o.ds.ExecContext(ctx, "DELETE FROM channel_definitions WHERE chain_selector = $1 AND addr = $2 AND don_id = $3", o.chainSelector, addr, donID) - if err != nil { - return fmt.Errorf("failed to CleanupChannelDefinitions; %w", err) - } - return nil -} diff --git a/core/services/llo/orm_test.go b/core/services/llo/orm_test.go deleted file mode 100644 index 58afe1ac901..00000000000 --- a/core/services/llo/orm_test.go +++ /dev/null @@ -1,232 +0,0 @@ -package llo - -import ( - "encoding/json" - "fmt" - "math/rand" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - "github.com/smartcontractkit/chainlink-data-streams/llo/types" - "github.com/smartcontractkit/chainlink-evm/pkg/testutils" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" -) - -func Test_ORM(t *testing.T) { - const ETHMainnetChainSelector uint64 = 5009297550715157269 - const OtherChainSelector uint64 = 1234567890 - - db := pgtest.NewSqlxDB(t) - orm := NewChainScopedORM(db, ETHMainnetChainSelector) - ctx := t.Context() - - addr1 := testutils.NewAddress() - addr2 := testutils.NewAddress() - addr3 := testutils.NewAddress() - - donID1 := uint32(1) - donID2 := uint32(2) - - t.Run("LoadChannelDefinitions", func(t *testing.T) { - t.Run("returns zero values if nothing in database", func(t *testing.T) { - pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) - assert.NoError(t, err) - assert.Nil(t, pd) - }) - t.Run("loads channel definitions from database for the given don ID", func(t *testing.T) { - expectedBlockNum := rand.Int63() - expectedBlockNum2 := rand.Int63() - cid1 := rand.Uint32() - cid2 := rand.Uint32() - sourceID := uint32(1) - - channelDefsJSON := fmt.Sprintf(` -{ - "%d": { - "trigger": { - "source": %d, - "url": "", - "sha": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - "block_num": 142, - "log_index": 0, - "version": 1, - "tx_hash": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - }, - "definitions": { - "%d": { - "reportFormat": 42, - "chainSelector": 142, - "streams": [{"streamId": 1, "aggregator": "median"}, {"streamId": 2, "aggregator": "mode"}], - "opts": {"foo":"bar"} - }, - "%d": { - "reportFormat": 43, - "chainSelector": 142, - "streams": [{"streamId": 1, "aggregator": "median"}, {"streamId": 3, "aggregator": "quote"}] - } - } - } -} - `, sourceID, sourceID, cid1, cid2) - pgtest.MustExec(t, db, ` - INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at, format) - VALUES ($1, $2, $3, $4, $5, $6, NOW(), $7) - `, addr1, ETHMainnetChainSelector, 1, channelDefsJSON, expectedBlockNum, 1, 1) - - pgtest.MustExec(t, db, ` - INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at, format) - VALUES ($1, $2, $3, $4, $5, $6, NOW(), $7) - `, addr2, ETHMainnetChainSelector, 1, `{}`, expectedBlockNum2, 1, 1) - - { - // alternative chain selector; we expect these ones to be ignored - pgtest.MustExec(t, db, ` - INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at, format) - VALUES ($1, $2, $3, $4, $5, $6, NOW(), $7) - `, addr1, OtherChainSelector, 1, channelDefsJSON, expectedBlockNum, 1, 1) - pgtest.MustExec(t, db, ` - INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at, format) - VALUES ($1, $2, $3, $4, $5, $6, NOW(), $7) - `, addr3, OtherChainSelector, 1, channelDefsJSON, expectedBlockNum, 1, 1) - } - - pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) - require.NoError(t, err) - - assert.Equal(t, ETHMainnetChainSelector, pd.ChainSelector) - assert.Equal(t, addr1, pd.Address) - assert.Equal(t, expectedBlockNum, pd.BlockNum) - assert.Equal(t, donID1, pd.DonID) - assert.Equal(t, uint32(1), pd.Version) - assert.Equal(t, uint32(1), pd.Format) - - // Unmarshal the definitions from json.RawMessage - var loadedDefs map[uint32]types.SourceDefinition - err = json.Unmarshal(pd.Definitions, &loadedDefs) - require.NoError(t, err) - require.Len(t, loadedDefs, 1) - assert.Equal(t, sourceID, loadedDefs[sourceID].Trigger.Source) - assert.Equal(t, int64(142), loadedDefs[sourceID].Trigger.BlockNum) - assert.Equal(t, llotypes.ChannelDefinitions{ - cid1: llotypes.ChannelDefinition{ - ReportFormat: 42, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}}, - Opts: []byte(`{"foo":"bar"}`), - }, - cid2: llotypes.ChannelDefinition{ - ReportFormat: 43, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, - }, - }, loadedDefs[sourceID].Definitions) - - // does not load erroneously for a different address - pd, err = orm.LoadChannelDefinitions(ctx, addr2, donID1) - require.NoError(t, err) - - // Unmarshal empty definitions - var emptyDefs map[uint32]types.SourceDefinition - err = json.Unmarshal(pd.Definitions, &emptyDefs) - require.NoError(t, err) - assert.Empty(t, emptyDefs) - assert.Equal(t, expectedBlockNum2, pd.BlockNum) - - // does not load erroneously for a different don ID - pd, err = orm.LoadChannelDefinitions(ctx, addr1, donID2) - require.NoError(t, err) - - assert.Equal(t, (*types.PersistedDefinitions)(nil), pd) - }) - }) - - t.Run("StoreChannelDefinitions", func(t *testing.T) { - expectedBlockNum := rand.Int63() - cid1 := rand.Uint32() - cid2 := rand.Uint32() - cid3 := rand.Uint32() - cid4 := rand.Uint32() - defs := map[uint32]types.SourceDefinition{ - 1: { - Trigger: types.Trigger{ - Source: 1, - BlockNum: 142, - Version: 42, - }, - Definitions: llotypes.ChannelDefinitions{ - cid1: llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}}, - Opts: []byte(`{"foo":"bar"}`), - }, - cid2: llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatEVMPremiumLegacy, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, - }, - }, - }, - 2: { - Trigger: types.Trigger{ - Source: 2, - BlockNum: 142, - Version: 42, - }, - Definitions: llotypes.ChannelDefinitions{ - cid3: llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatJSON, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}}, - Opts: []byte(`{"foo":"bar"}`), - }, - cid4: llotypes.ChannelDefinition{ - ReportFormat: llotypes.ReportFormatEVMPremiumLegacy, - Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, - }, - }, - }, - } - - // Marshal definitions to json.RawMessage - defsJSON, err := json.Marshal(defs) - require.NoError(t, err) - - t.Run("stores channel definitions in the database", func(t *testing.T) { - err := orm.StoreChannelDefinitions(ctx, addr1, donID1, 42, defsJSON, expectedBlockNum, channeldefinitions.MultiChannelDefinitionsFormat) - require.NoError(t, err) - - pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) - require.NoError(t, err) - assert.Equal(t, ETHMainnetChainSelector, pd.ChainSelector) - assert.Equal(t, addr1, pd.Address) - assert.Equal(t, expectedBlockNum, pd.BlockNum) - assert.Equal(t, donID1, pd.DonID) - assert.Equal(t, uint32(42), pd.Version) - assert.Equal(t, channeldefinitions.MultiChannelDefinitionsFormat, pd.Format) - - // Unmarshal and compare - var loadedDefs map[uint32]types.SourceDefinition - err = json.Unmarshal(pd.Definitions, &loadedDefs) - require.NoError(t, err) - assert.Equal(t, defs, loadedDefs) - }) - t.Run("does not update if version is older than the database persisted version", func(t *testing.T) { - // try to update with an older version - emptyDefsJSON, err := json.Marshal(map[uint32]types.SourceDefinition{}) - require.NoError(t, err) - err = orm.StoreChannelDefinitions(ctx, addr1, donID1, 41, emptyDefsJSON, expectedBlockNum-1, channeldefinitions.MultiChannelDefinitionsFormat) - require.NoError(t, err) - - pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) - require.NoError(t, err) - assert.Equal(t, uint32(42), pd.Version) - - // Unmarshal and verify original definitions are still there - var loadedDefs map[uint32]types.SourceDefinition - err = json.Unmarshal(pd.Definitions, &loadedDefs) - require.NoError(t, err) - assert.Equal(t, defs, loadedDefs) - }) - }) -} diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index a83b86b04af..f26171a22c0 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -27,19 +27,19 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils" llotypes2 "github.com/smartcontractkit/chainlink-data-streams/llo/types" + "github.com/smartcontractkit/chainlink-evm/gethwrappers/llo-feeds/generated/channel_config_store" "github.com/smartcontractkit/chainlink-evm/pkg/assets" "github.com/smartcontractkit/chainlink-evm/pkg/client" "github.com/smartcontractkit/chainlink-evm/pkg/heads/headstest" + "github.com/smartcontractkit/chainlink-evm/pkg/llo" + "github.com/smartcontractkit/chainlink-evm/pkg/llo/channeldefinitions" "github.com/smartcontractkit/chainlink-evm/pkg/logpoller" evmtestutils "github.com/smartcontractkit/chainlink-evm/pkg/testutils" - "github.com/smartcontractkit/chainlink-evm/gethwrappers/llo-feeds/generated/channel_config_store" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/llo" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" ) type mockHTTPClient struct { diff --git a/core/services/relay/dummy/relayer.go b/core/services/relay/dummy/relayer.go index 529fa3d5305..83bacd2b64d 100644 --- a/core/services/relay/dummy/relayer.go +++ b/core/services/relay/dummy/relayer.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-data-streams/llo/retirement" "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/bm" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" + "github.com/smartcontractkit/chainlink-evm/pkg/llo/channeldefinitions" ) // The dummy relayer is a simple reference implementation that doesn't actually diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 474114c749a..154497017c1 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -44,6 +44,8 @@ import ( evmtoml "github.com/smartcontractkit/chainlink-evm/pkg/config/toml" "github.com/smartcontractkit/chainlink-evm/pkg/functions" "github.com/smartcontractkit/chainlink-evm/pkg/keys" + "github.com/smartcontractkit/chainlink-evm/pkg/llo" + "github.com/smartcontractkit/chainlink-evm/pkg/llo/channeldefinitions" evmmercury "github.com/smartcontractkit/chainlink-evm/pkg/mercury" "github.com/smartcontractkit/chainlink-evm/pkg/read" "github.com/smartcontractkit/chainlink-evm/pkg/transmitter" @@ -51,8 +53,6 @@ import ( "github.com/smartcontractkit/chainlink-evm/pkg/writer" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" - "github.com/smartcontractkit/chainlink/v2/core/services/llo" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" ) var ( diff --git a/core/services/relay/evm/llo_provider.go b/core/services/relay/evm/llo_provider.go index 34fe49729b8..801eb888fa9 100644 --- a/core/services/relay/evm/llo_provider.go +++ b/core/services/relay/evm/llo_provider.go @@ -35,7 +35,7 @@ import ( llotransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter" "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/bm" mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" - "github.com/smartcontractkit/chainlink/v2/core/services/llo/channeldefinitions" + "github.com/smartcontractkit/chainlink-evm/pkg/llo/channeldefinitions" ) var _ commontypes.LLOProvider = (*lloProvider)(nil) From a665bfe4604459ceaacfb42d71c8f959126d0e8a Mon Sep 17 00:00:00 2001 From: pavel-raykov Date: Thu, 21 May 2026 11:02:53 +0200 Subject: [PATCH 2/4] Gomods. --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- deployment/go.mod | 2 +- deployment/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- system-tests/lib/go.mod | 2 +- system-tests/lib/go.sum | 4 ++-- system-tests/tests/go.mod | 2 +- system-tests/tests/go.sum | 4 ++-- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 65166ad5d2a..7e140132810 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -47,7 +47,7 @@ require ( github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260514104516-a827acdffe43 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index fe206a06a8b..7834586b700 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1587,8 +1587,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/deployment/go.mod b/deployment/go.mod index 5a4930c5000..8f60cc8d0d6 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -46,7 +46,7 @@ require ( github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260514104516-a827acdffe43 diff --git a/deployment/go.sum b/deployment/go.sum index 5f57d4cadb6..5d3353d1641 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1388,8 +1388,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/go.mod b/go.mod index a8eaf2e8fe7..38ccd75a8f3 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,7 @@ require ( github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 diff --git a/go.sum b/go.sum index 81c1543505a..d64174cdbf7 100644 --- a/go.sum +++ b/go.sum @@ -1185,8 +1185,8 @@ github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.202605201 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260520194751-11a4f360f4e2/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f h1:vLlkJ+RxLV/k3bl4JN6WZOvNdwweVQmwxs+xXWL68jw= github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index ccdc67ebbf3..b36eb0343ec 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f5677da5559..7108e0378b2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1373,8 +1373,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index f14aa8ede60..40d5c3c32cc 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -22,7 +22,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7 github.com/smartcontractkit/chainlink-common v0.11.2-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.1 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.5 github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index f0c29f974cb..c6194c7876b 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1639,8 +1639,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index c7fb6ac1ae2..4da3dcf478c 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -36,7 +36,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260514104516-a827acdffe43 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index e320c771ddb..2b531e33508 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1554,8 +1554,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 3214debbb73..dc96734ddb2 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -511,7 +511,7 @@ require ( github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260520194751-11a4f360f4e2 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 // indirect + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 // indirect github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20260423135514-5b1a7565a99c // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260423135514-5b1a7565a99c // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 01851e57dac..6acbcec31ca 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1568,8 +1568,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374 h1:dWBveDfyBhtLJNaSAVJdHyYSEtTdxmAueMet2XFBHF0= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260519153648-e22912dab374/go.mod h1:ow+Q/Tl8iDgDFaMkQveJJWEL6odFZAmuYRUm/dwk26M= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= From dea5e98c0399b2c6f2fd62d61425d722909d552e Mon Sep 17 00:00:00 2001 From: pavel-raykov Date: Thu, 21 May 2026 11:22:52 +0200 Subject: [PATCH 3/4] Gomods. --- go.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/go.md b/go.md index f747300615b..4188cb721f9 100644 --- a/go.md +++ b/go.md @@ -91,8 +91,7 @@ flowchart LR click chainlink-data-streams href "https://github.com/smartcontractkit/chainlink-data-streams" chainlink-deployments-framework click chainlink-deployments-framework href "https://github.com/smartcontractkit/chainlink-deployments-framework" - chainlink-evm --> chainlink-common/keystore - chainlink-evm --> chainlink-evm/gethwrappers + chainlink-evm --> chainlink-data-streams chainlink-evm --> chainlink-framework/capabilities chainlink-evm --> chainlink-framework/chains chainlink-evm --> chainlink-protos/svr @@ -169,7 +168,6 @@ flowchart LR click chainlink-tron/relayer href "https://github.com/smartcontractkit/chainlink-tron" chainlink/v2 --> chainlink-automation chainlink/v2 --> chainlink-ccip/chains/evm - chainlink/v2 --> chainlink-data-streams chainlink/v2 --> chainlink-evm/contracts/cre/gobindings chainlink/v2 --> chainlink-feeds chainlink/v2 --> chainlink-protos/data-feeds @@ -381,8 +379,7 @@ flowchart LR chainlink-deployments-framework --> chainlink-tron/relayer chainlink-deployments-framework --> mcms click chainlink-deployments-framework href "https://github.com/smartcontractkit/chainlink-deployments-framework" - chainlink-evm --> chainlink-common/keystore - chainlink-evm --> chainlink-evm/gethwrappers + chainlink-evm --> chainlink-data-streams chainlink-evm --> chainlink-framework/capabilities chainlink-evm --> chainlink-framework/chains chainlink-evm --> chainlink-protos/svr @@ -582,7 +579,6 @@ flowchart LR chainlink/v2 --> chainlink-automation chainlink/v2 --> chainlink-ccip/chains/evm chainlink/v2 --> chainlink-ccv - chainlink/v2 --> chainlink-data-streams chainlink/v2 --> chainlink-evm/contracts/cre/gobindings chainlink/v2 --> chainlink-feeds chainlink/v2 --> chainlink-protos/data-feeds From 2eba8fdced4aa32370ee9fab98ab53b26fb7b86c Mon Sep 17 00:00:00 2001 From: pavel-raykov Date: Thu, 21 May 2026 16:57:14 +0200 Subject: [PATCH 4/4] Gomods. --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- deployment/go.mod | 2 +- deployment/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- system-tests/lib/go.mod | 2 +- system-tests/lib/go.sum | 4 ++-- system-tests/tests/go.mod | 2 +- system-tests/tests/go.sum | 4 ++-- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7e140132810..7c320fbb016 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -47,7 +47,7 @@ require ( github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260514104516-a827acdffe43 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7834586b700..e151a0e9768 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1587,8 +1587,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/deployment/go.mod b/deployment/go.mod index 8f60cc8d0d6..80132b0955d 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -46,7 +46,7 @@ require ( github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260514104516-a827acdffe43 diff --git a/deployment/go.sum b/deployment/go.sum index 5d3353d1641..9488b0907f1 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1388,8 +1388,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/go.mod b/go.mod index 38ccd75a8f3..42f3107e1c5 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,7 @@ require ( github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 diff --git a/go.sum b/go.sum index d64174cdbf7..f11b29f2d45 100644 --- a/go.sum +++ b/go.sum @@ -1185,8 +1185,8 @@ github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.202605201 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260520194751-11a4f360f4e2/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f h1:vLlkJ+RxLV/k3bl4JN6WZOvNdwweVQmwxs+xXWL68jw= github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index b36eb0343ec..091b0a91f10 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 7108e0378b2..4b181fa3bf8 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1373,8 +1373,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 40d5c3c32cc..b19fcf7d031 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -22,7 +22,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7 github.com/smartcontractkit/chainlink-common v0.11.2-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.1 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.5 github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index c6194c7876b..f3930acff89 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1639,8 +1639,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index 4da3dcf478c..bde40466933 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -36,7 +36,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260520194751-11a4f360f4e2 github.com/smartcontractkit/chainlink-common/keystore v1.1.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260514104516-a827acdffe43 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index 2b531e33508..bba5322919f 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1554,8 +1554,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index dc96734ddb2..27882dbc418 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -511,7 +511,7 @@ require ( github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260520194751-11a4f360f4e2 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 // indirect + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c // indirect github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 // indirect github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20260423135514-5b1a7565a99c // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260423135514-5b1a7565a99c // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 6acbcec31ca..391b59e7160 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1568,8 +1568,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260520173048-0333bc7b082f/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 h1:Vp4EwkvxcBzgahIZdbWyCExDXLha93cS63xvwd2xwx8= github.com/smartcontractkit/chainlink-deployments-framework v0.105.0/go.mod h1:xFLOOpIz7vqqno4YngHZlF9MKqk8rnvQa9adVElUXaE= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0 h1:48IsBX174rI84eRfDhrSxhwYMioCuxUOP6hZ/rAZHEk= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521080649-280622dddcb0/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c h1:Udi8eopVcBCcr3+M3Jzbe3SksQdl1HxMaBMvD1XJh90= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260521145337-fdf89453516c/go.mod h1:pTPpE5TQD0re8MJ9mWx5VjmXXPXF/2ZYAdS5KcBcc/c= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI=