Skip to content

Commit 99a6fc1

Browse files
authored
Have MsgsBetweenSeqNums populate TxHash (#1337)
* Have MsgsBetweenSeqNums populate TxHash * Fix lint issue * Add chain accessor wrapper * Use existing mock chain accessor * Commit plugin uses ChainAccessorWrapper * Move PopulateTxHashEnabled check to CCIPChainReader * Add unit tests
1 parent 8772649 commit 99a6fc1

File tree

9 files changed

+399
-15
lines changed

9 files changed

+399
-15
lines changed

commit/factory.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ func (p *PluginFactory) NewReportingPlugin(ctx context.Context, config ocr3types
187187
p.ocrConfig.Config.ChainSelector,
188188
p.ocrConfig.Config.OfframpAddress,
189189
p.addrCodec,
190+
offchainConfig.PopulateTxHashEnabled,
190191
)
191192
if err != nil {
192193
return nil, ocr3types.ReportingPluginInfo{}, fmt.Errorf("failed to create CCIP chain reader: %w", err)

execute/factory.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func (p PluginFactory) NewReportingPlugin(
151151
p.ocrConfig.Config.ChainSelector,
152152
p.ocrConfig.Config.OfframpAddress,
153153
p.addrCodec,
154+
offchainConfig.PopulateTxHashEnabled,
154155
)
155156
if err != nil {
156157
return nil, ocr3types.ReportingPluginInfo{}, fmt.Errorf("failed to create ccip reader: %w", err)

pkg/chainaccessor/default_accessor.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strconv"
99
"time"
1010

11+
"github.com/ethereum/go-ethereum/common/hexutil"
1112
"github.com/smartcontractkit/chainlink-common/pkg/logger"
1213
"github.com/smartcontractkit/chainlink-common/pkg/types"
1314
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
@@ -378,6 +379,12 @@ func (l *DefaultAccessor) MsgsBetweenSeqNums(
378379
}
379380

380381
msg.Message.Header.OnRamp = onRampAddress
382+
383+
// Populate TxHash from Sequence item
384+
if len(item.TxHash) > 0 {
385+
msg.Message.Header.TxHash = hexutil.Encode(item.TxHash)
386+
}
387+
381388
msgs = append(msgs, msg.Message)
382389
}
383390

pkg/chainaccessor/default_accessor_test.go

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package chainaccessor
33
import (
44
"context"
55
"fmt"
6+
"math/big"
67
"reflect"
78
"testing"
89

@@ -381,3 +382,206 @@ func TestDefaultAccessor_GetSourceChainsConfig(t *testing.T) {
381382
require.NoError(t, err)
382383
assert.Equal(t, []byte(expectedRouterB), cfgB.Router)
383384
}
385+
386+
// Helper function to create a valid SendRequestedEvent for testing
387+
func createValidSendRequestedEvent(seqNum cciptypes.SeqNum) *SendRequestedEvent {
388+
return &SendRequestedEvent{
389+
DestChainSelector: chainB,
390+
SequenceNumber: seqNum,
391+
Message: cciptypes.Message{
392+
Header: cciptypes.RampMessageHeader{
393+
SourceChainSelector: chainA,
394+
DestChainSelector: chainB,
395+
SequenceNumber: seqNum,
396+
MessageID: cciptypes.Bytes32{byte(seqNum)},
397+
},
398+
Sender: cciptypes.UnknownAddress("sender"),
399+
Receiver: cciptypes.UnknownAddress("receiver"),
400+
FeeToken: cciptypes.UnknownAddress("feeToken"),
401+
FeeTokenAmount: cciptypes.NewBigInt(big.NewInt(100)),
402+
},
403+
}
404+
}
405+
406+
func TestMsgsBetweenSeqNums(t *testing.T) {
407+
tests := []struct {
408+
name string
409+
seqNumRange cciptypes.SeqNumRange
410+
destChainSelector cciptypes.ChainSelector
411+
sequences []types.Sequence
412+
expectedError bool
413+
expectedMsgCount int
414+
validateTxHash func(t *testing.T, msgs []cciptypes.Message)
415+
}{
416+
{
417+
name: "TxHash populated from item.TxHash",
418+
seqNumRange: cciptypes.NewSeqNumRange(1, 3),
419+
destChainSelector: chainB,
420+
sequences: []types.Sequence{
421+
{
422+
Cursor: "100-1-0xabc123",
423+
TxHash: []byte{0xab, 0xcd, 0xef, 0x12, 0x34, 0x56},
424+
Data: createValidSendRequestedEvent(1),
425+
},
426+
{
427+
Cursor: "100-2-0xdef456",
428+
TxHash: []byte{0xde, 0xad, 0xbe, 0xef, 0x78, 0x90},
429+
Data: createValidSendRequestedEvent(2),
430+
},
431+
},
432+
expectedError: false,
433+
expectedMsgCount: 2,
434+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
435+
require.Len(t, msgs, 2)
436+
// TxHash should be populated from item.TxHash with 0x prefix
437+
assert.Equal(t, "0xabcdef123456", msgs[0].Header.TxHash)
438+
assert.Equal(t, "0xdeadbeef7890", msgs[1].Header.TxHash)
439+
},
440+
},
441+
{
442+
name: "Mixed: some with item.TxHash, some without",
443+
seqNumRange: cciptypes.NewSeqNumRange(1, 3),
444+
destChainSelector: chainB,
445+
sequences: []types.Sequence{
446+
{
447+
Cursor: "100-1-0xcursor111",
448+
TxHash: []byte{0x11, 0x22, 0x33}, // Has TxHash
449+
Data: createValidSendRequestedEvent(1),
450+
},
451+
{
452+
Cursor: "100-2-0xcursor222",
453+
TxHash: nil, // No TxHash
454+
Data: createValidSendRequestedEvent(2),
455+
},
456+
{
457+
Cursor: "100-3-0xcursor333",
458+
TxHash: []byte{0x44, 0x55, 0x66}, // Has TxHash
459+
Data: createValidSendRequestedEvent(3),
460+
},
461+
},
462+
expectedError: false,
463+
expectedMsgCount: 3,
464+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
465+
require.Len(t, msgs, 3)
466+
assert.Equal(t, "0x112233", msgs[0].Header.TxHash) // From item.TxHash
467+
assert.Equal(t, "", msgs[1].Header.TxHash) // Empty - no TxHash provided
468+
assert.Equal(t, "0x445566", msgs[2].Header.TxHash) // From item.TxHash
469+
},
470+
},
471+
{
472+
name: "Empty TxHash when item.TxHash is not provided",
473+
seqNumRange: cciptypes.NewSeqNumRange(1, 1),
474+
destChainSelector: chainB,
475+
sequences: []types.Sequence{
476+
{
477+
Cursor: "100-1-0xabc123",
478+
TxHash: nil, // No TxHash
479+
Data: createValidSendRequestedEvent(1),
480+
},
481+
},
482+
expectedError: false,
483+
expectedMsgCount: 1,
484+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
485+
require.Len(t, msgs, 1)
486+
// TxHash should be empty when item.TxHash is not provided
487+
assert.Equal(t, "", msgs[0].Header.TxHash)
488+
},
489+
},
490+
{
491+
name: "Filter out invalid sequence number",
492+
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
493+
destChainSelector: chainB,
494+
sequences: []types.Sequence{
495+
{
496+
Cursor: "100-1-0xabc123",
497+
TxHash: []byte{0xab, 0xcd},
498+
Data: createValidSendRequestedEvent(1),
499+
},
500+
{
501+
Cursor: "100-2-0xdef456",
502+
TxHash: []byte{0xde, 0xef},
503+
Data: createValidSendRequestedEvent(10), // Out of range
504+
},
505+
},
506+
expectedError: false,
507+
expectedMsgCount: 1,
508+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
509+
require.Len(t, msgs, 1)
510+
assert.Equal(t, "0xabcd", msgs[0].Header.TxHash)
511+
},
512+
},
513+
{
514+
name: "Wrong data type in sequence",
515+
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
516+
destChainSelector: chainB,
517+
sequences: []types.Sequence{
518+
{
519+
Cursor: "100-1-0xabc123",
520+
TxHash: []byte{0xab, 0xcd},
521+
Data: "invalid data type", // Not SendRequestedEvent
522+
},
523+
},
524+
expectedError: true,
525+
expectedMsgCount: 0,
526+
validateTxHash: nil,
527+
},
528+
}
529+
530+
for _, tt := range tests {
531+
t.Run(tt.name, func(t *testing.T) {
532+
// Setup mocks
533+
mockReader := reader_mocks.NewMockExtended(t)
534+
mockWriter := writer_mocks.NewMockContractWriter(t)
535+
codec := internal.NewMockAddressCodecHex(t)
536+
537+
accessor := &DefaultAccessor{
538+
lggr: logger.Test(t),
539+
chainSelector: chainA,
540+
contractReader: mockReader,
541+
contractWriter: mockWriter,
542+
addrCodec: codec,
543+
}
544+
545+
// Setup mock expectations
546+
mockReader.On("ExtendedQueryKey", mock.Anything, mock.Anything,
547+
mock.Anything, mock.Anything, mock.Anything).
548+
Return(tt.sequences, nil).Once()
549+
550+
// Setup GetBindings mock for GetContractAddress call
551+
// The address string will be converted to bytes by the codec
552+
onRampAddressStr := "0x1234567890abcdef"
553+
onRampAddress := cciptypes.UnknownAddress{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}
554+
bindings := []contractreader.ExtendedBoundContract{
555+
{
556+
Binding: types.BoundContract{
557+
Name: consts.ContractNameOnRamp,
558+
Address: onRampAddressStr,
559+
},
560+
},
561+
}
562+
mockReader.On("GetBindings", consts.ContractNameOnRamp).
563+
Return(bindings).Once()
564+
565+
// Execute test
566+
msgs, err := accessor.MsgsBetweenSeqNums(context.Background(), tt.destChainSelector, tt.seqNumRange)
567+
568+
// Verify results
569+
if tt.expectedError {
570+
assert.Error(t, err)
571+
} else {
572+
assert.NoError(t, err)
573+
assert.Len(t, msgs, tt.expectedMsgCount)
574+
if tt.validateTxHash != nil {
575+
tt.validateTxHash(t, msgs)
576+
}
577+
// Verify OnRamp is always set
578+
for _, msg := range msgs {
579+
assert.Equal(t, onRampAddress, msg.Header.OnRamp)
580+
}
581+
}
582+
583+
// Verify all mock expectations were met
584+
mockReader.AssertExpectations(t)
585+
})
586+
}
587+
}

pkg/reader/ccip.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ type ccipChainReader struct {
3939
contractReaders map[cciptypes.ChainSelector]contractreader.Extended
4040
contractWriters map[cciptypes.ChainSelector]types.ContractWriter
4141

42-
destChain cciptypes.ChainSelector
43-
offrampAddress string
44-
configPoller ConfigPoller
45-
addrCodec cciptypes.AddressCodec
46-
donAddressBook *addressbook.Book
42+
destChain cciptypes.ChainSelector
43+
offrampAddress string
44+
configPoller ConfigPoller
45+
addrCodec cciptypes.AddressCodec
46+
donAddressBook *addressbook.Book
47+
populateTxHashEnabled bool
4748
}
4849

4950
func newCCIPChainReaderInternal(
@@ -55,6 +56,7 @@ func newCCIPChainReaderInternal(
5556
destChain cciptypes.ChainSelector,
5657
offrampAddress []byte,
5758
addrCodec cciptypes.AddressCodec,
59+
populateTxHashEnabled bool,
5860
) (*ccipChainReader, error) {
5961
return newCCIPChainReaderWithConfigPollerInternal(
6062
ctx,
@@ -66,6 +68,7 @@ func newCCIPChainReaderInternal(
6668
offrampAddress,
6769
addrCodec,
6870
nil,
71+
populateTxHashEnabled,
6972
)
7073
}
7174

@@ -79,6 +82,7 @@ func newCCIPChainReaderWithConfigPollerInternal(
7982
offrampAddress []byte,
8083
addrCodec cciptypes.AddressCodec,
8184
configPoller ConfigPoller,
85+
populateTxHashEnabled bool,
8286
) (*ccipChainReader, error) {
8387
var crs = make(map[cciptypes.ChainSelector]contractreader.Extended)
8488
for chainSelector, cr := range contractReaders {
@@ -92,14 +96,15 @@ func newCCIPChainReaderWithConfigPollerInternal(
9296
}
9397

9498
reader := &ccipChainReader{
95-
lggr: lggr,
96-
contractReaders: crs,
97-
contractWriters: contractWriters,
98-
accessors: chainAccessors,
99-
destChain: destChain,
100-
offrampAddress: offrampAddrStr,
101-
addrCodec: addrCodec,
102-
donAddressBook: addressbook.NewBook(),
99+
lggr: lggr,
100+
contractReaders: crs,
101+
contractWriters: contractWriters,
102+
accessors: chainAccessors,
103+
destChain: destChain,
104+
offrampAddress: offrampAddrStr,
105+
addrCodec: addrCodec,
106+
donAddressBook: addressbook.NewBook(),
107+
populateTxHashEnabled: populateTxHashEnabled,
103108
}
104109

105110
// Initialize cache with readers
@@ -269,6 +274,13 @@ func (r *ccipChainReader) MsgsBetweenSeqNums(
269274
return nil, fmt.Errorf("onRamp address has changed from %s to %s", onRampAddressBeforeQuery, onRampAddressAfterQuery)
270275
}
271276

277+
// Clear TxHash if population is disabled
278+
if !r.populateTxHashEnabled {
279+
for i := range messages {
280+
messages[i].Header.TxHash = ""
281+
}
282+
}
283+
272284
return messages, nil
273285
}
274286

pkg/reader/ccip_interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func NewCCIPChainReader(
8989
destChain cciptypes.ChainSelector,
9090
offrampAddress []byte,
9191
addrCodec cciptypes.AddressCodec,
92+
populateTxHashEnabled bool,
9293
) (CCIPReader, error) {
9394
reader, err := newCCIPChainReaderInternal(
9495
ctx,
@@ -99,6 +100,7 @@ func NewCCIPChainReader(
99100
destChain,
100101
offrampAddress,
101102
addrCodec,
103+
populateTxHashEnabled,
102104
)
103105
if err != nil {
104106
return nil, err
@@ -131,6 +133,7 @@ func NewCCIPReaderWithExtendedContractReaders(
131133
destChain,
132134
offrampAddress,
133135
addrCodec,
136+
false, // populateTxHashEnabled - default to false for deprecated test function
134137
)
135138
if err != nil {
136139
// Panic here since right now this is only called from tests in core

0 commit comments

Comments
 (0)