Skip to content

Commit d77db32

Browse files
Parallel Comp (LLO) cleanup and minor optimizations (smartcontractkit#15368)
* Parallel Comp (LLO) cleanup and minor optimizations - Add ChannelDefinitionCacheFactory tests - Cleanup TODOs/FIXMEs - Add comments/docs - Include Don ID in LLO extra hash - Optimize log poller calls * Fix test * Fix linter issue re: append * Update core/services/llo/evm/report_codec_premium_legacy.go Co-authored-by: msuchacz-cll <170782674+msuchacz-cll@users.noreply.github.com> --------- Co-authored-by: msuchacz-cll <170782674+msuchacz-cll@users.noreply.github.com>
1 parent b79da55 commit d77db32

25 files changed

+304
-233
lines changed

core/services/llo/channel_definition_cache_factory.go

-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ type channelDefinitionCacheFactory struct {
4141
mu sync.Mutex
4242
}
4343

44-
// TODO: Test this
45-
// MERC-3653
4644
func (f *channelDefinitionCacheFactory) NewCache(cfg lloconfig.PluginConfig) (llotypes.ChannelDefinitionCache, error) {
4745
if cfg.ChannelDefinitions != "" {
4846
return NewStaticChannelDefinitionCache(f.lggr, cfg.ChannelDefinitions)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package llo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/smartcontractkit/chainlink/v2/core/logger"
10+
lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config"
11+
)
12+
13+
func Test_ChannelDefinitionCacheFactory(t *testing.T) {
14+
lggr := logger.TestLogger(t)
15+
cdcFactory := NewChannelDefinitionCacheFactory(lggr, nil, nil, nil)
16+
17+
t.Run("NewCache", func(t *testing.T) {
18+
t.Run("when ChannelDefinitions is present, returns static cache", func(t *testing.T) {
19+
_, err := cdcFactory.NewCache(lloconfig.PluginConfig{ChannelDefinitions: "..."})
20+
require.EqualError(t, err, "failed to unmarshal static channel definitions: invalid character '.' looking for beginning of value")
21+
22+
cdc, err := cdcFactory.NewCache(lloconfig.PluginConfig{ChannelDefinitions: "{}"})
23+
require.NoError(t, err)
24+
require.IsType(t, &staticCDC{}, cdc)
25+
})
26+
t.Run("when ChannelDefinitions is not present, returns dynamic cache", func(t *testing.T) {
27+
cdc, err := cdcFactory.NewCache(lloconfig.PluginConfig{
28+
ChannelDefinitionsContractAddress: common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
29+
DonID: 1,
30+
})
31+
require.NoError(t, err)
32+
require.IsType(t, &channelDefinitionCache{}, cdc)
33+
34+
// returns error if you try to do it again with the same addr/donID
35+
_, err = cdcFactory.NewCache(lloconfig.PluginConfig{
36+
ChannelDefinitionsContractAddress: common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
37+
DonID: 1,
38+
})
39+
require.EqualError(t, err, "cache already exists for contract address 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa and don ID 1")
40+
41+
// is fine if you do it again with different addr
42+
cdc, err = cdcFactory.NewCache(lloconfig.PluginConfig{
43+
ChannelDefinitionsContractAddress: common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
44+
DonID: 1,
45+
})
46+
require.NoError(t, err)
47+
require.IsType(t, &channelDefinitionCache{}, cdc)
48+
49+
// is fine if you do it again with different don ID
50+
cdc, err = cdcFactory.NewCache(lloconfig.PluginConfig{
51+
ChannelDefinitionsContractAddress: common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
52+
DonID: 2,
53+
})
54+
require.NoError(t, err)
55+
require.IsType(t, &channelDefinitionCache{}, cdc)
56+
})
57+
})
58+
}

core/services/llo/codecs.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import (
99
)
1010

1111
// NOTE: All supported codecs must be specified here
12-
func NewReportCodecs(lggr logger.Logger) map[llotypes.ReportFormat]llo.ReportCodec {
12+
func NewReportCodecs(lggr logger.Logger, donID uint32) map[llotypes.ReportFormat]llo.ReportCodec {
1313
codecs := make(map[llotypes.ReportFormat]llo.ReportCodec)
1414

1515
codecs[llotypes.ReportFormatJSON] = llo.JSONReportCodec{}
16-
codecs[llotypes.ReportFormatEVMPremiumLegacy] = evm.NewReportCodecPremiumLegacy(lggr)
16+
codecs[llotypes.ReportFormatEVMPremiumLegacy] = evm.NewReportCodecPremiumLegacy(lggr, donID)
1717

1818
return codecs
1919
}

core/services/llo/codecs_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
func Test_NewReportCodecs(t *testing.T) {
13-
c := NewReportCodecs(logger.TestLogger(t))
13+
c := NewReportCodecs(logger.TestLogger(t), 1)
1414

1515
assert.Contains(t, c, llotypes.ReportFormatJSON, "expected JSON to be supported")
1616
assert.Contains(t, c, llotypes.ReportFormatEVMPremiumLegacy, "expected EVMPremiumLegacy to be supported")

core/services/llo/data_source.go

-3
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,6 @@ func ExtractStreamValue(trrs pipeline.TaskRunResults) (llo.StreamValue, error) {
193193
// by the pipeline executor
194194
finaltrrs := trrs.Terminals()
195195

196-
// TODO: Special handling for missing native/link streams?
197-
// https://smartcontract-it.atlassian.net/browse/MERC-5949
198-
199196
// HACK: Right now we rely on the number of outputs to determine whether
200197
// its a Decimal or a Quote.
201198
// This isn't very robust or future-proof but is sufficient to support v0.3

core/services/llo/delegate.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) {
9898
} else {
9999
codecLggr = corelogger.NullLogger
100100
}
101-
reportCodecs := NewReportCodecs(codecLggr)
101+
reportCodecs := NewReportCodecs(codecLggr, cfg.DonID)
102102

103103
var t TelemeterService
104104
if cfg.CaptureEATelemetry {
@@ -134,8 +134,9 @@ func (d *delegate) Start(ctx context.Context) error {
134134
lggr = logger.With(lggr, "instanceType", "Green")
135135
}
136136
ocrLogger := logger.NewOCRWrapper(NewSuppressedLogger(lggr, d.cfg.ReportingPluginConfig.VerboseLogging), d.cfg.TraceLogging, func(msg string) {
137-
// TODO: do we actually need to DB-persist errors?
138-
// MERC-3524
137+
// NOTE: Some OCR loggers include a DB-persist here
138+
// We do not DB persist errors in LLO, since they could be quite voluminous and ought to be present in logs anyway.
139+
// This is a performance optimization
139140
})
140141

141142
oracle, err := ocr2plus.NewOracle(ocr2plus.OCR3OracleArgs[llotypes.ReportInfo]{

core/services/llo/evm/report_codec.go

-54
This file was deleted.

core/services/llo/evm/report_codec_premium_legacy.go

+26-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"math/big"
89

910
"github.com/ethereum/go-ethereum/common"
1011
"github.com/shopspring/decimal"
@@ -30,10 +31,11 @@ var (
3031

3132
type ReportCodecPremiumLegacy struct {
3233
logger.Logger
34+
donID uint32
3335
}
3436

35-
func NewReportCodecPremiumLegacy(lggr logger.Logger) ReportCodecPremiumLegacy {
36-
return ReportCodecPremiumLegacy{logger.Sugared(lggr).Named("ReportCodecPremiumLegacy")}
37+
func NewReportCodecPremiumLegacy(lggr logger.Logger, donID uint32) ReportCodecPremiumLegacy {
38+
return ReportCodecPremiumLegacy{logger.Sugared(lggr).Named("ReportCodecPremiumLegacy"), donID}
3739
}
3840

3941
type ReportFormatEVMPremiumLegacyOpts struct {
@@ -119,7 +121,7 @@ func (r ReportCodecPremiumLegacy) Pack(digest types.ConfigDigest, seqNr uint64,
119121
ss = append(ss, s)
120122
vs[i] = v
121123
}
122-
reportCtx := LegacyReportContext(digest, seqNr)
124+
reportCtx := LegacyReportContext(digest, seqNr, r.donID)
123125
rawReportCtx := evmutil.RawReportContext(reportCtx)
124126

125127
payload, err := mercury.PayloadTypes.Pack(rawReportCtx, []byte(report), rs, ss, vs)
@@ -181,9 +183,25 @@ func extractPrice(price llo.StreamValue) (decimal.Decimal, error) {
181183
}
182184
}
183185

184-
// TODO: Consider embedding the DON ID here?
185-
// MERC-3524
186-
var LLOExtraHash = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001")
186+
const PluginVersion uint32 = 1 // the legacy mercury plugin is 0
187+
188+
// Uniquely identifies this as LLO plugin, rather than the legacy plugin (which
189+
// uses all zeroes).
190+
//
191+
// This is quite a hack but serves the purpose of uniquely identifying
192+
// dons/plugin versions to the mercury server without having to modify any
193+
// existing tooling or breaking backwards compatibility. It should be safe
194+
// since the DonID is encoded into the config digest anyway so report context
195+
// is already dependent on it, and all LLO jobs in the same don are expected to
196+
// have the same don ID set.
197+
//
198+
// Packs donID+pluginVersion as (uint32, uint32), for example donID=2,
199+
// PluginVersion=1 Yields:
200+
// 0x0000000000000000000000000000000000000000000000000000000200000001
201+
func LLOExtraHash(donID uint32) common.Hash {
202+
combined := uint64(donID)<<32 | uint64(PluginVersion)
203+
return common.BigToHash(new(big.Int).SetUint64(combined))
204+
}
187205

188206
func SeqNrToEpochAndRound(seqNr uint64) (epoch uint32, round uint8) {
189207
// Simulate 256 rounds/epoch
@@ -192,14 +210,14 @@ func SeqNrToEpochAndRound(seqNr uint64) (epoch uint32, round uint8) {
192210
return
193211
}
194212

195-
func LegacyReportContext(cd ocr2types.ConfigDigest, seqNr uint64) ocr2types.ReportContext {
213+
func LegacyReportContext(cd ocr2types.ConfigDigest, seqNr uint64, donID uint32) ocr2types.ReportContext {
196214
epoch, round := SeqNrToEpochAndRound(seqNr)
197215
return ocr2types.ReportContext{
198216
ReportTimestamp: ocr2types.ReportTimestamp{
199217
ConfigDigest: cd,
200218
Epoch: uint32(epoch),
201219
Round: uint8(round),
202220
},
203-
ExtraHash: LLOExtraHash, // ExtraHash is always zero for mercury, we use LLOExtraHash here to differentiate from the legacy plugin
221+
ExtraHash: LLOExtraHash(donID), // ExtraHash is always zero for mercury, we use LLOExtraHash here to differentiate from the legacy plugin
204222
}
205223
}

core/services/llo/evm/report_codec_premium_legacy_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func newValidPremiumLegacyReport() llo.Report {
3333
}
3434

3535
func Test_ReportCodecPremiumLegacy(t *testing.T) {
36-
rc := ReportCodecPremiumLegacy{logger.TestLogger(t)}
36+
rc := ReportCodecPremiumLegacy{logger.TestLogger(t), 2}
3737

3838
feedID := [32]uint8{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
3939
cd := llotypes.ChannelDefinition{Opts: llotypes.ChannelOpts(fmt.Sprintf(`{"baseUSDFee":"10.50","expirationWindow":60,"feedId":"0x%x","multiplier":10}`, feedID))}
@@ -225,3 +225,9 @@ func Test_ExtractReportValues(t *testing.T) {
225225
assert.Equal(t, &llo.Quote{Bid: decimal.NewFromInt(37), Benchmark: decimal.NewFromInt(38), Ask: decimal.NewFromInt(39)}, quote)
226226
})
227227
}
228+
229+
func Test_LLOExtraHash(t *testing.T) {
230+
donID := uint32(8)
231+
extraHash := LLOExtraHash(donID)
232+
assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000800000001", extraHash.String())
233+
}

core/services/llo/keyring.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ type Key interface {
3333
}
3434

3535
type onchainKeyring struct {
36-
lggr logger.Logger
37-
keys map[llotypes.ReportFormat]Key
36+
lggr logger.Logger
37+
keys map[llotypes.ReportFormat]Key
38+
donID uint32
3839
}
3940

40-
func NewOnchainKeyring(lggr logger.Logger, keys map[llotypes.ReportFormat]Key) LLOOnchainKeyring {
41+
func NewOnchainKeyring(lggr logger.Logger, keys map[llotypes.ReportFormat]Key, donID uint32) LLOOnchainKeyring {
4142
return &onchainKeyring{
42-
lggr.Named("OnchainKeyring"), keys,
43+
lggr.Named("OnchainKeyring"), keys, donID,
4344
}
4445
}
4546

@@ -83,7 +84,7 @@ func (okr *onchainKeyring) Sign(digest types.ConfigDigest, seqNr uint64, r ocr3t
8384
rf := r.Info.ReportFormat
8485
if key, exists := okr.keys[rf]; exists {
8586
// NOTE: Must use legacy Sign method for compatibility with v0.3 report verification
86-
rc := evm.LegacyReportContext(digest, seqNr)
87+
rc := evm.LegacyReportContext(digest, seqNr, okr.donID)
8788
return key.Sign(rc, r.Report)
8889
}
8990
default:
@@ -101,7 +102,7 @@ func (okr *onchainKeyring) Verify(key types.OnchainPublicKey, digest types.Confi
101102
rf := r.Info.ReportFormat
102103
if verifier, exists := okr.keys[rf]; exists {
103104
// NOTE: Must use legacy Verify method for compatibility with v0.3 report verification
104-
rc := evm.LegacyReportContext(digest, seqNr)
105+
rc := evm.LegacyReportContext(digest, seqNr, okr.donID)
105106
return verifier.Verify(key, rc, r.Report, signature)
106107
}
107108
default:

core/services/llo/keyring_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func Test_Keyring(t *testing.T) {
6868
llotypes.ReportFormatJSON: &mockKey{format: llotypes.ReportFormatJSON, maxSignatureLen: 2, sig: []byte("sig-2")},
6969
}
7070

71-
kr := NewOnchainKeyring(lggr, ks)
71+
kr := NewOnchainKeyring(lggr, ks, 2)
7272

7373
cases := []struct {
7474
format llotypes.ReportFormat

core/services/llo/mercurytransmitter/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func newServer(lggr logger.Logger, verboseLogging bool, cfg QueueConfig, client
122122
NewTransmitQueue(lggr, serverURL, int(cfg.TransmitQueueMaxSize()), pm),
123123
make(chan [32]byte, int(cfg.TransmitQueueMaxSize())),
124124
serverURL,
125-
evm.NewReportCodecPremiumLegacy(codecLggr),
125+
evm.NewReportCodecPremiumLegacy(codecLggr, pm.DonID()),
126126
llo.JSONReportCodec{},
127127
promTransmitSuccessCount.WithLabelValues(donIDStr, serverURL),
128128
promTransmitDuplicateCount.WithLabelValues(donIDStr, serverURL),

0 commit comments

Comments
 (0)