Skip to content

Commit 35b45ae

Browse files
committed
Merge branch 'main' into metrics-improvements
2 parents a68f0e2 + 99c1fc5 commit 35b45ae

File tree

10 files changed

+684
-232
lines changed

10 files changed

+684
-232
lines changed

pkg/capabilities/capabilities.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ type ExecutableAndTriggerCapability interface {
292292
ExecutableCapability
293293
}
294294

295+
type DONWithNodes struct {
296+
DON DON
297+
Nodes []Node
298+
}
299+
295300
// DON represents a network of connected nodes.
296301
//
297302
// For an example of an empty DON check, see the following link:

pkg/capabilities/consensus/ocr3/datafeeds/securemint_aggregator.go

Lines changed: 79 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package datafeeds
22

33
import (
44
"crypto/sha256"
5-
"encoding/binary"
5+
"encoding/hex"
66
"encoding/json"
77
"errors"
88
"fmt"
99
"math/big"
1010
"strconv"
11+
"strings"
1112

1213
chainselectors "github.com/smartcontractkit/chain-selectors"
1314
ocrcommon "github.com/smartcontractkit/libocr/commontypes"
@@ -21,10 +22,6 @@ import (
2122
"github.com/smartcontractkit/chainlink-protos/cre/go/values"
2223
)
2324

24-
var (
25-
ErrNoMatchingChainSelector = errors.New("no matching chain selector found")
26-
)
27-
2825
type SolanaEncoderKey = string
2926

3027
const (
@@ -84,6 +81,7 @@ type SolanaConfig struct {
8481
type SecureMintAggregatorConfig struct {
8582
// TargetChainSelector is the chain selector to look for
8683
TargetChainSelector chainSelector `mapstructure:"targetChainSelector"`
84+
DataID [16]byte `mapstructure:"dataID"`
8785
Solana SolanaConfig `mapstructure:"solana"`
8886
}
8987

@@ -101,25 +99,20 @@ func (c SecureMintAggregatorConfig) ToMap() (*values.Map, error) {
10199
var _ types.Aggregator = (*SecureMintAggregator)(nil)
102100

103101
type SecureMintAggregator struct {
104-
config SecureMintAggregatorConfig
105-
registry FormatterFactory
102+
config SecureMintAggregatorConfig
103+
formatters *formatterFactory
106104
}
107105

108-
type ChainReportFormatter interface {
109-
PackReport(lggr logger.Logger, report *secureMintReport) (*values.Map, error)
106+
type chainReportFormatter interface {
107+
packReport(lggr logger.Logger, report *secureMintReport) (*values.Map, error)
110108
}
111109

112-
type EVMReportFormatter struct {
113-
TargetChainSelector uint64
110+
type evmReportFormatter struct {
111+
targetChainSelector chainSelector
112+
dataID [16]byte
114113
}
115114

116-
func (f *EVMReportFormatter) PackReport(lggr logger.Logger, report *secureMintReport) (*values.Map, error) {
117-
// Convert chain selector to bytes for data ID
118-
// Secure Mint dataID: 0x04 + chain selector as bytes + right padded with 0s
119-
var chainSelectorAsDataID [16]byte
120-
chainSelectorAsDataID[0] = 0x04
121-
binary.BigEndian.PutUint64(chainSelectorAsDataID[1:], uint64(f.TargetChainSelector))
122-
115+
func (f *evmReportFormatter) packReport(lggr logger.Logger, report *secureMintReport) (*values.Map, error) {
123116
smReportAsAnswer, err := packSecureMintReportIntoUint224ForEVM(report.Mintable, report.Block)
124117
if err != nil {
125118
return nil, fmt.Errorf("failed to pack secure mint report for evm into uint224: %w", err)
@@ -131,9 +124,9 @@ func (f *EVMReportFormatter) PackReport(lggr logger.Logger, report *secureMintRe
131124
// abi: "(bytes16 dataId, uint32 timestamp, uint224 answer)[] Reports"
132125
toWrap := []any{
133126
map[EVMEncoderKey]any{
134-
DataIDOutputFieldName: chainSelectorAsDataID,
127+
DataIDOutputFieldName: f.dataID,
135128
AnswerOutputFieldName: smReportAsAnswer,
136-
TimestampOutputFieldName: int64(report.Block),
129+
TimestampOutputFieldName: uint32(report.SeqNr),
137130
},
138131
}
139132

@@ -147,23 +140,17 @@ func (f *EVMReportFormatter) PackReport(lggr logger.Logger, report *secureMintRe
147140
return wrappedReport, nil
148141
}
149142

150-
func NewEVMReportFormatter(chainSelector uint64, config SecureMintAggregatorConfig) (ChainReportFormatter, error) {
151-
return &EVMReportFormatter{TargetChainSelector: chainSelector}, nil
143+
func newEVMReportFormatter(chainSelector chainSelector, config SecureMintAggregatorConfig) chainReportFormatter {
144+
return &evmReportFormatter{targetChainSelector: chainSelector, dataID: config.DataID}
152145
}
153146

154-
type SolanaReportFormatter struct {
155-
TargetChainSelector uint64
156-
OnReportAccounts solana.AccountMetaSlice
147+
type solanaReportFormatter struct {
148+
targetChainSelector chainSelector
149+
dataID [16]byte
150+
onReportAccounts solana.AccountMetaSlice
157151
}
158152

159-
func (f *SolanaReportFormatter) PackReport(lggr logger.Logger, report *secureMintReport) (*values.Map, error) {
160-
// TEMPORARY DATA ID
161-
// Convert chain selector to bytes for data ID
162-
// Secure Mint dataID: 0x04 + chain selector as bytes + right padded with 0s
163-
var chainSelectorAsDataID [16]byte
164-
chainSelectorAsDataID[0] = 0x04
165-
binary.BigEndian.PutUint64(chainSelectorAsDataID[1:], uint64(f.TargetChainSelector))
166-
153+
func (f *solanaReportFormatter) packReport(lggr logger.Logger, report *secureMintReport) (*values.Map, error) {
167154
// pack answer
168155
smReportAsAnswer, err := packSecureMintReportIntoU128ForSolana(report.Mintable, report.Block)
169156
if err != nil {
@@ -173,21 +160,21 @@ func (f *SolanaReportFormatter) PackReport(lggr logger.Logger, report *secureMin
173160

174161
// hash account contexts
175162
var accounts = make([]byte, 0)
176-
for _, acc := range f.OnReportAccounts {
163+
for _, acc := range f.onReportAccounts {
177164
accounts = append(accounts, acc.PublicKey[:]...)
178165
}
179166
accountContextHash := sha256.Sum256(accounts)
180167
lggr.Debugw("calculated account context hash", "accountContextHash", accountContextHash)
181168

182-
if report.Block > (1<<32 - 1) { // timestamp must fit in u32 in solana
183-
return nil, fmt.Errorf("timestamp exceeds u32 bounds: %v", report.Block)
169+
if report.SeqNr > (1<<32 - 1) { // timestamp must fit in u32 in solana
170+
return nil, fmt.Errorf("timestamp exceeds u32 bounds: %v", report.SeqNr)
184171
}
185172

186173
toWrap := []any{
187174
map[SolanaEncoderKey]any{
188-
SolTimestampOutputFieldName: uint32(report.Block), // TODO: Verify with Michael/Geert timestamp should be block number?
175+
SolTimestampOutputFieldName: uint32(report.SeqNr),
189176
SolAnswerOutputFieldName: smReportAsAnswer,
190-
SolDataIDOutputFieldName: chainSelectorAsDataID,
177+
SolDataIDOutputFieldName: f.dataID,
191178
},
192179
}
193180

@@ -203,47 +190,46 @@ func (f *SolanaReportFormatter) PackReport(lggr logger.Logger, report *secureMin
203190
return wrappedReport, nil
204191
}
205192

206-
func NewSolanaReportFormatter(chainSelector uint64, config SecureMintAggregatorConfig) (ChainReportFormatter, error) {
207-
return &SolanaReportFormatter{TargetChainSelector: chainSelector, OnReportAccounts: config.Solana.AccountContext}, nil
193+
func newSolanaReportFormatter(chainSelector chainSelector, config SecureMintAggregatorConfig) chainReportFormatter {
194+
return &solanaReportFormatter{targetChainSelector: chainSelector, onReportAccounts: config.Solana.AccountContext, dataID: config.DataID}
208195
}
209196

210-
type Builder func(chainSelector uint64, config SecureMintAggregatorConfig) (ChainReportFormatter, error)
211-
212-
type FormatterFactory interface {
213-
Register(chainSelector uint64, builder Builder)
214-
Get(chainSelector uint64, config SecureMintAggregatorConfig) (ChainReportFormatter, error)
215-
}
197+
// chainReportFormatterBuilder is a function that returns a chainReportFormatter for a given chain selector and config
198+
type chainReportFormatterBuilder func(chainSelector chainSelector, config SecureMintAggregatorConfig) chainReportFormatter
216199

217-
type DefaultFormatterFactory struct {
218-
builders map[uint64]Builder
200+
type formatterFactory struct {
201+
builders map[chainSelector]chainReportFormatterBuilder
219202
}
220203

221-
func (r *DefaultFormatterFactory) Register(chainSelector uint64, builder Builder) {
222-
r.builders[chainSelector] = builder
204+
// register registers a new chain report formatter builder for a given chain selector
205+
func (r *formatterFactory) register(chSel chainSelector, builder chainReportFormatterBuilder) {
206+
r.builders[chSel] = builder
223207
}
224208

225-
func (r *DefaultFormatterFactory) Get(chainSelector uint64, config SecureMintAggregatorConfig) (ChainReportFormatter, error) {
226-
b, ok := r.builders[chainSelector]
209+
// get uses a chain report formatter builder to create a chain report formatter
210+
func (r *formatterFactory) get(chSel chainSelector, config SecureMintAggregatorConfig) (chainReportFormatter, error) {
211+
b, ok := r.builders[chSel]
227212
if !ok {
228-
return nil, fmt.Errorf("no formatter registered for chain selector: %d", chainSelector)
213+
return nil, fmt.Errorf("no formatter registered for chain selector: %d", chSel)
229214
}
230215

231-
return b(chainSelector, config)
216+
return b(chSel, config), nil
232217
}
233218

234-
func NewDefaultFormatterFactory() FormatterFactory {
235-
r := DefaultFormatterFactory{
236-
builders: map[uint64]Builder{},
219+
// newFormatterFactory collects all chain report formatters per chain family so that they can be used to pack reports for different chains
220+
func newFormatterFactory() *formatterFactory {
221+
r := formatterFactory{
222+
builders: map[chainSelector]chainReportFormatterBuilder{},
237223
}
238224

239225
// EVM
240226
for _, selector := range chainselectors.EvmChainIdToChainSelector() {
241-
r.Register(selector, NewEVMReportFormatter)
227+
r.register(chainSelector(selector), newEVMReportFormatter)
242228
}
243229

244230
// Solana
245231
for _, selector := range chainselectors.SolanaChainIdToChainSelector() {
246-
r.Register(selector, NewSolanaReportFormatter)
232+
r.register(chainSelector(selector), newSolanaReportFormatter)
247233
}
248234

249235
return &r
@@ -256,11 +242,11 @@ func NewSecureMintAggregator(config values.Map) (types.Aggregator, error) {
256242
if err != nil {
257243
return nil, fmt.Errorf("failed to parse config (%+v): %w", config, err)
258244
}
259-
registry := NewDefaultFormatterFactory()
245+
registry := newFormatterFactory()
260246

261247
return &SecureMintAggregator{
262-
config: parsedConfig,
263-
registry: registry,
248+
config: parsedConfig,
249+
formatters: registry,
264250
}, nil
265251
}
266252

@@ -315,7 +301,6 @@ func (a *SecureMintAggregator) extractAndValidateReports(lggr logger.Logger, obs
315301

316302
for _, observation := range nodeObservations {
317303
lggr.Debugw("processing observation", "observation", observation)
318-
lggr.Debugf("processing observation %+v", observation)
319304

320305
// Extract OCRTriggerEvent from the observation
321306
triggerEvent := &capabilities.OCRTriggerEvent{}
@@ -367,16 +352,15 @@ func (a *SecureMintAggregator) createOutcome(lggr logger.Logger, report *secureM
367352
lggr = logger.Named(lggr, "SecureMintAggregator")
368353
lggr.Debugw("createOutcome called", "report", report)
369354

370-
reportFormatter, err := a.registry.Get(
371-
uint64(a.config.TargetChainSelector),
355+
reportFormatter, err := a.formatters.get(
356+
a.config.TargetChainSelector,
372357
a.config,
373358
)
374-
375359
if err != nil {
376360
return nil, fmt.Errorf("encountered issue fetching report formatter in createOutcome %w", err)
377361
}
378362

379-
wrappedReport, err := reportFormatter.PackReport(lggr, report)
363+
wrappedReport, err := reportFormatter.packReport(lggr, report)
380364

381365
if err != nil {
382366
return nil, fmt.Errorf("encountered issue generating report in createOutcome %w", err)
@@ -402,6 +386,7 @@ func (a *SecureMintAggregator) createOutcome(lggr logger.Logger, report *secureM
402386
func parseSecureMintConfig(config values.Map) (SecureMintAggregatorConfig, error) {
403387
type rawConfig struct {
404388
TargetChainSelector string `mapstructure:"targetChainSelector"`
389+
DataID string `mapstructure:"dataID"`
405390
Solana SolanaConfig `mapstructure:"solana"`
406391
}
407392

@@ -419,8 +404,33 @@ func parseSecureMintConfig(config values.Map) (SecureMintAggregatorConfig, error
419404
return SecureMintAggregatorConfig{}, fmt.Errorf("invalid chain selector: %w", err)
420405
}
421406

407+
if rawCfg.DataID == "" {
408+
return SecureMintAggregatorConfig{}, errors.New("dataID is required")
409+
}
410+
411+
// strip 0x prefix if present
412+
dataID := strings.TrimPrefix(rawCfg.DataID, "0x")
413+
414+
decodedDataID, err := hex.DecodeString(dataID)
415+
if err != nil {
416+
return SecureMintAggregatorConfig{}, fmt.Errorf("invalid dataID: %v %w", dataID, err)
417+
}
418+
419+
if len(decodedDataID) != 16 {
420+
return SecureMintAggregatorConfig{}, fmt.Errorf("dataID must be 16 bytes, got %d", len(decodedDataID))
421+
}
422+
423+
if len(rawCfg.Solana.AccountContext) > 0 {
424+
for _, acc := range rawCfg.Solana.AccountContext {
425+
if acc.PublicKey == [32]byte{} {
426+
return SecureMintAggregatorConfig{}, errors.New("solana account context public key must not be all zeros")
427+
}
428+
}
429+
}
430+
422431
parsedConfig := SecureMintAggregatorConfig{
423432
TargetChainSelector: chainSelector(sel),
433+
DataID: [16]byte(decodedDataID),
424434
Solana: rawCfg.Solana,
425435
}
426436

@@ -434,7 +444,7 @@ var maxMintableEVM = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.
434444
func packSecureMintReportIntoUint224ForEVM(mintable *big.Int, blockNumber uint64) (*big.Int, error) {
435445
// Handle nil mintable
436446
if mintable == nil {
437-
return nil, fmt.Errorf("mintable cannot be nil")
447+
return nil, errors.New("mintable cannot be nil")
438448
}
439449

440450
// Validate that mintable fits in 128 bits
@@ -461,7 +471,7 @@ var maxBlockNumberSolana uint64 = 1<<36 - 1
461471
func packSecureMintReportIntoU128ForSolana(mintable *big.Int, blockNumber uint64) (*big.Int, error) {
462472
// Handle nil mintable
463473
if mintable == nil {
464-
return nil, fmt.Errorf("mintable cannot be nil")
474+
return nil, errors.New("mintable cannot be nil")
465475
}
466476

467477
// Validate that mintable fits in 91 bits

0 commit comments

Comments
 (0)