From 87e89695e3e800c2995f9258de466cbb8ddb84a8 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 25 Feb 2025 17:30:38 -0500 Subject: [PATCH 1/2] Support sub-second reports --- .changeset/empty-terms-mate.md | 11 + core/services/llo/bm/dummy_transmitter.go | 4 +- core/services/llo/evm/report_codec_common.go | 23 + .../report_codec_evm_abi_encode_unpacked.go | 11 +- ...port_codec_evm_abi_encode_unpacked_test.go | 138 ++-- .../llo/evm/report_codec_premium_legacy.go | 12 +- .../evm/report_codec_premium_legacy_test.go | 14 +- ...gin_scoped_retirement_report_cache_test.go | 7 +- core/services/llo/retirement_report_orm.go | 8 +- .../llo/retirement_report_orm_test.go | 4 + .../services/ocr2/plugins/llo/helpers_test.go | 15 + .../ocr2/plugins/llo/integration_test.go | 596 +++++++++++++++--- go.mod | 2 + go.sum | 2 - 14 files changed, 665 insertions(+), 182 deletions(-) create mode 100644 .changeset/empty-terms-mate.md create mode 100644 core/services/llo/evm/report_codec_common.go diff --git a/.changeset/empty-terms-mate.md b/.changeset/empty-terms-mate.md new file mode 100644 index 00000000000..dcd41989af3 --- /dev/null +++ b/.changeset/empty-terms-mate.md @@ -0,0 +1,11 @@ +--- +"chainlink": major +--- + +Upgrade LLO protocol to support sub-seconds reports. #breaking_change + +CAUTION: This release needs a careful rollout. It _must_ be tested on a staging DON first by setting half of the nodes to this version, while leaving half at the previous version, to verify inter-version interoperability before node-by-node rollout in production. + +NOTE: Protocol version 0 does NOT support gapless handover on sub-second reports. You must upgrade to version 1 for that. + +Rollout plan is here: https://smartcontract-it.atlassian.net/browse/MERC-6852 diff --git a/core/services/llo/bm/dummy_transmitter.go b/core/services/llo/bm/dummy_transmitter.go index af07423b9a5..3313266cf6f 100644 --- a/core/services/llo/bm/dummy_transmitter.go +++ b/core/services/llo/bm/dummy_transmitter.go @@ -74,8 +74,8 @@ func (t *transmitter) Transmit( "report.Report.ConfigDigest", r.ConfigDigest, "report.Report.SeqNr", r.SeqNr, "report.Report.ChannelID", r.ChannelID, - "report.Report.ValidAfterSeconds", r.ValidAfterSeconds, - "report.Report.ObservationTimestampSeconds", r.ObservationTimestampSeconds, + "report.Report.ValidAfterNanoseconds", r.ValidAfterNanoseconds, + "report.Report.ObservationTimestampNanoseconds", r.ObservationTimestampNanoseconds, "report.Report.Values", r.Values, "report.Report.Specimen", r.Specimen, ) diff --git a/core/services/llo/evm/report_codec_common.go b/core/services/llo/evm/report_codec_common.go new file mode 100644 index 00000000000..3a1162c3f7b --- /dev/null +++ b/core/services/llo/evm/report_codec_common.go @@ -0,0 +1,23 @@ +package evm + +import ( + "fmt" + "math" + + "github.com/smartcontractkit/chainlink-data-streams/llo" +) + +// Extracts nanosecond timestamps as uint32 number of seconds +func ExtractTimestamps(report llo.Report) (validAfterSeconds, observationTimestampSeconds uint32, err error) { + vas := report.ValidAfterNanoseconds / 1e9 + ots := report.ObservationTimestampNanoseconds / 1e9 + if vas > math.MaxUint32 { + err = fmt.Errorf("validAfterSeconds too large: %d", vas) + return + } + if ots > math.MaxUint32 { + err = fmt.Errorf("observationTimestampSeconds too large: %d", ots) + return + } + return uint32(vas), uint32(ots), nil +} diff --git a/core/services/llo/evm/report_codec_evm_abi_encode_unpacked.go b/core/services/llo/evm/report_codec_evm_abi_encode_unpacked.go index c24474f4a65..e477b91585f 100644 --- a/core/services/llo/evm/report_codec_evm_abi_encode_unpacked.go +++ b/core/services/llo/evm/report_codec_evm_abi_encode_unpacked.go @@ -103,13 +103,18 @@ func (r ReportCodecEVMABIEncodeUnpacked) Encode(ctx context.Context, report llo. return nil, fmt.Errorf("failed to decode opts; got: '%s'; %w", cd.Opts, err) } + validAfterSeconds, observationTimestampSeconds, err := ExtractTimestamps(report) + if err != nil { + return nil, fmt.Errorf("failed to extract timestamps; %w", err) + } + rf := BaseReportFields{ FeedID: opts.FeedID, - ValidFromTimestamp: report.ValidAfterSeconds + 1, - Timestamp: report.ObservationTimestampSeconds, + ValidFromTimestamp: validAfterSeconds + 1, + Timestamp: observationTimestampSeconds, NativeFee: CalculateFee(nativePrice, opts.BaseUSDFee), LinkFee: CalculateFee(linkPrice, opts.BaseUSDFee), - ExpiresAt: report.ObservationTimestampSeconds + opts.ExpirationWindow, + ExpiresAt: observationTimestampSeconds + opts.ExpirationWindow, } header, err := r.buildHeader(ctx, rf) diff --git a/core/services/llo/evm/report_codec_evm_abi_encode_unpacked_test.go b/core/services/llo/evm/report_codec_evm_abi_encode_unpacked_test.go index 5a7c4ee7409..83b02f33a92 100644 --- a/core/services/llo/evm/report_codec_evm_abi_encode_unpacked_test.go +++ b/core/services/llo/evm/report_codec_evm_abi_encode_unpacked_test.go @@ -107,13 +107,13 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { {Name: "baseMarketDepth", Type: mustNewABIType("int192")}, {Name: "quoteMarketDepth", Type: mustNewABIType("int192")}, }) - runTest := func(sampleFeedID common.Hash, sampleObservationsTimestamp, sampleValidAfterSeconds, sampleExpirationWindow uint32, priceMultiplier, marketDepthMultiplier *ubig.Big, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleDexBasedAssetPrice, sampleBaseMarketDepth, sampleQuoteMarketDepth decimal.Decimal) bool { + runTest := func(sampleFeedID common.Hash, sampleObservationTimestampNanoseconds, sampleValidAfterNanoseconds uint64, sampleExpirationWindow uint32, priceMultiplier, marketDepthMultiplier *ubig.Big, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleDexBasedAssetPrice, sampleBaseMarketDepth, sampleQuoteMarketDepth decimal.Decimal) bool { report := llo.Report{ - ConfigDigest: types.ConfigDigest{0x01}, - SeqNr: 0x02, - ChannelID: llotypes.ChannelID(0x03), - ValidAfterSeconds: sampleValidAfterSeconds, - ObservationTimestampSeconds: sampleObservationsTimestamp, + ConfigDigest: types.ConfigDigest{0x01}, + SeqNr: 0x02, + ChannelID: llotypes.ChannelID(0x03), + ValidAfterNanoseconds: sampleValidAfterNanoseconds, + ObservationTimestampNanoseconds: sampleObservationTimestampNanoseconds, Values: []llo.StreamValue{ &llo.Quote{Bid: decimal.NewFromFloat(6.1), Benchmark: sampleLinkBenchmarkPrice, Ask: decimal.NewFromFloat(8.2332)}, // Link price &llo.Quote{Bid: decimal.NewFromFloat(9.4), Benchmark: sampleNativeBenchmarkPrice, Ask: decimal.NewFromFloat(11.33)}, // Native price @@ -199,11 +199,11 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { return AllTrue([]bool{ assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), //nolint:testifylint // false positive // feedId - assert.Equal(t, sampleValidAfterSeconds+1, values[1].(uint32)), // validFromTimestamp - assert.Equal(t, sampleObservationsTimestamp, values[2].(uint32)), // observationsTimestamp + assert.Equal(t, uint32(sampleValidAfterNanoseconds/1e9)+1, values[1].(uint32)), //nolint:gosec // G115 // validFromTimestamp + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9), values[2].(uint32)), //nolint:gosec // G115 // observationsTimestamp assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee - assert.Equal(t, sampleObservationsTimestamp+sampleExpirationWindow, values[5].(uint32)), // expiresAt + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9)+sampleExpirationWindow, values[5].(uint32)), //nolint:gosec // G115 generator ensures it wont overflow assert.Equal(t, sampleDexBasedAssetPrice.Mul(decimal.NewFromBigInt(priceMultiplier.ToInt(), 0)).BigInt(), values[6].(*big.Int)), // price assert.Equal(t, sampleBaseMarketDepth.Mul(decimal.NewFromBigInt(marketDepthMultiplier.ToInt(), 0)).BigInt(), values[7].(*big.Int)), // baseMarketDepth assert.Equal(t, sampleQuoteMarketDepth.Mul(decimal.NewFromBigInt(marketDepthMultiplier.ToInt(), 0)).BigInt(), values[8].(*big.Int)), // quoteMarketDepth @@ -213,8 +213,8 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { properties.Property("Encodes values", prop.ForAll( runTest, genFeedID(), - genObservationsTimestamp(), - genValidAfterSeconds(), + genObservationTimestampNanoseconds(), + genValidAfterNanoseconds(), genExpirationWindow(), genPriceMultiplier(), genMarketDepthMultiplier(), @@ -240,13 +240,13 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { {Name: "marketStatus", Type: mustNewABIType("uint32")}, }) - runTest := func(sampleFeedID common.Hash, sampleObservationsTimestamp, sampleValidAfterSeconds, sampleExpirationWindow uint32, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleMarketStatus decimal.Decimal) bool { + runTest := func(sampleFeedID common.Hash, sampleObservationTimestampNanoseconds, sampleValidAfterNanoseconds uint64, sampleExpirationWindow uint32, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleMarketStatus decimal.Decimal) bool { report := llo.Report{ - ConfigDigest: types.ConfigDigest{0x01}, - SeqNr: 0x02, - ChannelID: llotypes.ChannelID(0x03), - ValidAfterSeconds: sampleValidAfterSeconds, - ObservationTimestampSeconds: sampleObservationsTimestamp, + ConfigDigest: types.ConfigDigest{0x01}, + SeqNr: 0x02, + ChannelID: llotypes.ChannelID(0x03), + ValidAfterNanoseconds: sampleValidAfterNanoseconds, + ObservationTimestampNanoseconds: sampleObservationTimestampNanoseconds, Values: []llo.StreamValue{ &llo.Quote{Bid: decimal.NewFromFloat(6.1), Benchmark: sampleLinkBenchmarkPrice, Ask: decimal.NewFromFloat(8.2332)}, // Link price &llo.Quote{Bid: decimal.NewFromFloat(9.4), Benchmark: sampleNativeBenchmarkPrice, Ask: decimal.NewFromFloat(11.33)}, // Native price @@ -316,21 +316,21 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { require.Error(t, err) return AllTrue([]bool{ - assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), //nolint:testifylint // false positive // feedId - assert.Equal(t, sampleValidAfterSeconds+1, values[1].(uint32)), // validFromTimestamp - assert.Equal(t, sampleObservationsTimestamp, values[2].(uint32)), // observationsTimestamp - assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee - assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee - assert.Equal(t, sampleObservationsTimestamp+sampleExpirationWindow, values[5].(uint32)), // expiresAt - assert.Equal(t, uint32(sampleMarketStatus.BigInt().Int64()), values[6].(uint32)), //nolint:gosec // G115 // market status + assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), //nolint:testifylint // false positive // feedId + assert.Equal(t, uint32(sampleValidAfterNanoseconds/1e9)+1, values[1].(uint32)), //nolint:gosec // G115 // validFromTimestamp + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9), values[2].(uint32)), //nolint:gosec // G115 // observationsTimestamp + assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee + assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9)+sampleExpirationWindow, values[5].(uint32)), //nolint:gosec // G115 // expiresAt + assert.Equal(t, uint32(sampleMarketStatus.BigInt().Int64()), values[6].(uint32)), //nolint:gosec // G115 // market status }) } properties.Property("Encodes values", prop.ForAll( runTest, genFeedID(), - genObservationsTimestamp(), - genValidAfterSeconds(), + genObservationTimestampNanoseconds(), + genValidAfterNanoseconds(), genExpirationWindow(), genBaseUSDFee(), genLinkBenchmarkPrice(), @@ -351,13 +351,13 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { {Name: "expiresAt", Type: mustNewABIType("uint32")}, {Name: "price", Type: mustNewABIType("int192")}, }) - runTest := func(sampleFeedID common.Hash, sampleObservationsTimestamp, sampleValidAfterSeconds, sampleExpirationWindow uint32, priceMultiplier, marketDepthMultiplier *ubig.Big, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleBenchmarkPrice decimal.Decimal) bool { + runTest := func(sampleFeedID common.Hash, sampleObservationTimestampNanoseconds, sampleValidAfterNanoseconds uint64, sampleExpirationWindow uint32, priceMultiplier, marketDepthMultiplier *ubig.Big, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleBenchmarkPrice decimal.Decimal) bool { report := llo.Report{ - ConfigDigest: types.ConfigDigest{0x01}, - SeqNr: 0x02, - ChannelID: llotypes.ChannelID(0x03), - ValidAfterSeconds: sampleValidAfterSeconds, - ObservationTimestampSeconds: sampleObservationsTimestamp, + ConfigDigest: types.ConfigDigest{0x01}, + SeqNr: 0x02, + ChannelID: llotypes.ChannelID(0x03), + ValidAfterNanoseconds: sampleValidAfterNanoseconds, + ObservationTimestampNanoseconds: sampleObservationTimestampNanoseconds, Values: []llo.StreamValue{ &llo.Quote{Bid: decimal.NewFromFloat(6.1), Benchmark: sampleLinkBenchmarkPrice, Ask: decimal.NewFromFloat(8.2332)}, // Link price &llo.Quote{Bid: decimal.NewFromFloat(9.4), Benchmark: sampleNativeBenchmarkPrice, Ask: decimal.NewFromFloat(11.33)}, // Native price @@ -421,11 +421,11 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { return AllTrue([]bool{ assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), //nolint:testifylint // false positive // feedId - assert.Equal(t, sampleValidAfterSeconds+1, values[1].(uint32)), // validFromTimestamp - assert.Equal(t, sampleObservationsTimestamp, values[2].(uint32)), // observationsTimestamp + assert.Equal(t, uint32(sampleValidAfterNanoseconds/1e9)+1, values[1].(uint32)), //nolint:gosec // G115 // validFromTimestamp + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9), values[2].(uint32)), //nolint:gosec // G115 // observationsTimestamp assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee - assert.Equal(t, sampleObservationsTimestamp+sampleExpirationWindow, values[5].(uint32)), // expiresAt + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9)+sampleExpirationWindow, values[5].(uint32)), //nolint:gosec // G115 // expiresAt assert.Equal(t, sampleBenchmarkPrice.Mul(decimal.NewFromBigInt(priceMultiplier.ToInt(), 0)).BigInt(), values[6].(*big.Int)), // price }) } @@ -433,8 +433,8 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { properties.Property("Encodes values", prop.ForAll( runTest, genFeedID(), - genObservationsTimestamp(), - genValidAfterSeconds(), + genObservationTimestampNanoseconds(), + genValidAfterNanoseconds(), genExpirationWindow(), genPriceMultiplier(), genMarketDepthMultiplier(), @@ -463,13 +463,13 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { {Name: "deribitFundingIntervalHours", Type: mustNewABIType("uint32")}, }) - runTest := func(sampleFeedID common.Hash, sampleObservationsTimestamp, sampleValidAfterSeconds, sampleExpirationWindow uint32, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleBinanceFundingRate, sampleBinanceFundingTime, sampleBinanceFundingIntervalHours, sampleDeribitFundingRate, sampleDeribitFundingTime, sampleDeribitFundingIntervalHours decimal.Decimal) bool { + runTest := func(sampleFeedID common.Hash, sampleObservationTimestampNanoseconds, sampleValidAfterNanoseconds uint64, sampleExpirationWindow uint32, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleBinanceFundingRate, sampleBinanceFundingTime, sampleBinanceFundingIntervalHours, sampleDeribitFundingRate, sampleDeribitFundingTime, sampleDeribitFundingIntervalHours decimal.Decimal) bool { report := llo.Report{ - ConfigDigest: types.ConfigDigest{0x01}, - SeqNr: 0x02, - ChannelID: llotypes.ChannelID(0x03), - ValidAfterSeconds: sampleValidAfterSeconds, - ObservationTimestampSeconds: sampleObservationsTimestamp, + ConfigDigest: types.ConfigDigest{0x01}, + SeqNr: 0x02, + ChannelID: llotypes.ChannelID(0x03), + ValidAfterNanoseconds: sampleValidAfterNanoseconds, + ObservationTimestampNanoseconds: sampleObservationTimestampNanoseconds, Values: []llo.StreamValue{ &llo.Quote{Bid: decimal.NewFromFloat(6.1), Benchmark: sampleLinkBenchmarkPrice, Ask: decimal.NewFromFloat(8.2332)}, // Link price &llo.Quote{Bid: decimal.NewFromFloat(9.4), Benchmark: sampleNativeBenchmarkPrice, Ask: decimal.NewFromFloat(11.33)}, // Native price @@ -545,26 +545,26 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { require.Error(t, err) return AllTrue([]bool{ - assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), //nolint:testifylint // false positive // feedId - assert.Equal(t, sampleValidAfterSeconds+1, values[1].(uint32)), // validFromTimestamp - assert.Equal(t, sampleObservationsTimestamp, values[2].(uint32)), // observationsTimestamp - assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee - assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee - assert.Equal(t, sampleObservationsTimestamp+sampleExpirationWindow, values[5].(uint32)), // expiresAt - assert.Equal(t, sampleBinanceFundingRate.BigInt().String(), values[6].(*big.Int).String()), // binanceFundingRate - assert.Equal(t, uint32(sampleBinanceFundingTime.BigInt().Int64()), values[7].(uint32)), //nolint:gosec // G115 // binanceFundingTime - assert.Equal(t, uint32(sampleBinanceFundingIntervalHours.BigInt().Int64()), values[8].(uint32)), //nolint:gosec // G115 // binanceFundingIntervalHours - assert.Equal(t, sampleDeribitFundingRate.BigInt().String(), values[9].(*big.Int).String()), // deribitFundingRate - assert.Equal(t, uint32(sampleDeribitFundingTime.BigInt().Int64()), values[10].(uint32)), //nolint:gosec // G115 // deribitFundingTime - assert.Equal(t, uint32(sampleDeribitFundingIntervalHours.BigInt().Int64()), values[11].(uint32)), //nolint:gosec // G115 // deribitFundingIntervalHours + assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), //nolint:testifylint // false positive // feedId + assert.Equal(t, uint32(sampleValidAfterNanoseconds/1e9)+1, values[1].(uint32)), //nolint:gosec // G115 // validFromTimestamp + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9), values[2].(uint32)), //nolint:gosec // G115 // observationsTimestamp + assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee + assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee + assert.Equal(t, uint32(sampleObservationTimestampNanoseconds/1e9)+sampleExpirationWindow, values[5].(uint32)), //nolint:gosec // G115 // expiresAt + assert.Equal(t, sampleBinanceFundingRate.BigInt().String(), values[6].(*big.Int).String()), // binanceFundingRate + assert.Equal(t, uint32(sampleBinanceFundingTime.BigInt().Int64()), values[7].(uint32)), //nolint:gosec // G115 // binanceFundingTime + assert.Equal(t, uint32(sampleBinanceFundingIntervalHours.BigInt().Int64()), values[8].(uint32)), //nolint:gosec // G115 // binanceFundingIntervalHours + assert.Equal(t, sampleDeribitFundingRate.BigInt().String(), values[9].(*big.Int).String()), // deribitFundingRate + assert.Equal(t, uint32(sampleDeribitFundingTime.BigInt().Int64()), values[10].(uint32)), //nolint:gosec // G115 // deribitFundingTime + assert.Equal(t, uint32(sampleDeribitFundingIntervalHours.BigInt().Int64()), values[11].(uint32)), //nolint:gosec // G115 // deribitFundingIntervalHours }) } properties.Property("Encodes values", prop.ForAll( runTest, genFeedID(), - genObservationsTimestamp(), - genValidAfterSeconds(), + genObservationTimestampNanoseconds(), + genValidAfterNanoseconds(), genExpirationWindow(), genBaseUSDFee(), genLinkBenchmarkPrice(), @@ -584,11 +584,11 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) { func TestReportCodecEVMABIEncodeUnpacked_Encode(t *testing.T) { t.Run("ABI and values length mismatch error", func(t *testing.T) { report := llo.Report{ - ConfigDigest: types.ConfigDigest{0x01}, - SeqNr: 0x02, - ChannelID: llotypes.ChannelID(0x03), - ValidAfterSeconds: 0x04, - ObservationTimestampSeconds: 0x05, + ConfigDigest: types.ConfigDigest{0x01}, + SeqNr: 0x02, + ChannelID: llotypes.ChannelID(0x03), + ValidAfterNanoseconds: 0x04, + ObservationTimestampNanoseconds: 0x05, Values: []llo.StreamValue{ &llo.Quote{Bid: decimal.NewFromFloat(6.1), Benchmark: decimal.NewFromFloat(7.4), Ask: decimal.NewFromFloat(8.2332)}, &llo.Quote{Bid: decimal.NewFromFloat(9.4), Benchmark: decimal.NewFromFloat(10.0), Ask: decimal.NewFromFloat(11.33)}, @@ -649,12 +649,18 @@ func genFeedID() gopter.Gen { } } -func genObservationsTimestamp() gopter.Gen { - return gen.UInt32() +func genObservationTimestampNanoseconds() gopter.Gen { + return genTimestampThatFitsUint32Seconds() } -func genValidAfterSeconds() gopter.Gen { - return gen.UInt32() +func genValidAfterNanoseconds() gopter.Gen { + return genTimestampThatFitsUint32Seconds() +} + +func genTimestampThatFitsUint32Seconds() gopter.Gen { + return gen.UInt32().Map(func(i uint32) uint64 { + return uint64(i) * 1e9 + }) } func genExpirationWindow() gopter.Gen { diff --git a/core/services/llo/evm/report_codec_premium_legacy.go b/core/services/llo/evm/report_codec_premium_legacy.go index b8dad12d9ad..e8ef4e55ab4 100644 --- a/core/services/llo/evm/report_codec_premium_legacy.go +++ b/core/services/llo/evm/report_codec_premium_legacy.go @@ -88,14 +88,17 @@ func (r ReportCodecPremiumLegacy) Encode(ctx context.Context, report llo.Report, multiplier = decimal.NewFromBigInt(opts.Multiplier.ToInt(), 0) } - codec := reportcodecv3.NewReportCodec(opts.FeedID, r.Logger) + validAfterSeconds, observationTimestampSeconds, err := ExtractTimestamps(report) + if err != nil { + return nil, fmt.Errorf("failed to extract timestamps; %w", err) + } rf := v3.ReportFields{ - ValidFromTimestamp: report.ValidAfterSeconds + 1, - Timestamp: report.ObservationTimestampSeconds, + ValidFromTimestamp: validAfterSeconds + 1, + Timestamp: observationTimestampSeconds, NativeFee: CalculateFee(nativePrice, opts.BaseUSDFee), LinkFee: CalculateFee(linkPrice, opts.BaseUSDFee), - ExpiresAt: report.ObservationTimestampSeconds + opts.ExpirationWindow, + ExpiresAt: observationTimestampSeconds + opts.ExpirationWindow, BenchmarkPrice: quote.Benchmark.Mul(multiplier).BigInt(), Bid: quote.Bid.Mul(multiplier).BigInt(), Ask: quote.Ask.Mul(multiplier).BigInt(), @@ -103,6 +106,7 @@ func (r ReportCodecPremiumLegacy) Encode(ctx context.Context, report llo.Report, r.Logger.Debugw("Encoding report", "report", report, "opts", opts, "nativePrice", nativePrice, "linkPrice", linkPrice, "quote", quote, "multiplier", multiplier, "rf", rf) + codec := reportcodecv3.NewReportCodec(opts.FeedID, r.Logger) return codec.BuildReport(ctx, rf) } diff --git a/core/services/llo/evm/report_codec_premium_legacy_test.go b/core/services/llo/evm/report_codec_premium_legacy_test.go index 19a2a297262..0934804c005 100644 --- a/core/services/llo/evm/report_codec_premium_legacy_test.go +++ b/core/services/llo/evm/report_codec_premium_legacy_test.go @@ -44,13 +44,13 @@ func FuzzReportCodecPremiumLegacy_Decode(f *testing.F) { func newValidPremiumLegacyReport() llo.Report { return llo.Report{ - ConfigDigest: types.ConfigDigest{1, 2, 3}, - SeqNr: 32, - ChannelID: llotypes.ChannelID(31), - ValidAfterSeconds: 28, - ObservationTimestampSeconds: 34, - Values: []llo.StreamValue{llo.ToDecimal(decimal.NewFromInt(35)), llo.ToDecimal(decimal.NewFromInt(36)), &llo.Quote{Bid: decimal.NewFromInt(37), Benchmark: decimal.NewFromInt(38), Ask: decimal.NewFromInt(39)}}, - Specimen: false, + ConfigDigest: types.ConfigDigest{1, 2, 3}, + SeqNr: 32, + ChannelID: llotypes.ChannelID(31), + ValidAfterNanoseconds: 28 * 1e9, + ObservationTimestampNanoseconds: 34 * 1e9, + Values: []llo.StreamValue{llo.ToDecimal(decimal.NewFromInt(35)), llo.ToDecimal(decimal.NewFromInt(36)), &llo.Quote{Bid: decimal.NewFromInt(37), Benchmark: decimal.NewFromInt(38), Ask: decimal.NewFromInt(39)}}, + Specimen: false, } } diff --git a/core/services/llo/plugin_scoped_retirement_report_cache_test.go b/core/services/llo/plugin_scoped_retirement_report_cache_test.go index f370cd9f1b4..e0a8486484a 100644 --- a/core/services/llo/plugin_scoped_retirement_report_cache_test.go +++ b/core/services/llo/plugin_scoped_retirement_report_cache_test.go @@ -149,9 +149,10 @@ func Test_PluginScopedRetirementReportCache(t *testing.T) { _, err = psrrc.CheckAttestedRetirementReport(exampleDigest, serializedValidArr) assert.EqualError(t, err, "Verify failed; failed to decode retirement report: codec decode failed") - exampleRetirementReport := datastreamsllo.RetirementReport{ValidAfterSeconds: map[llotypes.ChannelID]uint32{ - 0: 1, - }, + exampleRetirementReport := datastreamsllo.RetirementReport{ + ValidAfterNanoseconds: map[llotypes.ChannelID]uint64{ + 0: 1, + }, } // enough valid sigs and codec decode succeeds diff --git a/core/services/llo/retirement_report_orm.go b/core/services/llo/retirement_report_orm.go index 5601e4fcd2f..b967ce381a8 100644 --- a/core/services/llo/retirement_report_orm.go +++ b/core/services/llo/retirement_report_orm.go @@ -62,8 +62,12 @@ func (o *retirementReportCacheORM) LoadAttestedRetirementReports(ctx context.Con } func (o *retirementReportCacheORM) StoreConfig(ctx context.Context, cd ocr2types.ConfigDigest, signers [][]byte, f uint8) error { - _, err := o.ds.ExecContext(ctx, `INSERT INTO llo_retirement_report_cache_configs (config_digest, signers, f, updated_at) VALUES ($1, $2, $3, NOW())`, cd, signers, f) - return err + // do nothing on overwrite since configs are supposedly immutable + _, err := o.ds.ExecContext(ctx, `INSERT INTO llo_retirement_report_cache_configs (config_digest, signers, f, updated_at) VALUES ($1, $2, $3, NOW()) ON CONFLICT (config_digest) DO NOTHING`, cd, signers, f) + if err != nil { + return fmt.Errorf("StoreConfig failed: %w", err) + } + return nil } type Config struct { diff --git a/core/services/llo/retirement_report_orm_test.go b/core/services/llo/retirement_report_orm_test.go index 835bfaaef76..9bc80ea5a58 100644 --- a/core/services/llo/retirement_report_orm_test.go +++ b/core/services/llo/retirement_report_orm_test.go @@ -42,6 +42,10 @@ func Test_RetirementReportCache_ORM(t *testing.T) { err = orm.StoreConfig(ctx, cd2, signers, 2) require.NoError(t, err) + + // overwriting does nothing, the 255 is ignored + err = orm.StoreConfig(ctx, cd2, signers, 255) + require.NoError(t, err) }) t.Run("LoadConfigs", func(t *testing.T) { configs, err := orm.LoadConfigs(ctx) diff --git a/core/services/ocr2/plugins/llo/helpers_test.go b/core/services/ocr2/plugins/llo/helpers_test.go index cdcd77fc97d..78eada23630 100644 --- a/core/services/ocr2/plugins/llo/helpers_test.go +++ b/core/services/ocr2/plugins/llo/helpers_test.go @@ -252,6 +252,11 @@ func setupNode( // [Log] c.Log.Level = ptr(toml.LogLevel(zapcore.DebugLevel)) // generally speaking we want debug level for logs unless overridden + // [EVM.Transactions] + for _, evmCfg := range c.EVM { + evmCfg.Transactions.Enabled = ptr(false) // don't need txmgr + } + // Optional overrides if f != nil { f(c) @@ -466,6 +471,16 @@ func createBridge(t *testing.T, bridgeName string, resultJSON string, borm bridg })) } +func addMemoStreamSpecs(t *testing.T, node Node, streams []Stream) { + for _, strm := range streams { + addStreamSpec(t, node, fmt.Sprintf("memo-%d", strm.id), &strm.id, fmt.Sprintf(` + value [type=memo value="%s"]; + multiply [type=multiply times=1]; + value -> multiply; + `, strm.baseBenchmarkPrice)) + } +} + func addOCRJobsEVMPremiumLegacy( t *testing.T, streams []Stream, diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index 634ab0be170..dc1b0185daa 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -10,6 +10,7 @@ import ( "net/http/httptest" "sort" "strings" + "sync" "testing" "time" @@ -177,74 +178,136 @@ var ( quoteStreamFeedID1 = common.HexToHash(`0x0003111111111111111111111111111111111111111111111111111111111111`) quoteStreamFeedID2 = common.HexToHash(`0x0003222222222222222222222222222222222222222222222222222222222222`) ethStream = Stream{ - id: 52, + id: ethStreamID, baseBenchmarkPrice: decimal.NewFromFloat32(2_976.39), } linkStream = Stream{ - id: 53, + id: linkStreamID, baseBenchmarkPrice: decimal.NewFromFloat32(13.25), } quoteStream1 = Stream{ - id: 55, + id: quoteStreamID1, baseBenchmarkPrice: decimal.NewFromFloat32(1000.1212), baseBid: decimal.NewFromFloat32(998.5431), baseAsk: decimal.NewFromFloat32(1001.6999), } quoteStream2 = Stream{ - id: 56, + id: quoteStreamID2, baseBenchmarkPrice: decimal.NewFromFloat32(500.1212), baseBid: decimal.NewFromFloat32(499.5431), baseAsk: decimal.NewFromFloat32(502.6999), } ) -func generateBlueGreenConfig(t *testing.T, oracles []confighelper.OracleIdentityExtra, predecessorConfigDigest *ocr2types.ConfigDigest) ( - signers []types.OnchainPublicKey, - transmitters []types.Account, - f uint8, - onchainConfig []byte, - offchainConfigVersion uint64, - offchainConfig []byte, -) { - onchainConfig, err := (&datastreamsllo.EVMOnchainConfigCodec{}).Encode(datastreamsllo.OnchainConfig{ +// see: https://github.com/smartcontractkit/offchain-reporting/blob/master/lib/offchainreporting2plus/internal/config/ocr3config/public_config.go +type OCRConfig struct { + DeltaProgress time.Duration + DeltaResend time.Duration + DeltaInitial time.Duration + DeltaRound time.Duration + DeltaGrace time.Duration + DeltaCertifiedCommitRequest time.Duration + DeltaStage time.Duration + RMax uint64 + S []int + Oracles []confighelper.OracleIdentityExtra + ReportingPluginConfig []byte + MaxDurationInitialization *time.Duration + MaxDurationQuery time.Duration + MaxDurationObservation time.Duration + MaxDurationShouldAcceptAttestedReport time.Duration + MaxDurationShouldTransmitAcceptedReport time.Duration + F int + OnchainConfig []byte +} + +func makeDefaultOCRConfig() *OCRConfig { + defaultOnchainConfig, err := (&datastreamsllo.EVMOnchainConfigCodec{}).Encode(datastreamsllo.OnchainConfig{ Version: 1, - PredecessorConfigDigest: predecessorConfigDigest, + PredecessorConfigDigest: nil, }) - require.NoError(t, err) - return generateConfig(t, oracles, onchainConfig) + if err != nil { + panic(err) + } + return &OCRConfig{ + DeltaProgress: 2 * time.Second, + DeltaResend: 20 * time.Second, + DeltaInitial: 400 * time.Millisecond, + DeltaRound: 500 * time.Millisecond, + DeltaGrace: 250 * time.Millisecond, + DeltaCertifiedCommitRequest: 300 * time.Millisecond, + DeltaStage: 1 * time.Minute, + RMax: 100, + ReportingPluginConfig: []byte{}, + MaxDurationInitialization: nil, + MaxDurationQuery: 0, + MaxDurationObservation: 250 * time.Millisecond, + MaxDurationShouldAcceptAttestedReport: 0, + MaxDurationShouldTransmitAcceptedReport: 0, + F: int(fNodes), + OnchainConfig: defaultOnchainConfig, + } } -func generateConfig(t *testing.T, oracles []confighelper.OracleIdentityExtra, inOnchainConfig []byte) ( - signers []types.OnchainPublicKey, - transmitters []types.Account, - f uint8, - outOnchainConfig []byte, - offchainConfigVersion uint64, - offchainConfig []byte, -) { - rawReportingPluginConfig := datastreamsllo.OffchainConfig{} - reportingPluginConfig, err := rawReportingPluginConfig.Encode() - require.NoError(t, err) +func WithPredecessorConfigDigest(predecessorConfigDigest ocr2types.ConfigDigest) OCRConfigOption { + return func(cfg *OCRConfig) { + onchainConfig, err := (&datastreamsllo.EVMOnchainConfigCodec{}).Encode(datastreamsllo.OnchainConfig{ + Version: 1, + PredecessorConfigDigest: &predecessorConfigDigest, + }) + if err != nil { + panic(err) + } + cfg.OnchainConfig = onchainConfig + } +} +func WithOffchainConfig(offchainConfig datastreamsllo.OffchainConfig) OCRConfigOption { + return func(cfg *OCRConfig) { + offchainConfigEncoded, err := offchainConfig.Encode() + if err != nil { + panic(err) + } + cfg.ReportingPluginConfig = offchainConfigEncoded + } +} + +func WithOracles(oracles []confighelper.OracleIdentityExtra) OCRConfigOption { + return func(cfg *OCRConfig) { + cfg.Oracles = oracles + cfg.S = []int{len(oracles)} // all oracles transmit by default + } +} + +type OCRConfigOption func(*OCRConfig) + +func generateConfig(t *testing.T, opts ...OCRConfigOption) (signers []types.OnchainPublicKey, transmitters []types.Account, f uint8, outOnchainConfig []byte, offchainConfigVersion uint64, offchainConfig []byte) { + cfg := makeDefaultOCRConfig() + + for _, opt := range opts { + opt(cfg) + } + t.Logf("Using OCR config: %+v\n", cfg) + var err error signers, transmitters, f, outOnchainConfig, offchainConfigVersion, offchainConfig, err = ocr3confighelper.ContractSetConfigArgsForTests( - 2*time.Second, // DeltaProgress - 20*time.Second, // DeltaResend - 400*time.Millisecond, // DeltaInitial - 500*time.Millisecond, // DeltaRound - 250*time.Millisecond, // DeltaGrace - 300*time.Millisecond, // DeltaCertifiedCommitRequest - 1*time.Minute, // DeltaStage - 100, // rMax - []int{len(oracles)}, // S - oracles, - reportingPluginConfig, // reportingPluginConfig []byte, - nil, // maxDurationInitialization - 0, // maxDurationQuery - 250*time.Millisecond, // maxDurationObservation - 0, // maxDurationShouldAcceptAttestedReport - 0, // maxDurationShouldTransmitAcceptedReport - int(fNodes), // f - inOnchainConfig, // encoded onchain config + cfg.DeltaProgress, + cfg.DeltaResend, + cfg.DeltaInitial, + cfg.DeltaRound, + cfg.DeltaGrace, + cfg.DeltaCertifiedCommitRequest, + cfg.DeltaStage, + cfg.RMax, + cfg.S, + cfg.Oracles, + cfg.ReportingPluginConfig, + cfg.MaxDurationInitialization, + cfg.MaxDurationQuery, + cfg.MaxDurationObservation, + cfg.MaxDurationShouldAcceptAttestedReport, + cfg.MaxDurationShouldTransmitAcceptedReport, + cfg.F, + cfg.OnchainConfig, ) require.NoError(t, err) @@ -252,14 +315,8 @@ func generateConfig(t *testing.T, oracles []confighelper.OracleIdentityExtra, in return } -func setLegacyConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, legacyVerifier *verifier.Verifier, legacyVerifierAddr common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra) ocr2types.ConfigDigest { - onchainConfig, err := (&datastreamsllo.EVMOnchainConfigCodec{}).Encode(datastreamsllo.OnchainConfig{ - Version: 1, - PredecessorConfigDigest: nil, - }) - require.NoError(t, err) - - signers, _, _, _, offchainConfigVersion, offchainConfig := generateConfig(t, oracles, onchainConfig) +func setLegacyConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, legacyVerifier *verifier.Verifier, legacyVerifierAddr common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra, inOffchainConfig datastreamsllo.OffchainConfig) ocr2types.ConfigDigest { + signers, _, _, onchainConfig, offchainConfigVersion, offchainConfig := generateConfig(t, WithOracles(oracles), WithOffchainConfig(inOffchainConfig)) signerAddresses, err := evm.OnchainPublicKeyToAddress(signers) require.NoError(t, err) @@ -283,16 +340,16 @@ func setLegacyConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backe return l.ConfigDigest } -func setStagingConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, configurator *configurator.Configurator, configuratorAddress common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra, predecessorConfigDigest ocr2types.ConfigDigest) ocr2types.ConfigDigest { - return setBlueGreenConfig(t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, &predecessorConfigDigest) +func setStagingConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, configurator *configurator.Configurator, configuratorAddress common.Address, nodes []Node, opts ...OCRConfigOption) ocr2types.ConfigDigest { + return setBlueGreenConfig(t, donID, steve, backend, configurator, configuratorAddress, nodes, opts...) } -func setProductionConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, configurator *configurator.Configurator, configuratorAddress common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra) ocr2types.ConfigDigest { - return setBlueGreenConfig(t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, nil) +func setProductionConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, configurator *configurator.Configurator, configuratorAddress common.Address, nodes []Node, opts ...OCRConfigOption) ocr2types.ConfigDigest { + return setBlueGreenConfig(t, donID, steve, backend, configurator, configuratorAddress, nodes, opts...) } -func setBlueGreenConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, configurator *configurator.Configurator, configuratorAddress common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra, predecessorConfigDigest *ocr2types.ConfigDigest) ocr2types.ConfigDigest { - signers, _, _, onchainConfig, offchainConfigVersion, offchainConfig := generateBlueGreenConfig(t, oracles, predecessorConfigDigest) +func setBlueGreenConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, configurator *configurator.Configurator, configuratorAddress common.Address, nodes []Node, opts ...OCRConfigOption) ocr2types.ConfigDigest { + signers, _, _, onchainConfig, offchainConfigVersion, offchainConfig := generateConfig(t, opts...) var onchainPubKeys [][]byte for _, signer := range signers { @@ -303,7 +360,12 @@ func setBlueGreenConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, ba offchainTransmitters[i] = nodes[i].ClientPubKey } donIDPadded := llo.DonIDToBytes32(donID) - isProduction := predecessorConfigDigest == nil + var isProduction bool + { + cfg, err := (&datastreamsllo.EVMOnchainConfigCodec{}).Decode(onchainConfig) + require.NoError(t, err) + isProduction = cfg.PredecessorConfigDigest == nil + } var err error if isProduction { _, err = configurator.SetProductionConfig(steve, donIDPadded, onchainPubKeys, offchainTransmitters, fNodes, onchainConfig, offchainConfigVersion, offchainConfig) @@ -348,7 +410,26 @@ func promoteStagingConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, func TestIntegration_LLO_evm_premium_legacy(t *testing.T) { t.Parallel() + offchainConfigs := []datastreamsllo.OffchainConfig{ + { + ProtocolVersion: 0, + DefaultMinReportIntervalNanoseconds: 0, + }, + { + ProtocolVersion: 1, + DefaultMinReportIntervalNanoseconds: 1, + }, + } + for _, offchainConfig := range offchainConfigs { + t.Run(fmt.Sprintf("offchainConfig=%+v", offchainConfig), func(t *testing.T) { + t.Parallel() + + testIntegrationLLOEVMPremiumLegacy(t, offchainConfig) + }) + } +} +func testIntegrationLLOEVMPremiumLegacy(t *testing.T, offchainConfig datastreamsllo.OffchainConfig) { testStartTimeStamp := time.Now() multiplier := decimal.New(1, 18) expirationWindow := time.Hour / time.Second @@ -389,7 +470,7 @@ func TestIntegration_LLO_evm_premium_legacy(t *testing.T) { } // Setup oracle nodes - oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, streams, func(c *chainlink.Config) { + oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, func(c *chainlink.Config) { c.Mercury.Transmitter.Protocol = ptr(config.MercuryTransmitterProtocolWSRPC) }) @@ -458,7 +539,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi // Set config on configurator setLegacyConfig( - t, donID, steve, backend, legacyVerifier, legacyVerifierAddr, nodes, oracles, + t, donID, steve, backend, legacyVerifier, legacyVerifierAddr, nodes, oracles, offchainConfig, ) // Set config on the destination verifier @@ -568,7 +649,26 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi func TestIntegration_LLO_evm_abi_encode_unpacked(t *testing.T) { t.Parallel() + offchainConfigs := []datastreamsllo.OffchainConfig{ + { + ProtocolVersion: 0, + DefaultMinReportIntervalNanoseconds: 0, + }, + { + ProtocolVersion: 1, + DefaultMinReportIntervalNanoseconds: 1, + }, + } + for _, offchainConfig := range offchainConfigs { + t.Run(fmt.Sprintf("offchainConfig=%+v", offchainConfig), func(t *testing.T) { + t.Parallel() + + testIntegrationLLOEVMABIEncodeUnpacked(t, offchainConfig) + }) + } +} +func testIntegrationLLOEVMABIEncodeUnpacked(t *testing.T, offchainConfig datastreamsllo.OffchainConfig) { testStartTimeStamp := time.Now() expirationWindow := uint32(3600) @@ -608,7 +708,7 @@ func TestIntegration_LLO_evm_abi_encode_unpacked(t *testing.T) { } // Setup oracle nodes - oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, streams, func(c *chainlink.Config) { + oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, func(c *chainlink.Config) { c.Mercury.Transmitter.Protocol = ptr(config.MercuryTransmitterProtocolGRPC) }) @@ -847,9 +947,21 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi "binanceFundingIntervalHours": "8", "deribitFundingRate": "5432.2345", "deribitFundingTime": "1630000000", - "deribitFundingIntervalHours": "8" + "deribitFundingIntervalHours": "8", + "ethPrice": "3976.39", + "linkPrice": "23.45" }` + pricePipeline := fmt.Sprintf(` +dp [type=bridge name="%s" requestData="{\\"data\\":{\\"data\\":\\"foo\\"}}"]; +eth_parse [type=jsonparse path="result,ethPrice"]; +eth_decimal [type=multiply times=1 streamID=%d]; +link_parse [type=jsonparse path="result,linkPrice"]; +link_decimal [type=multiply times=1 streamID=%d]; +dp -> eth_parse -> eth_decimal; +dp -> link_parse -> link_decimal; +`, bridgeName, ethStreamID, linkStreamID) + dexBasedAssetPipeline := fmt.Sprintf(` dp [type=bridge name="%s" requestData="{\\"data\\":{\\"data\\":\\"foo\\"}}"]; @@ -917,6 +1029,7 @@ dp -> deribit_funding_interval_hours_parse -> deribit_funding_interval_hours_dec // superBridge returns a JSON with everything you want in it, // stream specs can just pick the individual fields they need createBridge(t, bridgeName, resultJSON, node.App.BridgeORM()) + addStreamSpec(t, node, "pricePipeline", nil, pricePipeline) addStreamSpec(t, node, "dexBasedAssetPipeline", nil, dexBasedAssetPipeline) addStreamSpec(t, node, "rwaPipeline", nil, rwaPipeline) addStreamSpec(t, node, "benchmarkPricePipeline", nil, benchmarkPricePipeline) @@ -937,7 +1050,7 @@ dp -> deribit_funding_interval_hours_parse -> deribit_funding_interval_hours_dec // Set config on configurator digest := setProductionConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, + t, donID, steve, backend, configurator, configuratorAddress, nodes, WithOracles(oracles), WithOffchainConfig(offchainConfig), ) // NOTE: Wait for one of each type of report @@ -979,8 +1092,8 @@ dp -> deribit_funding_interval_hours_parse -> deribit_funding_interval_hours_dec assert.GreaterOrEqual(t, int(reportElems["observationsTimestamp"].(uint32)), int(testStartTimeStamp.Unix())) // Zero fees since both eth/link stream specs are missing, don't // care about billing for purposes of this test - assert.Equal(t, "0", reportElems["nativeFee"].(*big.Int).String()) - assert.Equal(t, "0", reportElems["linkFee"].(*big.Int).String()) + assert.Equal(t, "25148438659186", reportElems["nativeFee"].(*big.Int).String()) + assert.Equal(t, "4264392324093817", reportElems["linkFee"].(*big.Int).String()) assert.Equal(t, reportElems["observationsTimestamp"].(uint32)+expirationWindow, reportElems["expiresAt"].(uint32)) // Check payload values @@ -1051,12 +1164,253 @@ dp -> deribit_funding_interval_hours_parse -> deribit_funding_interval_hours_dec }) } -func TestIntegration_LLO_stress_test_and_transmit_errors(t *testing.T) { +func TestIntegration_LLO_stress_test_V1(t *testing.T) { + t.Parallel() + + // logLevel: the log level to use for the nodes + // setting a more verbose log level increases cpu usage significantly + // const logLevel = toml.LogLevel(zapcore.DebugLevel) + const logLevel = toml.LogLevel(zapcore.ErrorLevel) + + // NOTE: Tweak these values to increase or decrease the intensity of the + // stress test + // + // nChannels: the total number of channels + // nReports: the number of reports to expect per node + // defaultMinReportInterval: minimum time between report emission (set to 1ns to produce as fast as possible) + + // STRESS TEST PARAMETERS + + // LOW STRESS + const nChannels = 100 + const nReports = 250 + const defaultMinReportInterval = 5 * time.Millisecond + + // HIGHER STRESS + // const nChannels = 2000 + // const nReports = 50_000 + // const defaultMinReportInterval = 1 * time.Nanosecond + + // PROTOCOL CONFIGURATION + ocrConfigOpts := []OCRConfigOption{ + WithOffchainConfig(datastreamsllo.OffchainConfig{ + ProtocolVersion: 1, + DefaultMinReportIntervalNanoseconds: uint64(defaultMinReportInterval), + }), + func(cfg *OCRConfig) { + // cfg.DeltaRound = 0 // Go as fast as possible + cfg.DeltaRound = 50 * time.Millisecond + cfg.DeltaGrace = 5 * time.Millisecond + cfg.DeltaCertifiedCommitRequest = 5 * time.Millisecond + }, + } + + // SETUP + + clientCSAKeys := make([]csakey.KeyV2, nNodes) + clientPubKeys := make([]ed25519.PublicKey, nNodes) + + const salt = 302 + + for i := 0; i < nNodes; i++ { + k := big.NewInt(int64(salt + i)) + key := csakey.MustNewV2XXXTestingOnly(k) + clientCSAKeys[i] = key + clientPubKeys[i] = key.PublicKey + } + + steve, backend, configurator, configuratorAddress, _, _, _, _, configStore, configStoreAddress, _, _, _, _ := setupBlockchain(t) + fromBlock := 1 + + // Setup bootstrap + bootstrapCSAKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(salt - 1)) + bootstrapNodePort := freeport.GetOne(t) + appBootstrap, bootstrapPeerID, _, bootstrapKb, _ := setupNode(t, bootstrapNodePort, "bootstrap_llo", backend, bootstrapCSAKey, func(c *chainlink.Config) { + c.Log.Level = ptr(logLevel) + }) + bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb} + + t.Run("produces reports properly", func(t *testing.T) { + packets := make(chan *packet, nReports*nNodes) + serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(salt - 2)) + serverPubKey := serverKey.PublicKey + srv := NewMercuryServer(t, ed25519.PrivateKey(serverKey.Raw()), packets) + + serverURL := startMercuryServer(t, srv, clientPubKeys) + + donID := uint32(888333) + streams := []Stream{ethStream} + streamMap := make(map[uint32]Stream) + for _, strm := range streams { + streamMap[strm.id] = strm + } + + // Setup oracle nodes + oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, func(c *chainlink.Config) { + c.Mercury.Transmitter.Protocol = ptr(config.MercuryTransmitterProtocolGRPC) + c.Log.Level = ptr(logLevel) + }) + + chainID := testutils.SimulatedChainID + relayType := "evm" + relayConfig := fmt.Sprintf(` +chainID = "%s" +fromBlock = %d +lloDonID = %d +lloConfigMode = "bluegreen" +`, chainID, fromBlock, donID) + addBootstrapJob(t, bootstrapNode, configuratorAddress, "job-3", relayType, relayConfig) + + // Channel definitions + channelDefinitions := llotypes.ChannelDefinitions{} + for i := uint32(0); i < nChannels; i++ { + channelDefinitions[i] = llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatJSON, + Streams: []llotypes.Stream{ + { + StreamID: ethStreamID, + Aggregator: llotypes.AggregatorMedian, + }, + }, + } + } + url, sha := newChannelDefinitionsServer(t, channelDefinitions) + + // Set channel definitions + _, err := configStore.SetChannelDefinitions(steve, donID, url, sha) + require.NoError(t, err) + backend.Commit() + + // one working and one broken transmission server + pluginConfig := fmt.Sprintf(`servers = { "%s" = "%x" } +donID = %d +channelDefinitionsContractAddress = "0x%x" +channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock) + for i, node := range nodes { + addLLOJob( + t, + node, + configuratorAddress, + bootstrapPeerID, + bootstrapNodePort, + clientPubKeys[i], + "feed-1", + pluginConfig, + relayType, + relayConfig, + ) + addMemoStreamSpecs(t, node, streams) + } + + // Set config on configurator + opts := []OCRConfigOption{WithOracles(oracles)} + opts = append(opts, ocrConfigOpts...) + blueDigest := setProductionConfig( + t, donID, steve, backend, configurator, configuratorAddress, nodes, opts..., + ) + + // NOTE: Wait for nReports reports per node + // transmitter addr => count of reports + cnts := map[string]int{} + // transmitter addr => channel ID => reports + m := map[string]map[uint32][]datastreamsllo.Report{} + stopOnce := sync.Once{} + + for pckt := range packets { + pr, ok := peer.FromContext(pckt.ctx) + require.True(t, ok) + addr := pr.Addr + req := pckt.req + + assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) + _, _, r, _, err := (datastreamsllo.JSONReportCodec{}).UnpackDecode(req.Payload) + require.NoError(t, err) + + cm, exists := m[addr.String()] + if !exists { + cm = make(map[uint32][]datastreamsllo.Report) + m[addr.String()] = cm + } + cm[r.ChannelID] = append(cm[r.ChannelID], r) + + cnts[addr.String()]++ + finished := 0 + for _, cnt := range cnts { + if cnt >= nReports { + finished++ + } + } + if finished >= nNodes { + stopOnce.Do(func() { + // Stop all nodes, close the channel + // This helps transmissions have a chance to complete (but + // doesn't ensure it; libocr cancels the transmit context + // immediately on stop signal) + // Loop will exit once all packets are consumed + for _, node := range nodes { + require.NoError(t, node.App.Stop()) + } + close(packets) + }) + } + } + + // Transmissions can occur out of order when we go very fast, so sort by seqNr + for _, cm := range m { + for _, rs := range cm { + sort.Slice(rs, func(i, j int) bool { + return rs[i].SeqNr < rs[j].SeqNr + }) + } + } + + // Check reports + for addr, cm := range m { + spacings := []uint64{} + for _, rs := range cm { + var prevObsTsNanos uint64 + for i, r := range rs { + assert.Equal(t, blueDigest, r.ConfigDigest) + assert.False(t, r.Specimen) + assert.Len(t, r.Values, 1) + assert.Equal(t, "2976.39", r.Values[0].(*datastreamsllo.Decimal).String()) + + if i > 0 { + if rs[i-1].SeqNr+1 != r.SeqNr { + // t.Logf("gap in SeqNr at index %d; %d!=%d: len(rs)=%d", i, rs[i-1].SeqNr, r.SeqNr, len(rs)) + // We actually expect a transmission every round; if there's a gap in seqNr it means that the transmissions were likely cut off due to the app being shut down. We are probably at the end of the usable reports list so just assume completion here. + break + } + + // No gaps + require.Equal(t, prevObsTsNanos, r.ValidAfterNanoseconds, "gap in reports for transmitter %s at index %d; %d!=%d: prevReport=%s, thisReport=%s", addr, i, prevObsTsNanos, r.ValidAfterNanoseconds, mustMarshalJSON(rs[i-1]), mustMarshalJSON(r)) + // Timestamps are sane + require.GreaterOrEqual(t, r.ObservationTimestampNanoseconds, r.ValidAfterNanoseconds, "observation timestamp is before valid after timestamp for transmitter %s at index %d: report=%s", addr, i, mustMarshalJSON(r)) + // Reports are separated by at least the minimum interval + require.GreaterOrEqual(t, r.ObservationTimestampNanoseconds-uint64(defaultMinReportInterval), prevObsTsNanos, "reports are too close together for transmitter %s at index %d: prevReport=%s, thisReport=%s; expected at least %d nanoseconds of distance", addr, i, mustMarshalJSON(rs[i-1]), mustMarshalJSON(r), defaultMinReportInterval) + + spacings = append(spacings, r.ObservationTimestampNanoseconds-prevObsTsNanos) + } + prevObsTsNanos = r.ObservationTimestampNanoseconds + } + } + avgSpacing := uint64(0) + for _, spacing := range spacings { + avgSpacing += spacing + } + avgSpacing /= uint64(len(spacings)) + t.Logf("transmitter %s: average spacing between reports: %d nanoseconds (%f seconds)", addr, avgSpacing, float64(avgSpacing)/1e9) + } + }) +} + +func TestIntegration_LLO_transmit_errors(t *testing.T) { t.Parallel() // logLevel: the log level to use for the nodes // setting a more verbose log level increases cpu usage significantly const logLevel = toml.LogLevel(zapcore.ErrorLevel) + // const logLevel = toml.LogLevel(zapcore.ErrorLevel) // NOTE: Tweak these values to increase or decrease the intensity of the // stress test @@ -1075,6 +1429,13 @@ func TestIntegration_LLO_stress_test_and_transmit_errors(t *testing.T) { // const maxQueueSize = 4_000 // const nReports = 10_000 + // PROTOCOL CONFIGURATION + // TODO: test both + offchainConfig := datastreamsllo.OffchainConfig{ + ProtocolVersion: 1, + DefaultMinReportIntervalNanoseconds: uint64(50 * time.Millisecond), + } + clientCSAKeys := make([]csakey.KeyV2, nNodes) clientPubKeys := make([]ed25519.PublicKey, nNodes) @@ -1093,7 +1454,9 @@ func TestIntegration_LLO_stress_test_and_transmit_errors(t *testing.T) { // Setup bootstrap bootstrapCSAKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(salt - 1)) bootstrapNodePort := freeport.GetOne(t) - appBootstrap, bootstrapPeerID, _, bootstrapKb, _ := setupNode(t, bootstrapNodePort, "bootstrap_llo", backend, bootstrapCSAKey, nil) + appBootstrap, bootstrapPeerID, _, bootstrapKb, _ := setupNode(t, bootstrapNodePort, "bootstrap_llo", backend, bootstrapCSAKey, func(c *chainlink.Config) { + c.Log.Level = ptr(logLevel) + }) bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb} t.Run("transmit queue does not grow unbounded", func(t *testing.T) { @@ -1112,7 +1475,7 @@ func TestIntegration_LLO_stress_test_and_transmit_errors(t *testing.T) { } // Setup oracle nodes - oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, streams, func(c *chainlink.Config) { + oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, func(c *chainlink.Config) { c.Mercury.Transmitter.Protocol = ptr(config.MercuryTransmitterProtocolGRPC) c.Mercury.Transmitter.TransmitQueueMaxSize = ptr(uint32(maxQueueSize)) // Test queue overflow c.Log.Level = ptr(logLevel) @@ -1129,7 +1492,6 @@ lloConfigMode = "bluegreen" addBootstrapJob(t, bootstrapNode, configuratorAddress, "job-3", relayType, relayConfig) // Channel definitions - // 2,000 channels should produce 2,000 reports per second channelDefinitions := llotypes.ChannelDefinitions{} for i := uint32(0); i < nChannels; i++ { channelDefinitions[i] = llotypes.ChannelDefinition{ @@ -1161,10 +1523,10 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, serverPubKey { // Set config on configurator blueDigest = setProductionConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, + t, donID, steve, backend, configurator, configuratorAddress, nodes, WithOracles(oracles), WithOffchainConfig(offchainConfig), ) - // NOTE: Wait for 40,000 reports (should take about 5 seconds) - 2,000 reports per second * 4 transmitters * 5 seconds + // NOTE: Wait for nReports reports // count of packets received keyed by transmitter IP m := map[string]int{} for pckt := range packets { @@ -1218,6 +1580,26 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, serverPubKey func TestIntegration_LLO_blue_green_lifecycle(t *testing.T) { t.Parallel() + offchainConfigs := []datastreamsllo.OffchainConfig{ + { + ProtocolVersion: 0, + DefaultMinReportIntervalNanoseconds: 0, + }, + { + ProtocolVersion: 1, + DefaultMinReportIntervalNanoseconds: 1, + }, + } + for _, offchainConfig := range offchainConfigs { + t.Run(fmt.Sprintf("offchainConfig=%+v", offchainConfig), func(t *testing.T) { + t.Parallel() + + testIntegrationLLOBlueGreenLifecycle(t, offchainConfig) + }) + } +} + +func testIntegrationLLOBlueGreenLifecycle(t *testing.T, offchainConfig datastreamsllo.OffchainConfig) { clientCSAKeys := make([]csakey.KeyV2, nNodes) clientPubKeys := make([]ed25519.PublicKey, nNodes) @@ -1255,7 +1637,7 @@ func TestIntegration_LLO_blue_green_lifecycle(t *testing.T) { } // Setup oracle nodes - oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, streams, func(c *chainlink.Config) { + oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, func(c *chainlink.Config) { c.Mercury.Transmitter.Protocol = ptr(config.MercuryTransmitterProtocolGRPC) }) @@ -1302,7 +1684,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi { // Set config on configurator blueDigest = setProductionConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, + t, donID, steve, backend, configurator, configuratorAddress, nodes, WithOracles(oracles), WithOffchainConfig(offchainConfig), ) // NOTE: Wait until blue produces a report @@ -1325,7 +1707,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi // setStagingConfig does not affect production { greenDigest = setStagingConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, blueDigest, + t, donID, steve, backend, configurator, configuratorAddress, nodes, WithPredecessorConfigDigest(blueDigest), WithOracles(oracles), WithOffchainConfig(offchainConfig), ) // NOTE: Wait until green produces the first "specimen" report @@ -1375,8 +1757,8 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi return allReports[digest][i].SeqNr < allReports[digest][j].SeqNr }) seenSeqNr := uint64(0) - highestObservationTs := uint32(0) - highestValidAfterSeconds := uint32(0) + highestObsTsNanos := uint64(0) + highestValidAfterNanos := uint64(0) for i := 0; i < len(allReports[digest]); i++ { r := allReports[digest][i] switch digest { @@ -1391,29 +1773,43 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi } if r.SeqNr > seenSeqNr { // skip first one - if highestObservationTs > 0 { + if highestObsTsNanos > 0 { if digest == greenDigest && i == len(allReports[digest])-1 { // NOTE: This actually CHANGES on the staging // handover and can go backwards - the gapless // handover test is handled below break } - assert.Equal(t, highestObservationTs, r.ValidAfterSeconds, "%d: (n-1)ObservationsTimestampSeconds->(n)ValidAfterSeconds should be gapless, got: %d vs %d", i, highestObservationTs, r.ValidAfterSeconds) - assert.Greater(t, r.ObservationTimestampSeconds, highestObservationTs, "%d: overlapping/duplicate report ObservationTimestampSeconds, got: %d vs %d", i, r.ObservationTimestampSeconds, highestObservationTs) - assert.Greater(t, r.ValidAfterSeconds, highestValidAfterSeconds, "%d: overlapping/duplicate report ValidAfterSeconds, got: %d vs %d", i, r.ValidAfterSeconds, highestValidAfterSeconds) - assert.Less(t, r.ValidAfterSeconds, r.ObservationTimestampSeconds) + if offchainConfig.ProtocolVersion == 0 { + // validAfter is always truncated to 1s in v0 + // IMPORTANT: gapless handovers in v0 ONLY supported at 1s resolution!! + assert.Equal(t, highestObsTsNanos/1e9*1e9, r.ValidAfterNanoseconds/1e9*1e9, "%d: (n-1)ObservationsTimestampSeconds->(n)ValidAfterNanoseconds should be gapless to within 1s resolution, got: %d vs %d", i, highestObsTsNanos, r.ValidAfterNanoseconds) + } else { + assert.Equal(t, highestObsTsNanos, r.ValidAfterNanoseconds, "%d: (n-1)ObservationsTimestampSeconds->(n)ValidAfterNanoseconds should be gapless, got: %d vs %d", i, highestObsTsNanos, r.ValidAfterNanoseconds) + } + assert.Greater(t, r.ObservationTimestampNanoseconds, highestObsTsNanos, "%d: overlapping/duplicate report ObservationTimestampNanoseconds, got: %d vs %d", i, r.ObservationTimestampNanoseconds, highestObsTsNanos) + assert.Greater(t, r.ValidAfterNanoseconds, highestValidAfterNanos, "%d: overlapping/duplicate report ValidAfterNanoseconds, got: %d vs %d", i, r.ValidAfterNanoseconds, highestValidAfterNanos) + assert.Less(t, r.ValidAfterNanoseconds, r.ObservationTimestampNanoseconds) } seenSeqNr = r.SeqNr - highestObservationTs = r.ObservationTimestampSeconds - highestValidAfterSeconds = r.ValidAfterSeconds + highestObsTsNanos = r.ObservationTimestampNanoseconds + highestValidAfterNanos = r.ValidAfterNanoseconds } } } // Gapless handover - assert.Less(t, finalBlueReport.ValidAfterSeconds, finalBlueReport.ObservationTimestampSeconds) - assert.Equal(t, finalBlueReport.ObservationTimestampSeconds, initialPromotedGreenReport.ValidAfterSeconds) - assert.Less(t, initialPromotedGreenReport.ValidAfterSeconds, initialPromotedGreenReport.ObservationTimestampSeconds) + assert.Less(t, finalBlueReport.ValidAfterNanoseconds, finalBlueReport.ObservationTimestampNanoseconds) + + if offchainConfig.ProtocolVersion == 0 { + // validAfter is always truncated to 1s in v0 + // IMPORTANT: gapless handovers in v0 ONLY supported at 1s resolution!! + assert.Equal(t, finalBlueReport.ObservationTimestampNanoseconds/1e9*1e9, initialPromotedGreenReport.ValidAfterNanoseconds/1e9*1e9) + } else { + assert.Equal(t, finalBlueReport.ObservationTimestampNanoseconds, initialPromotedGreenReport.ValidAfterNanoseconds) + } + + assert.Less(t, initialPromotedGreenReport.ValidAfterNanoseconds, initialPromotedGreenReport.ObservationTimestampNanoseconds) } // retired instance does not produce reports { @@ -1438,7 +1834,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi // setStagingConfig replaces 'retired' instance with new config and starts producing specimen reports again { blueDigest = setStagingConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles, greenDigest, + t, donID, steve, backend, configurator, configuratorAddress, nodes, WithPredecessorConfigDigest(greenDigest), WithOracles(oracles), WithOffchainConfig(offchainConfig), ) // NOTE: Wait until blue produces the first "specimen" report @@ -1482,9 +1878,15 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi finalGreenReport := allReports[greenDigest][len(allReports[greenDigest])-1] // Gapless handover - assert.Less(t, finalGreenReport.ValidAfterSeconds, finalGreenReport.ObservationTimestampSeconds) - assert.Equal(t, finalGreenReport.ObservationTimestampSeconds, initialPromotedBlueReport.ValidAfterSeconds) - assert.Less(t, initialPromotedBlueReport.ValidAfterSeconds, initialPromotedBlueReport.ObservationTimestampSeconds) + assert.Less(t, finalGreenReport.ValidAfterNanoseconds, finalGreenReport.ObservationTimestampNanoseconds) + if offchainConfig.ProtocolVersion == 0 { + // validAfter is always truncated to 1s in v0 + // IMPORTANT: gapless handovers in v0 ONLY supported at 1s resolution!! + assert.Equal(t, finalGreenReport.ObservationTimestampNanoseconds/1e9*1e9, initialPromotedBlueReport.ValidAfterNanoseconds/1e9*1e9, 1_000_000_000, "ObservationTimestampSeconds->ValidAfterNanoseconds should be gapless to within 1s resolution, got: %d vs %d", finalGreenReport.ObservationTimestampNanoseconds, initialPromotedBlueReport.ValidAfterNanoseconds) + } else { + assert.Equal(t, finalGreenReport.ObservationTimestampNanoseconds, initialPromotedBlueReport.ValidAfterNanoseconds, "ObservationTimestampSeconds->ValidAfterNanoseconds should be gapless, got: %d vs %d", finalGreenReport.ObservationTimestampNanoseconds, initialPromotedBlueReport.ValidAfterNanoseconds) + } + assert.Less(t, initialPromotedBlueReport.ValidAfterNanoseconds, initialPromotedBlueReport.ObservationTimestampNanoseconds) } // adding a new channel definition is picked up on the fly { @@ -1537,7 +1939,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi }) } -func setupNodes(t *testing.T, nNodes int, backend evmtypes.Backend, clientCSAKeys []csakey.KeyV2, streams []Stream, f func(*chainlink.Config)) (oracles []confighelper.OracleIdentityExtra, nodes []Node) { +func setupNodes(t *testing.T, nNodes int, backend evmtypes.Backend, clientCSAKeys []csakey.KeyV2, f func(*chainlink.Config)) (oracles []confighelper.OracleIdentityExtra, nodes []Node) { ports := freeport.GetN(t, nNodes) for i := 0; i < nNodes; i++ { app, peerID, transmitter, kb, observedLogs := setupNode(t, ports[i], fmt.Sprintf("oracle_streams_%d", i), backend, clientCSAKeys[i], f) @@ -1585,3 +1987,11 @@ func mustNewType(t string) abi.Type { } return result } + +func mustMarshalJSON(v interface{}) string { + b, err := json.Marshal(v) + if err != nil { + panic(err) + } + return string(b) +} diff --git a/go.mod b/go.mod index 6ffb117780e..457c3466e81 100644 --- a/go.mod +++ b/go.mod @@ -389,6 +389,8 @@ replace ( // replicating the replace directive on cosmos SDK github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + github.com/smartcontractkit/chainlink-data-streams => /Users/sam/code/smartcontractkit/chainlink-data-streams + github.com/sourcegraph/sourcegraph/lib => github.com/sourcegraph/sourcegraph-public-snapshot/lib v0.0.0-20240822153003-c864f15af264 ) diff --git a/go.sum b/go.sum index 6860ae1bdcb..5e5dd74d190 100644 --- a/go.sum +++ b/go.sum @@ -1022,8 +1022,6 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= From 9eafee925c4043cc5675557f6e598ce2f7585f30 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Thu, 27 Feb 2025 07:09:27 -0500 Subject: [PATCH 2/2] Bump chainlink-common --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- deployment/go.mod | 2 +- deployment/go.sum | 4 ++-- go.mod | 4 +--- go.sum | 2 ++ 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 36d15adffbd..b0744839cf7 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -33,7 +33,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.22 github.com/smartcontractkit/libocr v0.0.0-20250220133800-f3b940c4f298 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index c1a570007be..36342541ba0 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1095,8 +1095,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= diff --git a/deployment/go.mod b/deployment/go.mod index bbaf6492c22..55ff39869d7 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -353,7 +353,7 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e // indirect + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.5.0 // indirect diff --git a/deployment/go.sum b/deployment/go.sum index d826718379e..804308cfedf 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1142,8 +1142,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= diff --git a/go.mod b/go.mod index 457c3466e81..d9d3e660992 100644 --- a/go.mod +++ b/go.mod @@ -79,7 +79,7 @@ require ( github.com/smartcontractkit/chainlink-ccip v0.0.0-20250226083129-e596590f48f7 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef github.com/smartcontractkit/chainlink-feeds v0.1.1 github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb @@ -389,8 +389,6 @@ replace ( // replicating the replace directive on cosmos SDK github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - github.com/smartcontractkit/chainlink-data-streams => /Users/sam/code/smartcontractkit/chainlink-data-streams - github.com/sourcegraph/sourcegraph/lib => github.com/sourcegraph/sourcegraph-public-snapshot/lib v0.0.0-20240822153003-c864f15af264 ) diff --git a/go.sum b/go.sum index 5e5dd74d190..51dc146c6b3 100644 --- a/go.sum +++ b/go.sum @@ -1022,6 +1022,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 6d7280b4740..152cf138eac 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -440,7 +440,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e // indirect + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index cd175605d3a..c9aa349193a 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1436,8 +1436,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 9dbc79c8cbb..cd77ea8fb7e 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -430,7 +430,7 @@ require ( github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e // indirect + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 37213c2e46c..3ec77c2eb0a 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1421,8 +1421,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index 458d8fd2054..c81b76b1038 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -340,7 +340,7 @@ require ( github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20250226083129-e596590f48f7 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e // indirect + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index 74810cf9921..442fc52b632 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1130,8 +1130,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 4d1c9863524..1e278013ecd 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -344,7 +344,7 @@ require ( github.com/smartcontractkit/chainlink-ccip v0.0.0-20250226083129-e596590f48f7 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e // indirect + github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index c5bddf1be3a..4d037ae5610 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1130,8 +1130,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e h1:QBG+Wn5rHAi4gjnBAq6x6CZj/GjWAahFjj81VhQEu6U= -github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250224190032-809e4b8cf29e/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef h1:gYz/R78AEeVi2JV5HLJIq5DiJYdOUhWzM90VAK/uJB0= +github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250227004503-d9ddd95ab0ef/go.mod h1:2yUpKW1/jFxpozO/Zkh3fKDzI0jthXoEcU2xuDq+vlo= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a h1:zllQ6pOs1T0oiDNK3EHr7ABy1zHp+2oxoCuVE/hK+uI=