@@ -2,12 +2,13 @@ package datafeeds
22
33import (
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-
2825type SolanaEncoderKey = string
2926
3027const (
@@ -84,6 +81,7 @@ type SolanaConfig struct {
8481type 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) {
10199var _ types.Aggregator = (* SecureMintAggregator )(nil )
102100
103101type 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 .Block ),
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,7 +160,7 @@ 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 )
@@ -187,7 +174,7 @@ func (f *SolanaReportFormatter) PackReport(lggr logger.Logger, report *secureMin
187174 map [SolanaEncoderKey ]any {
188175 SolTimestampOutputFieldName : uint32 (report .Block ), // TODO: Verify with Michael/Geert timestamp should be block number?
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
402386func 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.
434444func 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
461471func 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