Skip to content

Commit

Permalink
Introducing ExtendedBatchGetLatestValuesGraceful (#546)
Browse files Browse the repository at this point in the history
* Introducing ExtendedBatchGetLatestValuesGraceful

* lint

* merge 2 batch methods
  • Loading branch information
0xnogo authored Feb 4, 2025
1 parent f76a638 commit ce39798
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 47 deletions.
48 changes: 29 additions & 19 deletions mocks/pkg/contractreader/extended.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 26 additions & 6 deletions pkg/contractreader/extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,15 @@ type Extended interface {
request types.BatchGetLatestValuesRequest,
) (types.BatchGetLatestValuesResult, error)

// ExtendedBatchGetLatestValues performs automatic binding from contractNames to bound contracts, and
// contructs a BatchGetLatestValuesRequest with the resolved bindings.
// ExtendedBatchGetLatestValues performs automatic binding from contractNames to bound contracts,
// and constructs a BatchGetLatestValuesRequest with the resolved bindings.
// If graceful is true, contracts with no bindings will be skipped and returned in the skipped slice
// rather than returning an error.
ExtendedBatchGetLatestValues(
ctx context.Context,
request ExtendedBatchGetLatestValuesRequest,
) (types.BatchGetLatestValuesResult, error)
graceful bool,
) (types.BatchGetLatestValuesResult, []string, error)
}

type ExtendedBatchGetLatestValuesRequest map[string]types.ContractBatch
Expand Down Expand Up @@ -235,23 +238,40 @@ func (e *extendedContractReader) BatchGetLatestValues(
func (e *extendedContractReader) ExtendedBatchGetLatestValues(
ctx context.Context,
request ExtendedBatchGetLatestValuesRequest,
) (types.BatchGetLatestValuesResult, error) {
graceful bool,
) (types.BatchGetLatestValuesResult, []string, error) {
// Convert the request from contract names to BoundContracts
convertedRequest := make(types.BatchGetLatestValuesRequest)
var skippedContracts []string

for contractName, batch := range request {
// Get the binding for this contract name
binding, err := e.getOneBinding(contractName)
if err != nil {
return nil, fmt.Errorf("BatchGetLatestValues: failed to get binding for contract %s: %w", contractName, err)
if graceful && errors.Is(err, ErrNoBindings) {
// Track skipped contracts but continue processing
skippedContracts = append(skippedContracts, contractName)
continue
}
return nil, nil, fmt.Errorf("BatchGetLatestValues: failed to get binding for contract %s: %w", contractName, err)
}

// Use the resolved binding for the request
convertedRequest[binding.Binding] = batch
}

// If we have no valid bindings after filtering in graceful mode, return empty result
if graceful && len(convertedRequest) == 0 {
return make(types.BatchGetLatestValuesResult), skippedContracts, nil
}

// Call the underlying BatchGetLatestValues with the converted request
return e.BatchGetLatestValues(ctx, convertedRequest)
results, err := e.BatchGetLatestValues(ctx, convertedRequest)
if err != nil {
return nil, nil, err
}

return results, skippedContracts, nil
}

func (e *extendedContractReader) Bind(ctx context.Context, allBindings []types.BoundContract) error {
Expand Down
154 changes: 134 additions & 20 deletions pkg/contractreader/extended_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,17 @@ func TestGetOneBinding(t *testing.T) {

func TestExtendedBatchGetLatestValues(t *testing.T) {
tests := []struct {
name string
bindings map[string][]ExtendedBoundContract
request ExtendedBatchGetLatestValuesRequest
mockResponse types.BatchGetLatestValuesResult
expectedError error
expectedResult types.BatchGetLatestValuesResult
name string
bindings map[string][]ExtendedBoundContract
request ExtendedBatchGetLatestValuesRequest
mockResponse types.BatchGetLatestValuesResult
graceful bool
expectedError error
expectedResult types.BatchGetLatestValuesResult
expectedSkipped []string
}{
{
name: "single contract single read success",
name: "single contract single read success - non-graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
Expand All @@ -92,15 +94,43 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
{ReadName: "read1"},
},
},
graceful: false,
expectedError: nil,
expectedResult: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
{ReadName: "read1"},
},
},
expectedSkipped: nil,
},
{
name: "contract not found",
name: "single contract single read success - graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
},
},
request: ExtendedBatchGetLatestValuesRequest{
"contract1": {
{ReadName: "read1", Params: "params1", ReturnVal: "return1"},
},
},
mockResponse: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
{ReadName: "read1"},
},
},
graceful: true,
expectedError: nil,
expectedResult: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
{ReadName: "read1"},
},
},
expectedSkipped: nil,
},
{
name: "contract not found - non-graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
Expand All @@ -111,12 +141,32 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
{ReadName: "read1", Params: "params1", ReturnVal: "return1"},
},
},
mockResponse: nil,
expectedError: ErrNoBindings,
expectedResult: nil,
mockResponse: nil,
graceful: false,
expectedError: ErrNoBindings,
expectedResult: nil,
expectedSkipped: nil,
},
{
name: "multiple bindings for contract",
name: "contract not found - graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
},
},
request: ExtendedBatchGetLatestValuesRequest{
"nonexistent": {
{ReadName: "read1", Params: "params1", ReturnVal: "return1"},
},
},
mockResponse: nil,
graceful: true,
expectedError: nil,
expectedResult: types.BatchGetLatestValuesResult{},
expectedSkipped: []string{"nonexistent"},
},
{
name: "multiple bindings error - both modes",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
Expand All @@ -128,12 +178,14 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
{ReadName: "read1", Params: "params1", ReturnVal: "return1"},
},
},
mockResponse: nil,
expectedError: ErrTooManyBindings,
expectedResult: nil,
mockResponse: nil,
graceful: true, // Should fail even in graceful mode
expectedError: ErrTooManyBindings,
expectedResult: nil,
expectedSkipped: nil,
},
{
name: "multiple contracts success",
name: "multiple contracts with mixed results - graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
Expand All @@ -149,6 +201,9 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
"contract2": {
{ReadName: "read2", Params: "params2", ReturnVal: "return2"},
},
"nonexistent": {
{ReadName: "read3", Params: "params3", ReturnVal: "return3"},
},
},
mockResponse: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
Expand All @@ -158,6 +213,7 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
{ReadName: "read2"},
},
},
graceful: true,
expectedError: nil,
expectedResult: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
Expand All @@ -167,6 +223,66 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
{ReadName: "read2"},
},
},
expectedSkipped: []string{"nonexistent"},
},
{
name: "multiple contracts - non-graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
},
"contract2": {
{Binding: types.BoundContract{Name: "contract2", Address: "0x456"}},
},
},
request: ExtendedBatchGetLatestValuesRequest{
"contract1": {
{ReadName: "read1", Params: "params1", ReturnVal: "return1"},
},
"contract2": {
{ReadName: "read2", Params: "params2", ReturnVal: "return2"},
},
},
mockResponse: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
{ReadName: "read1"},
},
types.BoundContract{Name: "contract2", Address: "0x456"}: {
{ReadName: "read2"},
},
},
graceful: false,
expectedError: nil,
expectedResult: types.BatchGetLatestValuesResult{
types.BoundContract{Name: "contract1", Address: "0x123"}: {
{ReadName: "read1"},
},
types.BoundContract{Name: "contract2", Address: "0x456"}: {
{ReadName: "read2"},
},
},
expectedSkipped: nil,
},
{
name: "all contracts skipped - graceful",
bindings: map[string][]ExtendedBoundContract{
"contract1": {
{Binding: types.BoundContract{Name: "contract1", Address: "0x123"}},
},
},
request: ExtendedBatchGetLatestValuesRequest{
"nonexistent1": {
{ReadName: "read1", Params: "params1", ReturnVal: "return1"},
},
"nonexistent2": {
{ReadName: "read2", Params: "params2", ReturnVal: "return2"},
},
},
mockResponse: nil,
graceful: true,
expectedError: nil,
expectedResult: types.BatchGetLatestValuesResult{},
expectedSkipped: []string{"nonexistent1", "nonexistent2"},
},
}

Expand All @@ -178,22 +294,20 @@ func TestExtendedBatchGetLatestValues(t *testing.T) {
BatchGetLatestValuesResponse: tt.mockResponse,
}

// Create extended reader with mock
extendedReader := &extendedContractReader{
reader: mockReader,
contractBindingsByName: tt.bindings,
mu: &sync.RWMutex{},
}

// Execute test
result, err := extendedReader.ExtendedBatchGetLatestValues(context.Background(), tt.request)
result, skipped, err := extendedReader.ExtendedBatchGetLatestValues(context.Background(), tt.request, tt.graceful)

// Verify results
if tt.expectedError != nil {
assert.ErrorIs(t, err, tt.expectedError)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expectedResult, result)
assert.ElementsMatch(t, tt.expectedSkipped, skipped)
}
})
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/reader/ccip.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,10 @@ func (r *ccipChainReader) Nonces(
consts.ContractNameNonceManager: contractBatch,
}

batchResult, err := r.contractReaders[r.destChain].ExtendedBatchGetLatestValues(
batchResult, _, err := r.contractReaders[r.destChain].ExtendedBatchGetLatestValues(
ctx,
request,
false,
)
if err != nil {
return nil, fmt.Errorf("batch get nonces failed: %w", err)
Expand Down
Loading

0 comments on commit ce39798

Please sign in to comment.