Skip to content

Commit 9563ad1

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

File tree

2 files changed

+247
-0
lines changed

2 files changed

+247
-0
lines changed

pkg/chainaccessor/default_accessor.go

Lines changed: 18 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,23 @@ func (l *DefaultAccessor) MsgsBetweenSeqNums(
378379
}
379380

380381
msg.Message.Header.OnRamp = onRampAddress
382+
383+
// Populate TxHash from Sequence item
384+
// Prefer item.TxHash if available, otherwise extract from cursor
385+
if len(item.TxHash) > 0 {
386+
msg.Message.Header.TxHash = hexutil.Encode(item.TxHash)
387+
} else {
388+
// Fallback to extracting from cursor if TxHash is empty
389+
if txHash, err := contractreader.ExtractTxHash(item.Cursor); err != nil {
390+
lggr.Warnw("failed to extract transaction hash from cursor",
391+
"err", err,
392+
"cursor", item.Cursor,
393+
"seqNum", msg.Message.Header.SequenceNumber)
394+
} else {
395+
msg.Message.Header.TxHash = txHash
396+
}
397+
}
398+
381399
msgs = append(msgs, msg.Message)
382400
}
383401

pkg/chainaccessor/default_accessor_test.go

Lines changed: 229 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,231 @@ 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: "TxHash extracted from cursor when item.TxHash is empty",
443+
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
444+
destChainSelector: chainB,
445+
sequences: []types.Sequence{
446+
{
447+
Cursor: "100-1-0xfallback111222333444555666777888999000",
448+
TxHash: nil, // Empty TxHash
449+
Data: createValidSendRequestedEvent(1),
450+
},
451+
{
452+
Cursor: "100-2-0xfallback222333444555666777888999000aaa",
453+
TxHash: []byte{}, // Empty TxHash
454+
Data: createValidSendRequestedEvent(2),
455+
},
456+
},
457+
expectedError: false,
458+
expectedMsgCount: 2,
459+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
460+
require.Len(t, msgs, 2)
461+
// TxHash should be extracted from cursor
462+
assert.Equal(t, "0xfallback111222333444555666777888999000", msgs[0].Header.TxHash)
463+
assert.Equal(t, "0xfallback222333444555666777888999000aaa", msgs[1].Header.TxHash)
464+
},
465+
},
466+
{
467+
name: "Mixed: some with item.TxHash, some from cursor",
468+
seqNumRange: cciptypes.NewSeqNumRange(1, 3),
469+
destChainSelector: chainB,
470+
sequences: []types.Sequence{
471+
{
472+
Cursor: "100-1-0xcursor111",
473+
TxHash: []byte{0x11, 0x22, 0x33}, // Has TxHash
474+
Data: createValidSendRequestedEvent(1),
475+
},
476+
{
477+
Cursor: "100-2-0xcursor222",
478+
TxHash: nil, // No TxHash
479+
Data: createValidSendRequestedEvent(2),
480+
},
481+
{
482+
Cursor: "100-3-0xcursor333",
483+
TxHash: []byte{0x44, 0x55, 0x66}, // Has TxHash
484+
Data: createValidSendRequestedEvent(3),
485+
},
486+
},
487+
expectedError: false,
488+
expectedMsgCount: 3,
489+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
490+
require.Len(t, msgs, 3)
491+
assert.Equal(t, "0x112233", msgs[0].Header.TxHash) // From item.TxHash
492+
assert.Equal(t, "0xcursor222", msgs[1].Header.TxHash) // From cursor
493+
assert.Equal(t, "0x445566", msgs[2].Header.TxHash) // From item.TxHash
494+
},
495+
},
496+
{
497+
name: "Invalid cursor format - TxHash extraction fails gracefully",
498+
seqNumRange: cciptypes.NewSeqNumRange(1, 1),
499+
destChainSelector: chainB,
500+
sequences: []types.Sequence{
501+
{
502+
Cursor: "invalid-cursor", // Invalid format
503+
TxHash: nil, // No TxHash
504+
Data: createValidSendRequestedEvent(1),
505+
},
506+
},
507+
expectedError: false,
508+
expectedMsgCount: 1,
509+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
510+
require.Len(t, msgs, 1)
511+
// TxHash should be empty when extraction fails
512+
assert.Equal(t, "", msgs[0].Header.TxHash)
513+
},
514+
},
515+
{
516+
name: "Filter out invalid sequence number",
517+
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
518+
destChainSelector: chainB,
519+
sequences: []types.Sequence{
520+
{
521+
Cursor: "100-1-0xabc123",
522+
TxHash: []byte{0xab, 0xcd},
523+
Data: createValidSendRequestedEvent(1),
524+
},
525+
{
526+
Cursor: "100-2-0xdef456",
527+
TxHash: []byte{0xde, 0xef},
528+
Data: createValidSendRequestedEvent(10), // Out of range
529+
},
530+
},
531+
expectedError: false,
532+
expectedMsgCount: 1,
533+
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
534+
require.Len(t, msgs, 1)
535+
assert.Equal(t, "0xabcd", msgs[0].Header.TxHash)
536+
},
537+
},
538+
{
539+
name: "Wrong data type in sequence",
540+
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
541+
destChainSelector: chainB,
542+
sequences: []types.Sequence{
543+
{
544+
Cursor: "100-1-0xabc123",
545+
TxHash: []byte{0xab, 0xcd},
546+
Data: "invalid data type", // Not SendRequestedEvent
547+
},
548+
},
549+
expectedError: true,
550+
expectedMsgCount: 0,
551+
validateTxHash: nil,
552+
},
553+
}
554+
555+
for _, tt := range tests {
556+
t.Run(tt.name, func(t *testing.T) {
557+
// Setup mocks
558+
mockReader := reader_mocks.NewMockExtended(t)
559+
mockWriter := writer_mocks.NewMockContractWriter(t)
560+
codec := internal.NewMockAddressCodecHex(t)
561+
562+
accessor := &DefaultAccessor{
563+
lggr: logger.Test(t),
564+
chainSelector: chainA,
565+
contractReader: mockReader,
566+
contractWriter: mockWriter,
567+
addrCodec: codec,
568+
}
569+
570+
// Setup mock expectations
571+
mockReader.On("ExtendedQueryKey", mock.Anything, mock.Anything,
572+
mock.Anything, mock.Anything, mock.Anything).
573+
Return(tt.sequences, nil).Once()
574+
575+
// Setup GetBindings mock for GetContractAddress call
576+
// The address string will be converted to bytes by the codec
577+
onRampAddressStr := "0x1234567890abcdef"
578+
onRampAddress := cciptypes.UnknownAddress{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}
579+
bindings := []contractreader.ExtendedBoundContract{
580+
{
581+
Binding: types.BoundContract{
582+
Name: consts.ContractNameOnRamp,
583+
Address: onRampAddressStr,
584+
},
585+
},
586+
}
587+
mockReader.On("GetBindings", consts.ContractNameOnRamp).
588+
Return(bindings).Once()
589+
590+
// Execute test
591+
msgs, err := accessor.MsgsBetweenSeqNums(context.Background(), tt.destChainSelector, tt.seqNumRange)
592+
593+
// Verify results
594+
if tt.expectedError {
595+
assert.Error(t, err)
596+
} else {
597+
assert.NoError(t, err)
598+
assert.Len(t, msgs, tt.expectedMsgCount)
599+
if tt.validateTxHash != nil {
600+
tt.validateTxHash(t, msgs)
601+
}
602+
// Verify OnRamp is always set
603+
for _, msg := range msgs {
604+
assert.Equal(t, onRampAddress, msg.Header.OnRamp)
605+
}
606+
}
607+
608+
// Verify all mock expectations were met
609+
mockReader.AssertExpectations(t)
610+
})
611+
}
612+
}

0 commit comments

Comments
 (0)