Skip to content

Commit ab672bc

Browse files
committed
Have MsgsBetweenSeqNums populate TxHash
1 parent 98d6277 commit ab672bc

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed

pkg/chainaccessor/default_accessor.go

Lines changed: 12 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,17 @@ 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+
} else {
387+
// TxHash is empty - log warning and leave it empty
388+
lggr.Warnw("transaction hash is empty",
389+
"cursor", item.Cursor,
390+
"seqNum", msg.Message.Header.SequenceNumber)
391+
}
392+
381393
msgs = append(msgs, msg.Message)
382394
}
383395

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+
}

0 commit comments

Comments
 (0)