Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions pkg/chainaccessor/default_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"time"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/types"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
Expand Down Expand Up @@ -378,6 +379,17 @@ func (l *DefaultAccessor) MsgsBetweenSeqNums(
}

msg.Message.Header.OnRamp = onRampAddress

// Populate TxHash from Sequence item
if len(item.TxHash) > 0 {
msg.Message.Header.TxHash = hexutil.Encode(item.TxHash)
} else {
// TxHash is empty - log warning and leave it empty
lggr.Warnw("transaction hash is empty",
"cursor", item.Cursor,
"seqNum", msg.Message.Header.SequenceNumber)
}

msgs = append(msgs, msg.Message)
}

Expand Down
204 changes: 204 additions & 0 deletions pkg/chainaccessor/default_accessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package chainaccessor
import (
"context"
"fmt"
"math/big"
"reflect"
"testing"

Expand Down Expand Up @@ -381,3 +382,206 @@ func TestDefaultAccessor_GetSourceChainsConfig(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, []byte(expectedRouterB), cfgB.Router)
}

// Helper function to create a valid SendRequestedEvent for testing
func createValidSendRequestedEvent(seqNum cciptypes.SeqNum) *SendRequestedEvent {
return &SendRequestedEvent{
DestChainSelector: chainB,
SequenceNumber: seqNum,
Message: cciptypes.Message{
Header: cciptypes.RampMessageHeader{
SourceChainSelector: chainA,
DestChainSelector: chainB,
SequenceNumber: seqNum,
MessageID: cciptypes.Bytes32{byte(seqNum)},
},
Sender: cciptypes.UnknownAddress("sender"),
Receiver: cciptypes.UnknownAddress("receiver"),
FeeToken: cciptypes.UnknownAddress("feeToken"),
FeeTokenAmount: cciptypes.NewBigInt(big.NewInt(100)),
},
}
}

func TestMsgsBetweenSeqNums(t *testing.T) {
tests := []struct {
name string
seqNumRange cciptypes.SeqNumRange
destChainSelector cciptypes.ChainSelector
sequences []types.Sequence
expectedError bool
expectedMsgCount int
validateTxHash func(t *testing.T, msgs []cciptypes.Message)
}{
{
name: "TxHash populated from item.TxHash",
seqNumRange: cciptypes.NewSeqNumRange(1, 3),
destChainSelector: chainB,
sequences: []types.Sequence{
{
Cursor: "100-1-0xabc123",
TxHash: []byte{0xab, 0xcd, 0xef, 0x12, 0x34, 0x56},
Data: createValidSendRequestedEvent(1),
},
{
Cursor: "100-2-0xdef456",
TxHash: []byte{0xde, 0xad, 0xbe, 0xef, 0x78, 0x90},
Data: createValidSendRequestedEvent(2),
},
},
expectedError: false,
expectedMsgCount: 2,
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
require.Len(t, msgs, 2)
// TxHash should be populated from item.TxHash with 0x prefix
assert.Equal(t, "0xabcdef123456", msgs[0].Header.TxHash)
assert.Equal(t, "0xdeadbeef7890", msgs[1].Header.TxHash)
},
},
{
name: "Mixed: some with item.TxHash, some without",
seqNumRange: cciptypes.NewSeqNumRange(1, 3),
destChainSelector: chainB,
sequences: []types.Sequence{
{
Cursor: "100-1-0xcursor111",
TxHash: []byte{0x11, 0x22, 0x33}, // Has TxHash
Data: createValidSendRequestedEvent(1),
},
{
Cursor: "100-2-0xcursor222",
TxHash: nil, // No TxHash
Data: createValidSendRequestedEvent(2),
},
{
Cursor: "100-3-0xcursor333",
TxHash: []byte{0x44, 0x55, 0x66}, // Has TxHash
Data: createValidSendRequestedEvent(3),
},
},
expectedError: false,
expectedMsgCount: 3,
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
require.Len(t, msgs, 3)
assert.Equal(t, "0x112233", msgs[0].Header.TxHash) // From item.TxHash
assert.Equal(t, "", msgs[1].Header.TxHash) // Empty - no TxHash provided
assert.Equal(t, "0x445566", msgs[2].Header.TxHash) // From item.TxHash
},
},
{
name: "Empty TxHash when item.TxHash is not provided",
seqNumRange: cciptypes.NewSeqNumRange(1, 1),
destChainSelector: chainB,
sequences: []types.Sequence{
{
Cursor: "100-1-0xabc123",
TxHash: nil, // No TxHash
Data: createValidSendRequestedEvent(1),
},
},
expectedError: false,
expectedMsgCount: 1,
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
require.Len(t, msgs, 1)
// TxHash should be empty when item.TxHash is not provided
assert.Equal(t, "", msgs[0].Header.TxHash)
},
},
{
name: "Filter out invalid sequence number",
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
destChainSelector: chainB,
sequences: []types.Sequence{
{
Cursor: "100-1-0xabc123",
TxHash: []byte{0xab, 0xcd},
Data: createValidSendRequestedEvent(1),
},
{
Cursor: "100-2-0xdef456",
TxHash: []byte{0xde, 0xef},
Data: createValidSendRequestedEvent(10), // Out of range
},
},
expectedError: false,
expectedMsgCount: 1,
validateTxHash: func(t *testing.T, msgs []cciptypes.Message) {
require.Len(t, msgs, 1)
assert.Equal(t, "0xabcd", msgs[0].Header.TxHash)
},
},
{
name: "Wrong data type in sequence",
seqNumRange: cciptypes.NewSeqNumRange(1, 2),
destChainSelector: chainB,
sequences: []types.Sequence{
{
Cursor: "100-1-0xabc123",
TxHash: []byte{0xab, 0xcd},
Data: "invalid data type", // Not SendRequestedEvent
},
},
expectedError: true,
expectedMsgCount: 0,
validateTxHash: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup mocks
mockReader := reader_mocks.NewMockExtended(t)
mockWriter := writer_mocks.NewMockContractWriter(t)
codec := internal.NewMockAddressCodecHex(t)

accessor := &DefaultAccessor{
lggr: logger.Test(t),
chainSelector: chainA,
contractReader: mockReader,
contractWriter: mockWriter,
addrCodec: codec,
}

// Setup mock expectations
mockReader.On("ExtendedQueryKey", mock.Anything, mock.Anything,
mock.Anything, mock.Anything, mock.Anything).
Return(tt.sequences, nil).Once()

// Setup GetBindings mock for GetContractAddress call
// The address string will be converted to bytes by the codec
onRampAddressStr := "0x1234567890abcdef"
onRampAddress := cciptypes.UnknownAddress{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}
bindings := []contractreader.ExtendedBoundContract{
{
Binding: types.BoundContract{
Name: consts.ContractNameOnRamp,
Address: onRampAddressStr,
},
},
}
mockReader.On("GetBindings", consts.ContractNameOnRamp).
Return(bindings).Once()

// Execute test
msgs, err := accessor.MsgsBetweenSeqNums(context.Background(), tt.destChainSelector, tt.seqNumRange)

// Verify results
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Len(t, msgs, tt.expectedMsgCount)
if tt.validateTxHash != nil {
tt.validateTxHash(t, msgs)
}
// Verify OnRamp is always set
for _, msg := range msgs {
assert.Equal(t, onRampAddress, msg.Header.OnRamp)
}
}

// Verify all mock expectations were met
mockReader.AssertExpectations(t)
})
}
}
Loading