From c50ed7e8ac16a7e9ce9c1ad22b8b74f5c66324cc Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 19 Mar 2026 14:22:48 +0100 Subject: [PATCH 1/6] CRE Don2Don accept OCR attestation of a response --- core/capabilities/launcher.go | 11 +- core/capabilities/remote/executable/client.go | 13 +- .../remote/executable/client_test.go | 18 +- .../remote/executable/endtoend_test.go | 2 +- .../executable/request/client_request.go | 117 +++++++-- .../request/client_request_internal_test.go | 225 ++++++++++++++++++ .../executable/request/client_request_test.go | 108 ++++++++- go.mod | 2 +- go.sum | 4 +- 9 files changed, 454 insertions(+), 46 deletions(-) create mode 100644 core/capabilities/remote/executable/request/client_request_internal_test.go diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index 32bda8c9e08..597d86eccff 100644 --- a/core/capabilities/launcher.go +++ b/core/capabilities/launcher.go @@ -9,6 +9,7 @@ import ( "time" "github.com/Masterminds/semver/v3" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/libocr/ragep2p" ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" @@ -490,7 +491,7 @@ func (w *launcher) addRemoteCapability(ctx context.Context, cid string, capabili methodConfig := capabilityConfig.CapabilityMethodConfig if methodConfig != nil { // v2 capability - handle via CombinedClient - errAdd := w.addRemoteCapabilityV2(ctx, capability.ID, methodConfig, myDON, remoteDON) + errAdd := w.addRemoteCapabilityV2(ctx, capability.ID, methodConfig, myDON, remoteDON, capabilityConfig.Ocr3Configs) if errAdd != nil { return fmt.Errorf("failed to add remote v2 capability %s: %w", capability.ID, errAdd) } @@ -581,7 +582,7 @@ func (w *launcher) addRemoteCapability(ctx context.Context, cid string, capabili w.cachedShims.executableClients[shimKey] = execCap } // V1 capabilities read transmission schedule from every request - if errCfg := execCap.SetConfig(info, myDON.DON, defaultTargetRequestTimeout, nil); errCfg != nil { + if errCfg := execCap.SetConfig(info, myDON.DON, defaultTargetRequestTimeout, nil, nil); errCfg != nil { return nil, fmt.Errorf("failed to set trigger config: %w", errCfg) } return execCap.(capabilityService), nil @@ -607,7 +608,7 @@ func (w *launcher) addRemoteCapability(ctx context.Context, cid string, capabili w.cachedShims.executableClients[shimKey] = execCap } // V1 capabilities read transmission schedule from every request - if errCfg := execCap.SetConfig(info, myDON.DON, defaultTargetRequestTimeout, nil); errCfg != nil { + if errCfg := execCap.SetConfig(info, myDON.DON, defaultTargetRequestTimeout, nil, nil); errCfg != nil { return nil, fmt.Errorf("failed to set trigger config: %w", errCfg) } return execCap.(capabilityService), nil @@ -914,7 +915,7 @@ func signersFor(don registrysyncer.DON, localRegistry *registrysyncer.LocalRegis } // Add a V2 capability with multiple methods, using CombinedClient. -func (w *launcher) addRemoteCapabilityV2(ctx context.Context, capID string, methodConfig map[string]capabilities.CapabilityMethodConfig, myDON registrysyncer.DON, remoteDON registrysyncer.DON) error { +func (w *launcher) addRemoteCapabilityV2(ctx context.Context, capID string, methodConfig map[string]capabilities.CapabilityMethodConfig, myDON registrysyncer.DON, remoteDON registrysyncer.DON, capabilityOcr3Configs map[string]ocrtypes.ContractConfig) error { info, err := capabilities.NewRemoteCapabilityInfo( capID, capabilities.CapabilityTypeCombined, @@ -969,7 +970,7 @@ func (w *launcher) addRemoteCapabilityV2(ctx context.Context, capID string, meth Schedule: transmission.EnumToString(config.RemoteExecutableConfig.TransmissionSchedule), DeltaStage: config.RemoteExecutableConfig.DeltaStage, } - err := client.SetConfig(info, myDON.DON, config.RemoteExecutableConfig.RequestTimeout, transmissionConfig) + err := client.SetConfig(info, myDON.DON, config.RemoteExecutableConfig.RequestTimeout, transmissionConfig, capabilityOcr3Configs) if err != nil { w.lggr.Errorw("failed to update client config", "capID", capID, "method", method, "error", err) continue diff --git a/core/capabilities/remote/executable/client.go b/core/capabilities/remote/executable/client.go index fe664aa6988..7f919053530 100644 --- a/core/capabilities/remote/executable/client.go +++ b/core/capabilities/remote/executable/client.go @@ -8,6 +8,8 @@ import ( "sync/atomic" "time" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -46,12 +48,14 @@ type dynamicConfig struct { requestTimeout time.Duration // Has to be set only for V2 capabilities. V1 capabilities read transmission schedule from every request. transmissionConfig *transmission.TransmissionConfig + // Has to be set only for V2 capabilities using OCR. + ocr3Configs map[string]ocrtypes.ContractConfig } type Client interface { commoncap.ExecutableCapability Receive(ctx context.Context, msg *types.MessageBody) - SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig) error + SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, ocr3Configs map[string]ocrtypes.ContractConfig) error } var _ Client = &client{} @@ -78,7 +82,7 @@ func NewClient(capabilityID string, capMethodName string, dispatcher types.Dispa // SetConfig sets the remote capability configuration dynamically // TransmissionConfig has to be set only for V2 capabilities. V1 capabilities read transmission schedule from every request. -func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig) error { +func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, ocr3Configs map[string]ocrtypes.ContractConfig) error { if remoteCapabilityInfo.ID == "" || remoteCapabilityInfo.ID != c.capabilityID { return fmt.Errorf("capability info provided does not match the client's capabilityID: %s != %s", remoteCapabilityInfo.ID, c.capabilityID) } @@ -98,8 +102,9 @@ func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localD localDONInfo: localDonInfo, requestTimeout: requestTimeout, transmissionConfig: transmissionConfig, + ocr3Configs: ocr3Configs, }) - c.lggr.Infow("SetConfig", "remoteDONName", remoteCapabilityInfo.DON.Name, "remoteDONID", remoteCapabilityInfo.DON.ID, "requestTimeout", requestTimeout, "transmissionConfig", transmissionConfig) + c.lggr.Infow("SetConfig", "remoteDONName", remoteCapabilityInfo.DON.Name, "remoteDONID", remoteCapabilityInfo.DON.ID, "requestTimeout", requestTimeout, "transmissionConfig", transmissionConfig, "ocr3Configs", ocr3Configs) return nil } @@ -234,7 +239,7 @@ func (c *client) Execute(ctx context.Context, capReq commoncap.CapabilityRequest } req, err := request.NewClientExecuteRequest(ctx, c.lggr, capReq, cfg.remoteCapabilityInfo, cfg.localDONInfo, c.dispatcher, - cfg.requestTimeout, cfg.transmissionConfig, c.capMethodName) + cfg.requestTimeout, cfg.transmissionConfig, c.capMethodName, cfg.ocr3Configs) if err != nil { return commoncap.CapabilityResponse{}, fmt.Errorf("failed to create client request: %w", err) } diff --git a/core/capabilities/remote/executable/client_test.go b/core/capabilities/remote/executable/client_test.go index 93c463f59c1..4d82eb85e57 100644 --- a/core/capabilities/remote/executable/client_test.go +++ b/core/capabilities/remote/executable/client_test.go @@ -243,7 +243,7 @@ func testClient(t *testing.T, numWorkflowPeers int, workflowNodeResponseTimeout for i := range numWorkflowPeers { workflowPeerDispatcher := broker.NewDispatcherForNode(workflowPeers[i]) caller := executable.NewClient(capInfo.ID, "", workflowPeerDispatcher, lggr) - err := caller.SetConfig(capInfo, workflowDonInfo, workflowNodeResponseTimeout, nil) + err := caller.SetConfig(capInfo, workflowDonInfo, workflowNodeResponseTimeout, nil, nil) require.NoError(t, err) servicetest.Run(t, caller) broker.RegisterReceiverNode(workflowPeers[i], caller) @@ -403,7 +403,7 @@ func TestClient_SetConfig(t *testing.T) { DeltaStage: 10 * time.Millisecond, } - err := client.SetConfig(validCapInfo, validDonInfo, validTimeout, transmissionConfig) + err := client.SetConfig(validCapInfo, validDonInfo, validTimeout, transmissionConfig, nil) require.NoError(t, err) // Verify config was set @@ -418,7 +418,7 @@ func TestClient_SetConfig(t *testing.T) { CapabilityType: commoncap.CapabilityTypeAction, } - err := client.SetConfig(invalidCapInfo, validDonInfo, validTimeout, nil) + err := client.SetConfig(invalidCapInfo, validDonInfo, validTimeout, nil, nil) require.Error(t, err) assert.Contains(t, err.Error(), "capability info provided does not match the client's capabilityID") assert.Contains(t, err.Error(), "different_capability@1.0.0 != test_capability@1.0.0") @@ -431,7 +431,7 @@ func TestClient_SetConfig(t *testing.T) { F: 0, } - err := client.SetConfig(validCapInfo, invalidDonInfo, validTimeout, nil) + err := client.SetConfig(validCapInfo, invalidDonInfo, validTimeout, nil, nil) require.Error(t, err) assert.Contains(t, err.Error(), "empty localDonInfo provided") }) @@ -439,7 +439,7 @@ func TestClient_SetConfig(t *testing.T) { t.Run("successful config update", func(t *testing.T) { // Set initial config initialTimeout := 10 * time.Second - err := client.SetConfig(validCapInfo, validDonInfo, initialTimeout, nil) + err := client.SetConfig(validCapInfo, validDonInfo, initialTimeout, nil, nil) require.NoError(t, err) // Replace with new config @@ -450,7 +450,7 @@ func TestClient_SetConfig(t *testing.T) { F: 1, } - err = client.SetConfig(validCapInfo, newDonInfo, newTimeout, nil) + err = client.SetConfig(validCapInfo, newDonInfo, newTimeout, nil, nil) require.NoError(t, err) // Verify the config was completely replaced @@ -494,7 +494,7 @@ func TestClient_SetConfig_StartClose(t *testing.T) { }) t.Run("start succeeds after config set", func(t *testing.T) { - require.NoError(t, client.SetConfig(validCapInfo, validDonInfo, validTimeout, nil)) + require.NoError(t, client.SetConfig(validCapInfo, validDonInfo, validTimeout, nil, nil)) require.NoError(t, client.Start(ctx)) require.NoError(t, client.Close()) }) @@ -504,12 +504,12 @@ func TestClient_SetConfig_StartClose(t *testing.T) { freshClient := executable.NewClient(capabilityID, "execute", dispatcher, lggr) // Set initial config and start - require.NoError(t, freshClient.SetConfig(validCapInfo, validDonInfo, validTimeout, nil)) + require.NoError(t, freshClient.SetConfig(validCapInfo, validDonInfo, validTimeout, nil, nil)) require.NoError(t, freshClient.Start(ctx)) // Update config while running validCapInfo.Description = "new description" - require.NoError(t, freshClient.SetConfig(validCapInfo, validDonInfo, validTimeout, nil)) + require.NoError(t, freshClient.SetConfig(validCapInfo, validDonInfo, validTimeout, nil, nil)) // Verify config was updated info, err := freshClient.Info(ctx) diff --git a/core/capabilities/remote/executable/endtoend_test.go b/core/capabilities/remote/executable/endtoend_test.go index 996c0ca4657..2917df3124b 100644 --- a/core/capabilities/remote/executable/endtoend_test.go +++ b/core/capabilities/remote/executable/endtoend_test.go @@ -309,7 +309,7 @@ func testRemoteExecutableCapability(ctx context.Context, t *testing.T, underlyin for i := range numWorkflowPeers { workflowPeerDispatcher := broker.NewDispatcherForNode(workflowPeers[i]) workflowNode := executable.NewClient(capInfo.ID, "", workflowPeerDispatcher, lggr) - err := workflowNode.SetConfig(capInfo, workflowDonInfo, workflowNodeTimeout, nil) + err := workflowNode.SetConfig(capInfo, workflowDonInfo, workflowNodeTimeout, nil, nil) require.NoError(t, err) servicetest.Run(t, workflowNode) broker.RegisterReceiverNode(workflowPeers[i], workflowNode) diff --git a/core/capabilities/remote/executable/request/client_request.go b/core/capabilities/remote/executable/request/client_request.go index 5afda38e4e2..fe3427ab081 100644 --- a/core/capabilities/remote/executable/request/client_request.go +++ b/core/capabilities/remote/executable/request/client_request.go @@ -10,10 +10,12 @@ import ( "sync" "time" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "google.golang.org/protobuf/proto" ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" + "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" "github.com/smartcontractkit/chainlink-common/pkg/beholder" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" @@ -33,16 +35,19 @@ type clientResponse struct { } type ClientRequest struct { - id string - cancelFn context.CancelFunc - responseCh chan clientResponse - createdAt time.Time - responseIDCount map[[32]byte]int - meteringResponses map[[32]byte][]commoncap.MeteringNodeDetail - errorCount map[string]int - totalErrorCount int - responseReceived map[p2ptypes.PeerID]bool - lggr logger.Logger + id string + cancelFn context.CancelFunc + responseCh chan clientResponse + createdAt time.Time + responseIDCount map[[32]byte]int + meteringResponses map[[32]byte][]commoncap.MeteringNodeDetail + errorCount map[string]int + totalErrorCount int + responseReceived map[p2ptypes.PeerID]bool + lggr logger.Logger + ocr3Configs map[string]ocrtypes.ContractConfig + workflowExecutionID string + referenceID string requiredIdenticalResponses int remoteNodeCount int @@ -58,6 +63,7 @@ type ClientRequest struct { func NewClientExecuteRequest(ctx context.Context, lggr logger.Logger, req commoncap.CapabilityRequest, remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, capMethodName string, + ocr3Configs map[string]ocrtypes.ContractConfig, ) (*ClientRequest, error) { rawRequest, err := proto.MarshalOptions{Deterministic: true}.Marshal(pb.CapabilityRequestToProto(req)) if err != nil { @@ -87,7 +93,7 @@ func NewClientExecuteRequest(ctx context.Context, lggr logger.Logger, req common } lggr = logger.With(lggr, "requestId", requestID) // cap ID and method name included in the parent logger - return newClientRequest(ctx, lggr, requestID, remoteCapabilityInfo, localDonInfo, dispatcher, requestTimeout, tc, types.MethodExecute, rawRequest, workflowExecutionID, req.Metadata.ReferenceID, capMethodName) + return newClientRequest(ctx, lggr, requestID, remoteCapabilityInfo, localDonInfo, dispatcher, requestTimeout, tc, types.MethodExecute, rawRequest, workflowExecutionID, req.Metadata.ReferenceID, capMethodName, ocr3Configs) } var defaultDelayMargin = 10 * time.Second @@ -95,6 +101,7 @@ var defaultDelayMargin = 10 * time.Second func newClientRequest(ctx context.Context, lggr logger.Logger, requestID string, remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, tc transmission.TransmissionConfig, methodType string, rawRequest []byte, workflowExecutionID string, stepRef string, capMethodName string, + ocr3Configs map[string]ocrtypes.ContractConfig, ) (*ClientRequest, error) { remoteCapabilityDonInfo := remoteCapabilityInfo.DON if remoteCapabilityDonInfo == nil { @@ -200,6 +207,9 @@ func newClientRequest(ctx context.Context, lggr logger.Logger, requestID string, responseCh: make(chan clientResponse, 1), wg: &wg, lggr: lggr, + ocr3Configs: ocr3Configs, + workflowExecutionID: workflowExecutionID, + referenceID: stepRef, }, nil } @@ -301,6 +311,34 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err c.responseReceived[sender] = true if msg.Error == types.Error_OK { + resp, err := pb.UnmarshalCapabilityResponse(msg.Payload) + if err != nil { + return fmt.Errorf("failed to unmarshal capability response: %w", err) + } + + if resp.Metadata.OCRAttestation != nil { + rpt, err := extractMeteringFromMetadata(sender, resp.Metadata) + if err != nil { + return fmt.Errorf("failed to extract metering detail from metadata: %w", err) + } + // Since signatures are provided switch to OCR based validation. It's enough to get 1 response with F+1 signatures + // to be confident that the response is honest. + err = c.verifyAttestation(resp, rpt) + if err != nil { + c.lggr.Errorw("failed to verify capability response OCR attestation", "peer", sender, "err", err, "requestID", c.id, "msgPayload", hex.EncodeToString(msg.Payload)) + return fmt.Errorf("failed to verify capability response OCR attestation: %w", err) + } + + var payload []byte + payload, err = c.encodePayloadWithMetadata(msg, commoncap.ResponseMetadata{Metering: []commoncap.MeteringNodeDetail{rpt}}) + if err != nil { + return fmt.Errorf("failed to encode payload with metadata: %w", err) + } + + c.sendResponse(clientResponse{Result: payload}) + return nil + } + // metering reports per node are aggregated into a single array of values. for any single node message, the // metering values are extracted from the CapabilityResponse, added to an array, and the CapabilityResponse // is marshalled without the metering value to get the hash. each node could have a different metering value @@ -317,13 +355,11 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err nodeReports = make([]commoncap.MeteringNodeDetail, 0) } - if len(metadata.Metering) == 1 { - rpt := metadata.Metering[0] - rpt.Peer2PeerID = sender.String() - - nodeReports = append(nodeReports, rpt) + rpt, err := extractMeteringFromMetadata(sender, metadata) + if err != nil { + lggr.Warnw("invalid metering detail", "err", err) } else { - lggr.Warnw("node metering detail did not contain exactly 1 record", "records", len(metadata.Metering)) + nodeReports = append(nodeReports, rpt) } c.responseIDCount[responseID]++ @@ -359,6 +395,53 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err return nil } +func extractMeteringFromMetadata(sender p2ptypes.PeerID, metadata commoncap.ResponseMetadata) (commoncap.MeteringNodeDetail, error) { + if len(metadata.Metering) != 1 { + return commoncap.MeteringNodeDetail{}, fmt.Errorf("unexpected number of metering records received from pperi %s: got %d, want 1", sender, len(metadata.Metering)) + } + + rpt := metadata.Metering[0] + rpt.Peer2PeerID = sender.String() + return rpt, nil +} + +func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse, metering commoncap.MeteringNodeDetail) error { + if c.ocr3Configs == nil { + return errors.New("OCR3 configs not provided, cannot verify signatures") + } + + cfg, ok := c.ocr3Configs[pb.OCR3ConfigDefaultKey] + if !ok { + return fmt.Errorf("OCR3 config with key %s not found", pb.OCR3ConfigDefaultKey) + } + + attestation := resp.Metadata.OCRAttestation + if len(attestation.Sigs) < int(cfg.F)+1 { + return fmt.Errorf("not enough signatures: got %d, need at least %d", len(attestation.Sigs), cfg.F+1) + } + + reportData := commoncap.ResponseToReportData(c.workflowExecutionID, c.referenceID, resp.Payload.Value, metering.SpendUnit, metering.SpendValue) + sigData := ocr2key.ReportToSigData3(attestation.ConfigDigest, attestation.SequenceNumber, reportData) + signed := make([]bool, len(cfg.Signers)) + for _, sig := range attestation.Sigs { + if int(sig.Signer) > len(cfg.Signers) { + return fmt.Errorf("invalid signer index: %d", sig.Signer) + } + + if signed[sig.Signer] { + return fmt.Errorf("duplicate signature from signer index: %d", sig.Signer) + } + + if !ocr2key.EvmVerifyBlob(cfg.Signers[sig.Signer], sigData, sig.Signature) { + return fmt.Errorf("invalid signature from signer index: %d", sig.Signer) + } + + signed[sig.Signer] = true + } + + return nil +} + func (c *ClientRequest) sendResponse(response clientResponse) { c.responseCh <- response close(c.responseCh) diff --git a/core/capabilities/remote/executable/request/client_request_internal_test.go b/core/capabilities/remote/executable/request/client_request_internal_test.go new file mode 100644 index 00000000000..851a3d2e385 --- /dev/null +++ b/core/capabilities/remote/executable/request/client_request_internal_test.go @@ -0,0 +1,225 @@ +package request + +import ( + "crypto/rand" + "testing" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/smartcontractkit/chainlink-common/keystore/corekeys" + "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" + commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-protos/cre/go/values" +) + +func Test_ClientRequest_VerifyAttestation(t *testing.T) { + const workflowExecutionID = "95ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0abbadeed" + const referenceID = "step1" + spendUnit, spendValue := "testunit", "42" + + val, err := values.NewMap(map[string]any{"response": "attested"}) + require.NoError(t, err) + valueProto := values.ProtoMap(val) + valueBytes, err := proto.Marshal(valueProto) + require.NoError(t, err) + + configDigest := ocrtypes.ConfigDigest{1, 2, 3, 4, 5} + seqNr := uint64(100) + + kb1, err := ocr2key.New(corekeys.EVM) + require.NoError(t, err) + kb2, err := ocr2key.New(corekeys.EVM) + require.NoError(t, err) + + reportData := commoncap.ResponseToReportData(workflowExecutionID, referenceID, valueBytes, spendUnit, spendValue) + + sig1, err := kb1.Sign3(configDigest, seqNr, reportData) + require.NoError(t, err) + sig2, err := kb2.Sign3(configDigest, seqNr, reportData) + require.NoError(t, err) + + ocr3Configs := map[string]ocrtypes.ContractConfig{ + pb.OCR3ConfigDefaultKey: { + ConfigDigest: configDigest, + Signers: []ocrtypes.OnchainPublicKey{kb1.PublicKey(), kb2.PublicKey()}, + F: 1, + }, + } + + c := &ClientRequest{ + lggr: logger.Test(t), + ocr3Configs: ocr3Configs, + workflowExecutionID: workflowExecutionID, + referenceID: referenceID, + } + + validResp := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{ + {SpendUnit: spendUnit, SpendValue: spendValue}, + }, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + + t.Run("nil ocr3Configs returns error", func(t *testing.T) { + cNil := &ClientRequest{workflowExecutionID: workflowExecutionID, referenceID: referenceID, lggr: logger.Test(t), ocr3Configs: nil} + err := cNil.verifyAttestation(validResp) + require.Error(t, err) + require.Contains(t, err.Error(), "OCR3 configs not provided") + }) + + t.Run("missing OCR3 config key returns error", func(t *testing.T) { + cBad := &ClientRequest{ + workflowExecutionID: workflowExecutionID, + referenceID: referenceID, + lggr: logger.Test(t), + ocr3Configs: map[string]ocrtypes.ContractConfig{}, + } + err := cBad.verifyAttestation(validResp) + require.Error(t, err) + require.Contains(t, err.Error(), "not found") + }) + + t.Run("not enough signatures returns error", func(t *testing.T) { + respFewSigs := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{{Signer: 0, Signature: sig1}}, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + err := c.verifyAttestation(respFewSigs) + require.Error(t, err) + require.Contains(t, err.Error(), "not enough signatures") + }) + + t.Run("unexpected number of metering records returns error", func(t *testing.T) { + respBadMetering := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{ + {SpendUnit: spendUnit, SpendValue: spendValue}, + {SpendUnit: "other", SpendValue: "99"}, + }, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + err := c.verifyAttestation(respBadMetering) + require.Error(t, err) + require.Contains(t, err.Error(), "unexpected number of metering records") + }) + + t.Run("invalid signer index returns error", func(t *testing.T) { + respBadSigner := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 99, Signature: sig2}, + }, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + err := c.verifyAttestation(respBadSigner) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid signer index") + }) + + t.Run("duplicate signature returns error", func(t *testing.T) { + respDupSig := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 0, Signature: sig1}, + }, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + err := c.verifyAttestation(respDupSig) + require.Error(t, err) + require.Contains(t, err.Error(), "duplicate signature") + }) + + t.Run("invalid signature returns error", func(t *testing.T) { + badSig := make([]byte, 65) + _, err := rand.Read(badSig) + require.NoError(t, err) + respBadSig := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: badSig}, + }, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + err = c.verifyAttestation(respBadSig) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid signature") + }) + + t.Run("wrong payload bytes produces invalid signature", func(t *testing.T) { + wrongBytes := []byte("tampered") + respWrongPayload := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + }, + }, + Payload: &anypb.Any{TypeUrl: "x", Value: wrongBytes}, + } + err := c.verifyAttestation(respWrongPayload) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid signature") + }) + + t.Run("valid attestation succeeds", func(t *testing.T) { + err := c.verifyAttestation(validResp) + require.NoError(t, err) + }) +} diff --git a/core/capabilities/remote/executable/request/client_request_test.go b/core/capabilities/remote/executable/request/client_request_test.go index 69bc64cef7c..c20a4286b80 100644 --- a/core/capabilities/remote/executable/request/client_request_test.go +++ b/core/capabilities/remote/executable/request/client_request_test.go @@ -8,14 +8,19 @@ import ( "testing" "time" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "google.golang.org/protobuf/types/known/anypb" + "github.com/smartcontractkit/chainlink-common/keystore/corekeys" + "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" "github.com/smartcontractkit/chainlink-common/pkg/beholder/beholdertest" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-protos/cre/go/values" + pbvalues "github.com/smartcontractkit/chainlink-protos/cre/go/values/pb" "github.com/smartcontractkit/chainlink-protos/workflows/go/events" "google.golang.org/protobuf/proto" @@ -83,7 +88,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) defer req.Cancel(errors.New("test end")) require.NoError(t, err) @@ -134,7 +139,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -168,7 +173,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -199,7 +204,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -237,7 +242,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -298,7 +303,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -330,6 +335,92 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { assert.Equal(t, resp, values.NewString("response1")) }) + t.Run("Execute Request With Valid Attestation", func(t *testing.T) { + ctx := t.Context() + capabilityPeers, capDonInfo, capInfo := capabilityDon(t, 4, 1) + + configDigest := ocrtypes.ConfigDigest{1, 2, 3, 4, 5} + kb1, err := ocr2key.New(corekeys.EVM) + require.NoError(t, err) + kb2, err := ocr2key.New(corekeys.EVM) + require.NoError(t, err) + dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} + req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", map[string]ocrtypes.ContractConfig{ + pb.OCR3ConfigDefaultKey: { + ConfigDigest: configDigest, + Signers: []ocrtypes.OnchainPublicKey{kb1.PublicKey(), kb2.PublicKey()}, + F: 1, + }, + }) + require.NoError(t, err) + defer req.Cancel(errors.New("test end")) + + <-dispatcher.msgs + <-dispatcher.msgs + assert.Empty(t, dispatcher.msgs) + + seqNr := uint64(100) + + payload, err := values.NewMap(map[string]int{ + "number": 42, + }) + require.NoError(t, err) + payloadAsAny, err := anypb.New(values.Proto(payload)) + require.NoError(t, err) + + spendUnit, spendValue := "testunit", "42" + reportData := commoncap.ResponseToReportData(capabilityRequest.Metadata.WorkflowExecutionID, capabilityRequest.Metadata.ReferenceID, payloadAsAny.Value, spendUnit, spendValue) + + sig1, err := kb1.Sign3(configDigest, seqNr, reportData) + require.NoError(t, err) + sig2, err := kb2.Sign3(configDigest, seqNr, reportData) + require.NoError(t, err) + + rawResponseWithAttestation, err := pb.MarshalCapabilityResponse(commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{ + {SpendUnit: spendUnit, SpendValue: spendValue}, + }, + OCRAttestation: &commoncap.ResponseOCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + }, + }, + Payload: payloadAsAny, + }) + require.NoError(t, err) + + msg := &types.MessageBody{ + CapabilityId: capInfo.ID, + CapabilityDonId: capDonInfo.ID, + CallerDonId: workflowDonInfo.ID, + Method: types.MethodExecute, + Payload: rawResponseWithAttestation, + MessageId: []byte("messageID"), + } + msg.Sender = capabilityPeers[0][:] + err = req.OnMessage(ctx, msg) + require.NoError(t, err) + + response := <-req.ResponseChan() + capResponse, err := pb.UnmarshalCapabilityResponse(response.Result) + require.NoError(t, err) + + var pbValue pbvalues.Value + require.NoError(t, capResponse.Payload.UnmarshalTo(&pbValue)) + receivedValue, err := values.FromProto(&pbValue) + require.NoError(t, err) + + var receivedMap map[string]int + require.NoError(t, receivedValue.UnwrapTo(&receivedMap)) + + assert.Equal(t, 42, receivedMap["number"]) + }) t.Run("Executes full schedule", func(t *testing.T) { beholderTester := beholdertest.NewObserver(t) @@ -357,6 +448,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { 10*time.Minute, nil, "", + nil, ) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -476,6 +568,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { 10*time.Minute, nil, "", + nil, ) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -568,7 +661,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -633,6 +726,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { DeltaStage: 1000 * time.Millisecond, }, "", + nil, ) require.NoError(t, err) defer req.Cancel(errors.New("test end")) diff --git a/go.mod b/go.mod index a2bfd6d5707..432996032c0 100644 --- a/go.mod +++ b/go.mod @@ -85,7 +85,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260224214816-cb23ec38649f github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 - github.com/smartcontractkit/chainlink-common v0.11.1 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260325173120-bbf92524d041 github.com/smartcontractkit/chainlink-common/keystore v1.0.2 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 github.com/smartcontractkit/chainlink-data-streams v0.1.13 diff --git a/go.sum b/go.sum index 90e6a40f131..106ce3327e2 100644 --- a/go.sum +++ b/go.sum @@ -1235,8 +1235,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260325173120-bbf92524d041 h1:7nd0tVXcfYT9Y661Ma/PK+jT5RcxChMow9uJ3PL/wE8= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260325173120-bbf92524d041/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= From b778e19de4bfbd07683a68b77fd68612c99a1f71 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 25 Mar 2026 18:52:45 +0100 Subject: [PATCH 2/6] Gracefully handle payload not available error --- core/capabilities/remote/executable/client.go | 2 +- .../executable/request/client_request.go | 50 ++++-- .../request/client_request_internal_test.go | 43 ++--- .../executable/request/client_request_test.go | 168 ++++++++++++++---- core/scripts/go.mod | 4 +- core/scripts/go.sum | 8 +- deployment/go.mod | 4 +- deployment/go.sum | 8 +- go.mod | 4 +- go.sum | 8 +- integration-tests/go.mod | 4 +- integration-tests/go.sum | 8 +- integration-tests/load/go.mod | 4 +- integration-tests/load/go.sum | 8 +- system-tests/lib/go.mod | 4 +- system-tests/lib/go.sum | 8 +- system-tests/tests/go.mod | 4 +- system-tests/tests/go.sum | 8 +- 18 files changed, 215 insertions(+), 132 deletions(-) diff --git a/core/capabilities/remote/executable/client.go b/core/capabilities/remote/executable/client.go index 7f919053530..8731facb2bc 100644 --- a/core/capabilities/remote/executable/client.go +++ b/core/capabilities/remote/executable/client.go @@ -104,7 +104,7 @@ func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localD transmissionConfig: transmissionConfig, ocr3Configs: ocr3Configs, }) - c.lggr.Infow("SetConfig", "remoteDONName", remoteCapabilityInfo.DON.Name, "remoteDONID", remoteCapabilityInfo.DON.ID, "requestTimeout", requestTimeout, "transmissionConfig", transmissionConfig, "ocr3Configs", ocr3Configs) + c.lggr.Infow("SetConfig", "remoteDONName", remoteCapabilityInfo.DON.Name, "remoteDONID", remoteCapabilityInfo.DON.ID, "requestTimeout", requestTimeout, "transmissionConfig", transmissionConfig) return nil } diff --git a/core/capabilities/remote/executable/request/client_request.go b/core/capabilities/remote/executable/request/client_request.go index fe3427ab081..208cf67a7ae 100644 --- a/core/capabilities/remote/executable/request/client_request.go +++ b/core/capabilities/remote/executable/request/client_request.go @@ -35,19 +35,20 @@ type clientResponse struct { } type ClientRequest struct { - id string - cancelFn context.CancelFunc - responseCh chan clientResponse - createdAt time.Time - responseIDCount map[[32]byte]int - meteringResponses map[[32]byte][]commoncap.MeteringNodeDetail - errorCount map[string]int - totalErrorCount int - responseReceived map[p2ptypes.PeerID]bool - lggr logger.Logger - ocr3Configs map[string]ocrtypes.ContractConfig - workflowExecutionID string - referenceID string + id string + cancelFn context.CancelFunc + responseCh chan clientResponse + createdAt time.Time + responseIDCount map[[32]byte]int + meteringResponses map[[32]byte][]commoncap.MeteringNodeDetail + errorCount map[string]int + totalErrorCount int + payloadNotAvailableCount int + responseReceived map[p2ptypes.PeerID]bool + lggr logger.Logger + ocr3Configs map[string]ocrtypes.ContractConfig + workflowExecutionID string + referenceID string requiredIdenticalResponses int remoteNodeCount int @@ -317,7 +318,8 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err } if resp.Metadata.OCRAttestation != nil { - rpt, err := extractMeteringFromMetadata(sender, resp.Metadata) + var rpt commoncap.MeteringNodeDetail + rpt, err = extractMeteringFromMetadata(sender, resp.Metadata) if err != nil { return fmt.Errorf("failed to extract metering detail from metadata: %w", err) } @@ -386,6 +388,16 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err c.lggr.Warnw("received multiple different errors for the same request", "numDifferentErrors", len(c.errorCount)) } + if commoncap.ErrResponsePayloadNotAvailable.Is(errors.New(msg.ErrorMsg)) { + c.payloadNotAvailableCount++ + if c.payloadNotAvailableCount == c.remoteNodeCount-c.requiredIdenticalResponses+1 { + // return an error to indicate unexpected state, but do not send an error as we might still receive a response with valid attestation. + return fmt.Errorf("unexpected state: received %d payload not available responses, while max allowed is %d. This means a bug in the code, please investigate", + c.payloadNotAvailableCount, c.remoteNodeCount-c.requiredIdenticalResponses) + } + return nil + } + if c.errorCount[msg.ErrorMsg] == c.requiredIdenticalResponses { c.sendResponse(clientResponse{Err: fmt.Errorf("%s : %s", msg.Error, msg.ErrorMsg)}) } else if c.totalErrorCount == c.remoteNodeCount-c.requiredIdenticalResponses+1 { @@ -397,7 +409,7 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err func extractMeteringFromMetadata(sender p2ptypes.PeerID, metadata commoncap.ResponseMetadata) (commoncap.MeteringNodeDetail, error) { if len(metadata.Metering) != 1 { - return commoncap.MeteringNodeDetail{}, fmt.Errorf("unexpected number of metering records received from pperi %s: got %d, want 1", sender, len(metadata.Metering)) + return commoncap.MeteringNodeDetail{}, fmt.Errorf("unexpected number of metering records received from peer %s: got %d, want 1", sender, len(metadata.Metering)) } rpt := metadata.Metering[0] @@ -416,15 +428,19 @@ func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse, met } attestation := resp.Metadata.OCRAttestation + if attestation == nil { + return errors.New("OCR attestation missing from response metadata") + } + if len(attestation.Sigs) < int(cfg.F)+1 { return fmt.Errorf("not enough signatures: got %d, need at least %d", len(attestation.Sigs), cfg.F+1) } reportData := commoncap.ResponseToReportData(c.workflowExecutionID, c.referenceID, resp.Payload.Value, metering.SpendUnit, metering.SpendValue) - sigData := ocr2key.ReportToSigData3(attestation.ConfigDigest, attestation.SequenceNumber, reportData) + sigData := ocr2key.ReportToSigData3(attestation.ConfigDigest, attestation.SequenceNumber, reportData[:]) signed := make([]bool, len(cfg.Signers)) for _, sig := range attestation.Sigs { - if int(sig.Signer) > len(cfg.Signers) { + if int(sig.Signer) >= len(cfg.Signers) { return fmt.Errorf("invalid signer index: %d", sig.Signer) } diff --git a/core/capabilities/remote/executable/request/client_request_internal_test.go b/core/capabilities/remote/executable/request/client_request_internal_test.go index 851a3d2e385..c1e7a5d20ca 100644 --- a/core/capabilities/remote/executable/request/client_request_internal_test.go +++ b/core/capabilities/remote/executable/request/client_request_internal_test.go @@ -38,9 +38,9 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { reportData := commoncap.ResponseToReportData(workflowExecutionID, referenceID, valueBytes, spendUnit, spendValue) - sig1, err := kb1.Sign3(configDigest, seqNr, reportData) + sig1, err := kb1.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) - sig2, err := kb2.Sign3(configDigest, seqNr, reportData) + sig2, err := kb2.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) ocr3Configs := map[string]ocrtypes.ContractConfig{ @@ -77,7 +77,7 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { t.Run("nil ocr3Configs returns error", func(t *testing.T) { cNil := &ClientRequest{workflowExecutionID: workflowExecutionID, referenceID: referenceID, lggr: logger.Test(t), ocr3Configs: nil} - err := cNil.verifyAttestation(validResp) + err := cNil.verifyAttestation(validResp, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "OCR3 configs not provided") }) @@ -89,7 +89,7 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { lggr: logger.Test(t), ocr3Configs: map[string]ocrtypes.ContractConfig{}, } - err := cBad.verifyAttestation(validResp) + err := cBad.verifyAttestation(validResp, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "not found") }) @@ -106,34 +106,11 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, } - err := c.verifyAttestation(respFewSigs) + err := c.verifyAttestation(respFewSigs, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "not enough signatures") }) - t.Run("unexpected number of metering records returns error", func(t *testing.T) { - respBadMetering := commoncap.CapabilityResponse{ - Metadata: commoncap.ResponseMetadata{ - Metering: []commoncap.MeteringNodeDetail{ - {SpendUnit: spendUnit, SpendValue: spendValue}, - {SpendUnit: "other", SpendValue: "99"}, - }, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 1, Signature: sig2}, - }, - }, - }, - Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, - } - err := c.verifyAttestation(respBadMetering) - require.Error(t, err) - require.Contains(t, err.Error(), "unexpected number of metering records") - }) - t.Run("invalid signer index returns error", func(t *testing.T) { respBadSigner := commoncap.CapabilityResponse{ Metadata: commoncap.ResponseMetadata{ @@ -149,7 +126,7 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, } - err := c.verifyAttestation(respBadSigner) + err := c.verifyAttestation(respBadSigner, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "invalid signer index") }) @@ -169,7 +146,7 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, } - err := c.verifyAttestation(respDupSig) + err := c.verifyAttestation(respDupSig, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "duplicate signature") }) @@ -192,7 +169,7 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, } - err = c.verifyAttestation(respBadSig) + err = c.verifyAttestation(respBadSig, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "invalid signature") }) @@ -213,13 +190,13 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { }, Payload: &anypb.Any{TypeUrl: "x", Value: wrongBytes}, } - err := c.verifyAttestation(respWrongPayload) + err := c.verifyAttestation(respWrongPayload, validResp.Metadata.Metering[0]) require.Error(t, err) require.Contains(t, err.Error(), "invalid signature") }) t.Run("valid attestation succeeds", func(t *testing.T) { - err := c.verifyAttestation(validResp) + err := c.verifyAttestation(validResp, validResp.Metadata.Metering[0]) require.NoError(t, err) }) } diff --git a/core/capabilities/remote/executable/request/client_request_test.go b/core/capabilities/remote/executable/request/client_request_test.go index c20a4286b80..1f30cc215c8 100644 --- a/core/capabilities/remote/executable/request/client_request_test.go +++ b/core/capabilities/remote/executable/request/client_request_test.go @@ -336,29 +336,15 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { assert.Equal(t, resp, values.NewString("response1")) }) t.Run("Execute Request With Valid Attestation", func(t *testing.T) { - ctx := t.Context() - capabilityPeers, capDonInfo, capInfo := capabilityDon(t, 4, 1) + const F = 1 + const N = 3*F + 1 + capabilityPeers, capDonInfo, capInfo := capabilityDon(t, N, F) configDigest := ocrtypes.ConfigDigest{1, 2, 3, 4, 5} kb1, err := ocr2key.New(corekeys.EVM) require.NoError(t, err) kb2, err := ocr2key.New(corekeys.EVM) require.NoError(t, err) - dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} - req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "", map[string]ocrtypes.ContractConfig{ - pb.OCR3ConfigDefaultKey: { - ConfigDigest: configDigest, - Signers: []ocrtypes.OnchainPublicKey{kb1.PublicKey(), kb2.PublicKey()}, - F: 1, - }, - }) - require.NoError(t, err) - defer req.Cancel(errors.New("test end")) - - <-dispatcher.msgs - <-dispatcher.msgs - assert.Empty(t, dispatcher.msgs) seqNr := uint64(100) @@ -372,9 +358,9 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { spendUnit, spendValue := "testunit", "42" reportData := commoncap.ResponseToReportData(capabilityRequest.Metadata.WorkflowExecutionID, capabilityRequest.Metadata.ReferenceID, payloadAsAny.Value, spendUnit, spendValue) - sig1, err := kb1.Sign3(configDigest, seqNr, reportData) + sig1, err := kb1.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) - sig2, err := kb2.Sign3(configDigest, seqNr, reportData) + sig2, err := kb2.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) rawResponseWithAttestation, err := pb.MarshalCapabilityResponse(commoncap.CapabilityResponse{ @@ -395,31 +381,135 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { }) require.NoError(t, err) - msg := &types.MessageBody{ - CapabilityId: capInfo.ID, - CapabilityDonId: capDonInfo.ID, - CallerDonId: workflowDonInfo.ID, - Method: types.MethodExecute, - Payload: rawResponseWithAttestation, - MessageId: []byte("messageID"), + ocrConfigs := map[string]ocrtypes.ContractConfig{ + pb.OCR3ConfigDefaultKey: { + ConfigDigest: configDigest, + Signers: []ocrtypes.OnchainPublicKey{kb1.PublicKey(), kb2.PublicKey()}, + F: F, + }, } - msg.Sender = capabilityPeers[0][:] - err = req.OnMessage(ctx, msg) - require.NoError(t, err) - response := <-req.ResponseChan() - capResponse, err := pb.UnmarshalCapabilityResponse(response.Result) - require.NoError(t, err) + assertValidResponse := func(t *testing.T, result []byte) { + capResponse, err := pb.UnmarshalCapabilityResponse(result) + require.NoError(t, err) - var pbValue pbvalues.Value - require.NoError(t, capResponse.Payload.UnmarshalTo(&pbValue)) - receivedValue, err := values.FromProto(&pbValue) - require.NoError(t, err) + var pbValue pbvalues.Value + require.NoError(t, capResponse.Payload.UnmarshalTo(&pbValue)) + receivedValue, err := values.FromProto(&pbValue) + require.NoError(t, err) + + var receivedMap map[string]int + require.NoError(t, receivedValue.UnwrapTo(&receivedMap)) + + assert.Equal(t, 42, receivedMap["number"]) + } - var receivedMap map[string]int - require.NoError(t, receivedValue.UnwrapTo(&receivedMap)) + t.Run("succeeds on first peer with valid attestation", func(t *testing.T) { + ctx := t.Context() - assert.Equal(t, 42, receivedMap["number"]) + dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} + req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrConfigs) + require.NoError(t, err) + defer req.Cancel(errors.New("test end")) + + for range N { + <-dispatcher.msgs + } + + assert.Empty(t, dispatcher.msgs) + + msg := &types.MessageBody{ + CapabilityId: capInfo.ID, + CapabilityDonId: capDonInfo.ID, + CallerDonId: workflowDonInfo.ID, + Method: types.MethodExecute, + Payload: rawResponseWithAttestation, + MessageId: []byte("messageID"), + } + msg.Sender = capabilityPeers[0][:] + err = req.OnMessage(ctx, msg) + require.NoError(t, err) + + response := <-req.ResponseChan() + assertValidResponse(t, response.Result) + }) + + t.Run("2F peers return ErrResponsePayloadNotAvailable then success", func(t *testing.T) { + ctx := t.Context() + dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} + req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrConfigs) + require.NoError(t, err) + defer req.Cancel(errors.New("test end")) + + for range N { + <-dispatcher.msgs + } + + assert.Empty(t, dispatcher.msgs) + + for i := range 2 * F { + msgNA := &types.MessageBody{ + CapabilityId: capInfo.ID, + CapabilityDonId: capDonInfo.ID, + CallerDonId: workflowDonInfo.ID, + Method: types.MethodExecute, + MessageId: []byte("messageID"), + Error: types.Error_INTERNAL_ERROR, + ErrorMsg: commoncap.ErrResponsePayloadNotAvailable.Error(), + } + msgNA.Sender = capabilityPeers[i][:] + require.NoError(t, req.OnMessage(ctx, msgNA)) + } + + msgOK := &types.MessageBody{ + CapabilityId: capInfo.ID, + CapabilityDonId: capDonInfo.ID, + CallerDonId: workflowDonInfo.ID, + Method: types.MethodExecute, + Payload: rawResponseWithAttestation, + MessageId: []byte("messageID"), + } + msgOK.Sender = capabilityPeers[2*F][:] + require.NoError(t, req.OnMessage(ctx, msgOK)) + + response := <-req.ResponseChan() + assertValidResponse(t, response.Result) + }) + + t.Run("2F+1 peers return ErrResponsePayloadNotAvailable", func(t *testing.T) { + ctx := t.Context() + dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} + req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrConfigs) + require.NoError(t, err) + defer req.Cancel(errors.New("test end")) + + for range N { + <-dispatcher.msgs + } + + assert.Empty(t, dispatcher.msgs) + + noPayloadMsg := types.MessageBody{ + CapabilityId: capInfo.ID, + CapabilityDonId: capDonInfo.ID, + CallerDonId: workflowDonInfo.ID, + Method: types.MethodExecute, + MessageId: []byte("messageID"), + Error: types.Error_INTERNAL_ERROR, + ErrorMsg: commoncap.ErrResponsePayloadNotAvailable.Error(), + } + + for i := range 2 * F { + noPayloadMsg.Sender = capabilityPeers[i][:] + require.NoError(t, req.OnMessage(ctx, &noPayloadMsg)) + } + + noPayloadMsg.Sender = capabilityPeers[2*F][:] + require.Error(t, req.OnMessage(ctx, &noPayloadMsg)) + }) }) t.Run("Executes full schedule", func(t *testing.T) { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c1a7117e70f..e9bfe3fcad9 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -46,8 +46,8 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 - github.com/smartcontractkit/chainlink-common v0.11.1 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-data-streams v0.1.13 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce diff --git a/core/scripts/go.sum b/core/scripts/go.sum index c11f114c825..9bc70547b54 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1634,10 +1634,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4 h1:NOUsjsMzNecbjiPWUQGlRSRAutEvCFrqqyETDJeh5q4= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4/go.mod h1:Zpvul9sTcZNAZOVzt5vBl1XZGNvQebFpnpn3/KOQvOQ= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/deployment/go.mod b/deployment/go.mod index 62165212e6c..ae8310abb96 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -44,8 +44,8 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260224214816-cb23ec38649f github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317175207-e9ff89561326 - github.com/smartcontractkit/chainlink-common v0.11.1 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 diff --git a/deployment/go.sum b/deployment/go.sum index 87f706591c9..c796d23721a 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1387,10 +1387,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317175207-e9ff github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317175207-e9ff89561326/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/go.mod b/go.mod index 432996032c0..f10008ed69d 100644 --- a/go.mod +++ b/go.mod @@ -85,8 +85,8 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260224214816-cb23ec38649f github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 - github.com/smartcontractkit/chainlink-common v0.10.1-0.20260325173120-bbf92524d041 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 github.com/smartcontractkit/chainlink-data-streams v0.1.13 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce diff --git a/go.sum b/go.sum index 106ce3327e2..5b7f016d157 100644 --- a/go.sum +++ b/go.sum @@ -1235,10 +1235,10 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260325173120-bbf92524d041 h1:7nd0tVXcfYT9Y661Ma/PK+jT5RcxChMow9uJ3PL/wE8= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260325173120-bbf92524d041/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index b06923d0586..d2db3aa1a76 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -47,8 +47,8 @@ require ( github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 - github.com/smartcontractkit/chainlink-common v0.11.1 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 8201c16deff..445a89e7154 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1626,10 +1626,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 58e172d9b07..ed297b1a8f8 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -27,7 +27,7 @@ require ( github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 - github.com/smartcontractkit/chainlink-common v0.11.1 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.3 @@ -475,7 +475,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20260317185256-d5f7db87ae70 // indirect github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70 // indirect github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 // indirect - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 // indirect + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.13 // indirect github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index bf2007188ce..7188eea47ab 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1596,10 +1596,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index dffa938c526..0117348cdc3 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -32,8 +32,8 @@ require ( github.com/sethvargo/go-retry v0.3.0 github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 - github.com/smartcontractkit/chainlink-common v0.11.1 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index ec5cce345cf..ec2b6b948d9 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1601,10 +1601,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 9a40f5dec83..c078fa1f33b 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -56,8 +56,8 @@ require ( github.com/rs/zerolog v1.34.0 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.97 - github.com/smartcontractkit/chainlink-common v0.11.1 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 github.com/smartcontractkit/chainlink-data-streams v0.1.13 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 016bb938e18..aa0985b13a9 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1785,10 +1785,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.1 h1:JVTnqoQjdLDmQQXNgssmzEQnJK0gQ/0427LqS4UDuqE= -github.com/smartcontractkit/chainlink-common v0.11.1/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= -github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4 h1:NOUsjsMzNecbjiPWUQGlRSRAutEvCFrqqyETDJeh5q4= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4/go.mod h1:Zpvul9sTcZNAZOVzt5vBl1XZGNvQebFpnpn3/KOQvOQ= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= From 28e17c5179f6cdb55f2f68e8261ec244153b7106 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 26 Mar 2026 12:16:53 +0100 Subject: [PATCH 3/6] Use signers instead of full OCR config --- core/capabilities/launcher.go | 12 ++- core/capabilities/remote/executable/client.go | 12 ++- .../executable/request/client_request.go | 80 +++++++++---------- .../request/client_request_internal_test.go | 37 +++------ .../executable/request/client_request_test.go | 14 +--- 5 files changed, 66 insertions(+), 89 deletions(-) diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index 597d86eccff..ee578ce0c0a 100644 --- a/core/capabilities/launcher.go +++ b/core/capabilities/launcher.go @@ -9,7 +9,6 @@ import ( "time" "github.com/Masterminds/semver/v3" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/libocr/ragep2p" ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" @@ -491,7 +490,7 @@ func (w *launcher) addRemoteCapability(ctx context.Context, cid string, capabili methodConfig := capabilityConfig.CapabilityMethodConfig if methodConfig != nil { // v2 capability - handle via CombinedClient - errAdd := w.addRemoteCapabilityV2(ctx, capability.ID, methodConfig, myDON, remoteDON, capabilityConfig.Ocr3Configs) + errAdd := w.addRemoteCapabilityV2(ctx, capability.ID, methodConfig, myDON, remoteDON, localRegistry) if errAdd != nil { return fmt.Errorf("failed to add remote v2 capability %s: %w", capability.ID, errAdd) } @@ -915,7 +914,7 @@ func signersFor(don registrysyncer.DON, localRegistry *registrysyncer.LocalRegis } // Add a V2 capability with multiple methods, using CombinedClient. -func (w *launcher) addRemoteCapabilityV2(ctx context.Context, capID string, methodConfig map[string]capabilities.CapabilityMethodConfig, myDON registrysyncer.DON, remoteDON registrysyncer.DON, capabilityOcr3Configs map[string]ocrtypes.ContractConfig) error { +func (w *launcher) addRemoteCapabilityV2(ctx context.Context, capID string, methodConfig map[string]capabilities.CapabilityMethodConfig, myDON registrysyncer.DON, remoteDON registrysyncer.DON, localRegistry *registrysyncer.LocalRegistry) error { info, err := capabilities.NewRemoteCapabilityInfo( capID, capabilities.CapabilityTypeCombined, @@ -970,7 +969,12 @@ func (w *launcher) addRemoteCapabilityV2(ctx context.Context, capID string, meth Schedule: transmission.EnumToString(config.RemoteExecutableConfig.TransmissionSchedule), DeltaStage: config.RemoteExecutableConfig.DeltaStage, } - err := client.SetConfig(info, myDON.DON, config.RemoteExecutableConfig.RequestTimeout, transmissionConfig, capabilityOcr3Configs) + + signers, err := signersFor(remoteDON, localRegistry) + if err != nil { + return fmt.Errorf("failed to get signers for executable client: %w", err) + } + err = client.SetConfig(info, myDON.DON, config.RemoteExecutableConfig.RequestTimeout, transmissionConfig, signers) if err != nil { w.lggr.Errorw("failed to update client config", "capID", capID, "method", method, "error", err) continue diff --git a/core/capabilities/remote/executable/client.go b/core/capabilities/remote/executable/client.go index 8731facb2bc..96abdc76eb3 100644 --- a/core/capabilities/remote/executable/client.go +++ b/core/capabilities/remote/executable/client.go @@ -8,8 +8,6 @@ import ( "sync/atomic" "time" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -49,13 +47,13 @@ type dynamicConfig struct { // Has to be set only for V2 capabilities. V1 capabilities read transmission schedule from every request. transmissionConfig *transmission.TransmissionConfig // Has to be set only for V2 capabilities using OCR. - ocr3Configs map[string]ocrtypes.ContractConfig + signers [][]byte } type Client interface { commoncap.ExecutableCapability Receive(ctx context.Context, msg *types.MessageBody) - SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, ocr3Configs map[string]ocrtypes.ContractConfig) error + SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, signers [][]byte) error } var _ Client = &client{} @@ -82,7 +80,7 @@ func NewClient(capabilityID string, capMethodName string, dispatcher types.Dispa // SetConfig sets the remote capability configuration dynamically // TransmissionConfig has to be set only for V2 capabilities. V1 capabilities read transmission schedule from every request. -func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, ocr3Configs map[string]ocrtypes.ContractConfig) error { +func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, signers [][]byte) error { if remoteCapabilityInfo.ID == "" || remoteCapabilityInfo.ID != c.capabilityID { return fmt.Errorf("capability info provided does not match the client's capabilityID: %s != %s", remoteCapabilityInfo.ID, c.capabilityID) } @@ -102,7 +100,7 @@ func (c *client) SetConfig(remoteCapabilityInfo commoncap.CapabilityInfo, localD localDONInfo: localDonInfo, requestTimeout: requestTimeout, transmissionConfig: transmissionConfig, - ocr3Configs: ocr3Configs, + signers: signers, }) c.lggr.Infow("SetConfig", "remoteDONName", remoteCapabilityInfo.DON.Name, "remoteDONID", remoteCapabilityInfo.DON.ID, "requestTimeout", requestTimeout, "transmissionConfig", transmissionConfig) return nil @@ -239,7 +237,7 @@ func (c *client) Execute(ctx context.Context, capReq commoncap.CapabilityRequest } req, err := request.NewClientExecuteRequest(ctx, c.lggr, capReq, cfg.remoteCapabilityInfo, cfg.localDONInfo, c.dispatcher, - cfg.requestTimeout, cfg.transmissionConfig, c.capMethodName, cfg.ocr3Configs) + cfg.requestTimeout, cfg.transmissionConfig, c.capMethodName, cfg.signers) if err != nil { return commoncap.CapabilityResponse{}, fmt.Errorf("failed to create client request: %w", err) } diff --git a/core/capabilities/remote/executable/request/client_request.go b/core/capabilities/remote/executable/request/client_request.go index 208cf67a7ae..b67fc85fc73 100644 --- a/core/capabilities/remote/executable/request/client_request.go +++ b/core/capabilities/remote/executable/request/client_request.go @@ -10,7 +10,6 @@ import ( "sync" "time" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "google.golang.org/protobuf/proto" ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" @@ -46,12 +45,12 @@ type ClientRequest struct { payloadNotAvailableCount int responseReceived map[p2ptypes.PeerID]bool lggr logger.Logger - ocr3Configs map[string]ocrtypes.ContractConfig + signers [][]byte workflowExecutionID string referenceID string - requiredIdenticalResponses int - remoteNodeCount int + requiredResponseConfirmations int + remoteNodeCount int requestTimeout time.Duration @@ -64,7 +63,7 @@ type ClientRequest struct { func NewClientExecuteRequest(ctx context.Context, lggr logger.Logger, req commoncap.CapabilityRequest, remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, transmissionConfig *transmission.TransmissionConfig, capMethodName string, - ocr3Configs map[string]ocrtypes.ContractConfig, + signers [][]byte, ) (*ClientRequest, error) { rawRequest, err := proto.MarshalOptions{Deterministic: true}.Marshal(pb.CapabilityRequestToProto(req)) if err != nil { @@ -94,7 +93,7 @@ func NewClientExecuteRequest(ctx context.Context, lggr logger.Logger, req common } lggr = logger.With(lggr, "requestId", requestID) // cap ID and method name included in the parent logger - return newClientRequest(ctx, lggr, requestID, remoteCapabilityInfo, localDonInfo, dispatcher, requestTimeout, tc, types.MethodExecute, rawRequest, workflowExecutionID, req.Metadata.ReferenceID, capMethodName, ocr3Configs) + return newClientRequest(ctx, lggr, requestID, remoteCapabilityInfo, localDonInfo, dispatcher, requestTimeout, tc, types.MethodExecute, rawRequest, workflowExecutionID, req.Metadata.ReferenceID, capMethodName, signers) } var defaultDelayMargin = 10 * time.Second @@ -102,7 +101,7 @@ var defaultDelayMargin = 10 * time.Second func newClientRequest(ctx context.Context, lggr logger.Logger, requestID string, remoteCapabilityInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, tc transmission.TransmissionConfig, methodType string, rawRequest []byte, workflowExecutionID string, stepRef string, capMethodName string, - ocr3Configs map[string]ocrtypes.ContractConfig, + signers [][]byte, ) (*ClientRequest, error) { remoteCapabilityDonInfo := remoteCapabilityInfo.DON if remoteCapabilityDonInfo == nil { @@ -195,22 +194,22 @@ func newClientRequest(ctx context.Context, lggr logger.Logger, requestID string, } return &ClientRequest{ - id: requestID, - cancelFn: cancelFn, - createdAt: time.Now(), - requestTimeout: requestTimeout, - requiredIdenticalResponses: int(remoteCapabilityDonInfo.F + 1), - remoteNodeCount: len(remoteCapabilityDonInfo.Members), - responseIDCount: make(map[[32]byte]int), - meteringResponses: make(map[[32]byte][]commoncap.MeteringNodeDetail), - errorCount: make(map[string]int), - responseReceived: responseReceived, - responseCh: make(chan clientResponse, 1), - wg: &wg, - lggr: lggr, - ocr3Configs: ocr3Configs, - workflowExecutionID: workflowExecutionID, - referenceID: stepRef, + id: requestID, + cancelFn: cancelFn, + createdAt: time.Now(), + requestTimeout: requestTimeout, + requiredResponseConfirmations: int(remoteCapabilityDonInfo.F + 1), + remoteNodeCount: len(remoteCapabilityDonInfo.Members), + responseIDCount: make(map[[32]byte]int), + meteringResponses: make(map[[32]byte][]commoncap.MeteringNodeDetail), + errorCount: make(map[string]int), + responseReceived: responseReceived, + responseCh: make(chan clientResponse, 1), + wg: &wg, + lggr: lggr, + signers: signers, + workflowExecutionID: workflowExecutionID, + referenceID: stepRef, }, nil } @@ -350,7 +349,7 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err return fmt.Errorf("failed to get message hash: %w", err) } - lggr := logger.With(c.lggr, "responseID", hex.EncodeToString(responseID[:]), "requiredCount", c.requiredIdenticalResponses, "peer", sender) + lggr := logger.With(c.lggr, "responseID", hex.EncodeToString(responseID[:]), "requiredCount", c.requiredResponseConfirmations, "peer", sender) nodeReports, exists := c.meteringResponses[responseID] if !exists { @@ -371,7 +370,7 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err lggr.Warnw("received multiple unique responses for the same request", "count for responseID", len(c.responseIDCount)) } - if c.responseIDCount[responseID] == c.requiredIdenticalResponses { + if c.responseIDCount[responseID] == c.requiredResponseConfirmations { payload, err := c.encodePayloadWithMetadata(msg, commoncap.ResponseMetadata{Metering: nodeReports}) if err != nil { return fmt.Errorf("failed to encode payload with metadata: %w", err) @@ -390,17 +389,17 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err if commoncap.ErrResponsePayloadNotAvailable.Is(errors.New(msg.ErrorMsg)) { c.payloadNotAvailableCount++ - if c.payloadNotAvailableCount == c.remoteNodeCount-c.requiredIdenticalResponses+1 { + if c.payloadNotAvailableCount == c.remoteNodeCount-c.requiredResponseConfirmations+1 { // return an error to indicate unexpected state, but do not send an error as we might still receive a response with valid attestation. return fmt.Errorf("unexpected state: received %d payload not available responses, while max allowed is %d. This means a bug in the code, please investigate", - c.payloadNotAvailableCount, c.remoteNodeCount-c.requiredIdenticalResponses) + c.payloadNotAvailableCount, c.remoteNodeCount-c.requiredResponseConfirmations) } return nil } - if c.errorCount[msg.ErrorMsg] == c.requiredIdenticalResponses { + if c.errorCount[msg.ErrorMsg] == c.requiredResponseConfirmations { c.sendResponse(clientResponse{Err: fmt.Errorf("%s : %s", msg.Error, msg.ErrorMsg)}) - } else if c.totalErrorCount == c.remoteNodeCount-c.requiredIdenticalResponses+1 { + } else if c.totalErrorCount == c.remoteNodeCount-c.requiredResponseConfirmations+1 { c.sendResponse(clientResponse{Err: fmt.Errorf("received %d errors, last error %s : %s", c.totalErrorCount, msg.Error, msg.ErrorMsg)}) } } @@ -418,29 +417,24 @@ func extractMeteringFromMetadata(sender p2ptypes.PeerID, metadata commoncap.Resp } func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse, metering commoncap.MeteringNodeDetail) error { - if c.ocr3Configs == nil { - return errors.New("OCR3 configs not provided, cannot verify signatures") - } - - cfg, ok := c.ocr3Configs[pb.OCR3ConfigDefaultKey] - if !ok { - return fmt.Errorf("OCR3 config with key %s not found", pb.OCR3ConfigDefaultKey) - } - attestation := resp.Metadata.OCRAttestation if attestation == nil { return errors.New("OCR attestation missing from response metadata") } - if len(attestation.Sigs) < int(cfg.F)+1 { - return fmt.Errorf("not enough signatures: got %d, need at least %d", len(attestation.Sigs), cfg.F+1) + if len(attestation.Sigs) < c.requiredResponseConfirmations { + return fmt.Errorf("not enough signatures: got %d, need at least %d", len(attestation.Sigs), c.requiredResponseConfirmations) + } + + if len(c.signers) < c.requiredResponseConfirmations { + return fmt.Errorf("number of configured OCR signers is less than required confirmations: got %d, need at least %d", len(c.signers), c.requiredResponseConfirmations) } reportData := commoncap.ResponseToReportData(c.workflowExecutionID, c.referenceID, resp.Payload.Value, metering.SpendUnit, metering.SpendValue) sigData := ocr2key.ReportToSigData3(attestation.ConfigDigest, attestation.SequenceNumber, reportData[:]) - signed := make([]bool, len(cfg.Signers)) + signed := make([]bool, len(c.signers)) for _, sig := range attestation.Sigs { - if int(sig.Signer) >= len(cfg.Signers) { + if int(sig.Signer) >= len(c.signers) { return fmt.Errorf("invalid signer index: %d", sig.Signer) } @@ -448,7 +442,7 @@ func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse, met return fmt.Errorf("duplicate signature from signer index: %d", sig.Signer) } - if !ocr2key.EvmVerifyBlob(cfg.Signers[sig.Signer], sigData, sig.Signature) { + if !ocr2key.EvmVerifyBlob(c.signers[sig.Signer], sigData, sig.Signature) { return fmt.Errorf("invalid signature from signer index: %d", sig.Signer) } diff --git a/core/capabilities/remote/executable/request/client_request_internal_test.go b/core/capabilities/remote/executable/request/client_request_internal_test.go index c1e7a5d20ca..4c6a556cd1f 100644 --- a/core/capabilities/remote/executable/request/client_request_internal_test.go +++ b/core/capabilities/remote/executable/request/client_request_internal_test.go @@ -12,7 +12,6 @@ import ( "github.com/smartcontractkit/chainlink-common/keystore/corekeys" "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-protos/cre/go/values" ) @@ -43,19 +42,14 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { sig2, err := kb2.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) - ocr3Configs := map[string]ocrtypes.ContractConfig{ - pb.OCR3ConfigDefaultKey: { - ConfigDigest: configDigest, - Signers: []ocrtypes.OnchainPublicKey{kb1.PublicKey(), kb2.PublicKey()}, - F: 1, - }, - } + signers := [][]byte{kb1.PublicKey(), kb2.PublicKey()} c := &ClientRequest{ - lggr: logger.Test(t), - ocr3Configs: ocr3Configs, - workflowExecutionID: workflowExecutionID, - referenceID: referenceID, + lggr: logger.Test(t), + signers: signers, + workflowExecutionID: workflowExecutionID, + referenceID: referenceID, + requiredResponseConfirmations: 2, } validResp := commoncap.CapabilityResponse{ @@ -75,23 +69,16 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, } - t.Run("nil ocr3Configs returns error", func(t *testing.T) { - cNil := &ClientRequest{workflowExecutionID: workflowExecutionID, referenceID: referenceID, lggr: logger.Test(t), ocr3Configs: nil} - err := cNil.verifyAttestation(validResp, validResp.Metadata.Metering[0]) - require.Error(t, err) - require.Contains(t, err.Error(), "OCR3 configs not provided") - }) - - t.Run("missing OCR3 config key returns error", func(t *testing.T) { + t.Run("not enough signers returns error", func(t *testing.T) { cBad := &ClientRequest{ - workflowExecutionID: workflowExecutionID, - referenceID: referenceID, - lggr: logger.Test(t), - ocr3Configs: map[string]ocrtypes.ContractConfig{}, + workflowExecutionID: workflowExecutionID, + referenceID: referenceID, + lggr: logger.Test(t), + requiredResponseConfirmations: 2, } err := cBad.verifyAttestation(validResp, validResp.Metadata.Metering[0]) require.Error(t, err) - require.Contains(t, err.Error(), "not found") + require.Contains(t, err.Error(), "number of configured OCR signers is less than required confirmations: got 0, need at least 2") }) t.Run("not enough signatures returns error", func(t *testing.T) { diff --git a/core/capabilities/remote/executable/request/client_request_test.go b/core/capabilities/remote/executable/request/client_request_test.go index 1f30cc215c8..0c157ad8ff8 100644 --- a/core/capabilities/remote/executable/request/client_request_test.go +++ b/core/capabilities/remote/executable/request/client_request_test.go @@ -381,13 +381,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { }) require.NoError(t, err) - ocrConfigs := map[string]ocrtypes.ContractConfig{ - pb.OCR3ConfigDefaultKey: { - ConfigDigest: configDigest, - Signers: []ocrtypes.OnchainPublicKey{kb1.PublicKey(), kb2.PublicKey()}, - F: F, - }, - } + ocrSigners := [][]byte{kb1.PublicKey(), kb2.PublicKey()} assertValidResponse := func(t *testing.T, result []byte) { capResponse, err := pb.UnmarshalCapabilityResponse(result) @@ -409,7 +403,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrConfigs) + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrSigners) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -439,7 +433,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { ctx := t.Context() dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrConfigs) + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrSigners) require.NoError(t, err) defer req.Cancel(errors.New("test end")) @@ -482,7 +476,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { ctx := t.Context() dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrConfigs) + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrSigners) require.NoError(t, err) defer req.Cancel(errors.New("test end")) From db96bf2e9aab62789eca1bc084c7d3a6fe31724f Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 27 Mar 2026 15:39:57 +0100 Subject: [PATCH 4/6] Refactor ocr attestation --- .../executable/request/client_request.go | 78 ++++------- .../request/client_request_internal_test.go | 126 +++++++++--------- .../executable/request/client_request_test.go | 39 +++--- core/scripts/go.mod | 6 +- core/scripts/go.sum | 12 +- deployment/go.mod | 6 +- deployment/go.sum | 12 +- go.mod | 6 +- go.sum | 12 +- integration-tests/go.mod | 6 +- integration-tests/go.sum | 12 +- integration-tests/load/go.mod | 6 +- integration-tests/load/go.sum | 12 +- system-tests/lib/go.mod | 6 +- system-tests/lib/go.sum | 12 +- system-tests/tests/go.mod | 6 +- system-tests/tests/go.sum | 12 +- 17 files changed, 177 insertions(+), 192 deletions(-) diff --git a/core/capabilities/remote/executable/request/client_request.go b/core/capabilities/remote/executable/request/client_request.go index b67fc85fc73..b5ac1f161ff 100644 --- a/core/capabilities/remote/executable/request/client_request.go +++ b/core/capabilities/remote/executable/request/client_request.go @@ -316,35 +316,11 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err return fmt.Errorf("failed to unmarshal capability response: %w", err) } - if resp.Metadata.OCRAttestation != nil { - var rpt commoncap.MeteringNodeDetail - rpt, err = extractMeteringFromMetadata(sender, resp.Metadata) - if err != nil { - return fmt.Errorf("failed to extract metering detail from metadata: %w", err) - } - // Since signatures are provided switch to OCR based validation. It's enough to get 1 response with F+1 signatures - // to be confident that the response is honest. - err = c.verifyAttestation(resp, rpt) - if err != nil { - c.lggr.Errorw("failed to verify capability response OCR attestation", "peer", sender, "err", err, "requestID", c.id, "msgPayload", hex.EncodeToString(msg.Payload)) - return fmt.Errorf("failed to verify capability response OCR attestation: %w", err) - } - - var payload []byte - payload, err = c.encodePayloadWithMetadata(msg, commoncap.ResponseMetadata{Metering: []commoncap.MeteringNodeDetail{rpt}}) - if err != nil { - return fmt.Errorf("failed to encode payload with metadata: %w", err) - } - - c.sendResponse(clientResponse{Result: payload}) - return nil - } - // metering reports per node are aggregated into a single array of values. for any single node message, the // metering values are extracted from the CapabilityResponse, added to an array, and the CapabilityResponse // is marshalled without the metering value to get the hash. each node could have a different metering value // which would result in different hashes. removing the metering detail allows for direct comparison of results. - responseID, metadata, err := c.getMessageHashAndMetadata(msg) + responseID, err := c.getMessageHash(resp) if err != nil { return fmt.Errorf("failed to get message hash: %w", err) } @@ -356,7 +332,7 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err nodeReports = make([]commoncap.MeteringNodeDetail, 0) } - rpt, err := extractMeteringFromMetadata(sender, metadata) + rpt, err := commoncap.ExtractMeteringFromMetadata(sender, resp.Metadata) if err != nil { lggr.Warnw("invalid metering detail", "err", err) } else { @@ -370,7 +346,7 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err lggr.Warnw("received multiple unique responses for the same request", "count for responseID", len(c.responseIDCount)) } - if c.responseIDCount[responseID] == c.requiredResponseConfirmations { + if c.responseIDCount[responseID] == c.requiredResponseConfirmations || c.hasValidAttestation(resp) { payload, err := c.encodePayloadWithMetadata(msg, commoncap.ResponseMetadata{Metering: nodeReports}) if err != nil { return fmt.Errorf("failed to encode payload with metadata: %w", err) @@ -406,20 +382,24 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err return nil } -func extractMeteringFromMetadata(sender p2ptypes.PeerID, metadata commoncap.ResponseMetadata) (commoncap.MeteringNodeDetail, error) { - if len(metadata.Metering) != 1 { - return commoncap.MeteringNodeDetail{}, fmt.Errorf("unexpected number of metering records received from peer %s: got %d, want 1", sender, len(metadata.Metering)) +func (c *ClientRequest) hasValidAttestation(resp commoncap.CapabilityResponse) bool { + if resp.OCRAttestation == nil { + return false + } + + err := c.verifyAttestation(resp) + if err != nil { + c.lggr.Errorw("Attestation is present, but not valid. This is most likely a bug and requires investigation - falling back to identical responses verification", "error", err) + return false } - rpt := metadata.Metering[0] - rpt.Peer2PeerID = sender.String() - return rpt, nil + return true } -func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse, metering commoncap.MeteringNodeDetail) error { - attestation := resp.Metadata.OCRAttestation +func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse) error { + attestation := resp.OCRAttestation if attestation == nil { - return errors.New("OCR attestation missing from response metadata") + return errors.New("attestation is missing") } if len(attestation.Sigs) < c.requiredResponseConfirmations { @@ -430,7 +410,10 @@ func (c *ClientRequest) verifyAttestation(resp commoncap.CapabilityResponse, met return fmt.Errorf("number of configured OCR signers is less than required confirmations: got %d, need at least %d", len(c.signers), c.requiredResponseConfirmations) } - reportData := commoncap.ResponseToReportData(c.workflowExecutionID, c.referenceID, resp.Payload.Value, metering.SpendUnit, metering.SpendValue) + reportData, err := commoncap.ResponseToReportData(c.workflowExecutionID, c.referenceID, resp.Payload.Value, resp.Metadata) + if err != nil { + return fmt.Errorf("failed to convert response to report data: %w", err) + } sigData := ocr2key.ReportToSigData3(attestation.ConfigDigest, attestation.SequenceNumber, reportData[:]) signed := make([]bool, len(c.signers)) for _, sig := range attestation.Sigs { @@ -463,23 +446,16 @@ func (c *ClientRequest) sendResponse(response clientResponse) { c.lggr.Debugw("received OK response") } -func (c *ClientRequest) getMessageHashAndMetadata(msg *types.MessageBody) ([32]byte, commoncap.ResponseMetadata, error) { - var metadata commoncap.ResponseMetadata - - resp, err := pb.UnmarshalCapabilityResponse(msg.Payload) - if err != nil { - return [32]byte{}, metadata, err - } - - metadata = resp.Metadata - resp.Metadata = commoncap.ResponseMetadata{} - - payload, err := pb.MarshalCapabilityResponse(resp) +func (c *ClientRequest) getMessageHash(msg commoncap.CapabilityResponse) ([32]byte, error) { + // clear metadata to ensure it doesn't affect the hash, as different nodes might have different metadata (e.g. different metering values) + // since msg is passed as value, this won't affect the original message + msg.Metadata = commoncap.ResponseMetadata{} + payload, err := pb.MarshalCapabilityResponse(msg) if err != nil { - return [32]byte{}, metadata, err + return [32]byte{}, err } - return sha256.Sum256(payload), metadata, nil + return sha256.Sum256(payload), nil } func (c *ClientRequest) encodePayloadWithMetadata(msg *types.MessageBody, metadata commoncap.ResponseMetadata) ([]byte, error) { diff --git a/core/capabilities/remote/executable/request/client_request_internal_test.go b/core/capabilities/remote/executable/request/client_request_internal_test.go index 4c6a556cd1f..2193bc3fa50 100644 --- a/core/capabilities/remote/executable/request/client_request_internal_test.go +++ b/core/capabilities/remote/executable/request/client_request_internal_test.go @@ -35,7 +35,17 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { kb2, err := ocr2key.New(corekeys.EVM) require.NoError(t, err) - reportData := commoncap.ResponseToReportData(workflowExecutionID, referenceID, valueBytes, spendUnit, spendValue) + validResp := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{ + {SpendUnit: spendUnit, SpendValue: spendValue}, + }, + }, + Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + } + + reportData, err := commoncap.ResponseToReportData(workflowExecutionID, referenceID, valueBytes, validResp.Metadata) + require.NoError(t, err) sig1, err := kb1.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) @@ -44,6 +54,15 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { signers := [][]byte{kb1.PublicKey(), kb2.PublicKey()} + validResp.OCRAttestation = &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + } + c := &ClientRequest{ lggr: logger.Test(t), signers: signers, @@ -52,23 +71,6 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { requiredResponseConfirmations: 2, } - validResp := commoncap.CapabilityResponse{ - Metadata: commoncap.ResponseMetadata{ - Metering: []commoncap.MeteringNodeDetail{ - {SpendUnit: spendUnit, SpendValue: spendValue}, - }, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 1, Signature: sig2}, - }, - }, - }, - Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, - } - t.Run("not enough signers returns error", func(t *testing.T) { cBad := &ClientRequest{ workflowExecutionID: workflowExecutionID, @@ -76,7 +78,7 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { lggr: logger.Test(t), requiredResponseConfirmations: 2, } - err := cBad.verifyAttestation(validResp, validResp.Metadata.Metering[0]) + err := cBad.verifyAttestation(validResp) require.Error(t, err) require.Contains(t, err.Error(), "number of configured OCR signers is less than required confirmations: got 0, need at least 2") }) @@ -85,15 +87,15 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { respFewSigs := commoncap.CapabilityResponse{ Metadata: commoncap.ResponseMetadata{ Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{{Signer: 0, Signature: sig1}}, - }, }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + OCRAttestation: &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{{Signer: 0, Signature: sig1}}, + }, } - err := c.verifyAttestation(respFewSigs, validResp.Metadata.Metering[0]) + err := c.verifyAttestation(respFewSigs) require.Error(t, err) require.Contains(t, err.Error(), "not enough signatures") }) @@ -102,18 +104,18 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { respBadSigner := commoncap.CapabilityResponse{ Metadata: commoncap.ResponseMetadata{ Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 99, Signature: sig2}, - }, - }, }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + OCRAttestation: &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 99, Signature: sig2}, + }, + }, } - err := c.verifyAttestation(respBadSigner, validResp.Metadata.Metering[0]) + err := c.verifyAttestation(respBadSigner) require.Error(t, err) require.Contains(t, err.Error(), "invalid signer index") }) @@ -122,18 +124,18 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { respDupSig := commoncap.CapabilityResponse{ Metadata: commoncap.ResponseMetadata{ Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 0, Signature: sig1}, - }, - }, }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + OCRAttestation: &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 0, Signature: sig1}, + }, + }, } - err := c.verifyAttestation(respDupSig, validResp.Metadata.Metering[0]) + err := c.verifyAttestation(respDupSig) require.Error(t, err) require.Contains(t, err.Error(), "duplicate signature") }) @@ -145,18 +147,18 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { respBadSig := commoncap.CapabilityResponse{ Metadata: commoncap.ResponseMetadata{ Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 1, Signature: badSig}, - }, - }, }, Payload: &anypb.Any{TypeUrl: "type.googleapis.com/values.v1.Map", Value: valueBytes}, + OCRAttestation: &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: badSig}, + }, + }, } - err = c.verifyAttestation(respBadSig, validResp.Metadata.Metering[0]) + err = c.verifyAttestation(respBadSig) require.Error(t, err) require.Contains(t, err.Error(), "invalid signature") }) @@ -166,24 +168,24 @@ func Test_ClientRequest_VerifyAttestation(t *testing.T) { respWrongPayload := commoncap.CapabilityResponse{ Metadata: commoncap.ResponseMetadata{ Metering: []commoncap.MeteringNodeDetail{{SpendUnit: spendUnit, SpendValue: spendValue}}, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 1, Signature: sig2}, - }, - }, }, Payload: &anypb.Any{TypeUrl: "x", Value: wrongBytes}, + OCRAttestation: &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + }, } - err := c.verifyAttestation(respWrongPayload, validResp.Metadata.Metering[0]) + err := c.verifyAttestation(respWrongPayload) require.Error(t, err) require.Contains(t, err.Error(), "invalid signature") }) t.Run("valid attestation succeeds", func(t *testing.T) { - err := c.verifyAttestation(validResp, validResp.Metadata.Metering[0]) + err := c.verifyAttestation(validResp) require.NoError(t, err) }) } diff --git a/core/capabilities/remote/executable/request/client_request_test.go b/core/capabilities/remote/executable/request/client_request_test.go index 0c157ad8ff8..04bb2e304bb 100644 --- a/core/capabilities/remote/executable/request/client_request_test.go +++ b/core/capabilities/remote/executable/request/client_request_test.go @@ -356,29 +356,33 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { require.NoError(t, err) spendUnit, spendValue := "testunit", "42" - reportData := commoncap.ResponseToReportData(capabilityRequest.Metadata.WorkflowExecutionID, capabilityRequest.Metadata.ReferenceID, payloadAsAny.Value, spendUnit, spendValue) + capResponse := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{ + {SpendUnit: spendUnit, SpendValue: spendValue}, + }, + }, + Payload: payloadAsAny, + } + + reportData, err := commoncap.ResponseToReportData(capabilityRequest.Metadata.WorkflowExecutionID, capabilityRequest.Metadata.ReferenceID, payloadAsAny.Value, capResponse.Metadata) + require.NoError(t, err) sig1, err := kb1.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) sig2, err := kb2.Sign3(configDigest, seqNr, reportData[:]) require.NoError(t, err) - rawResponseWithAttestation, err := pb.MarshalCapabilityResponse(commoncap.CapabilityResponse{ - Metadata: commoncap.ResponseMetadata{ - Metering: []commoncap.MeteringNodeDetail{ - {SpendUnit: spendUnit, SpendValue: spendValue}, - }, - OCRAttestation: &commoncap.ResponseOCRAttestation{ - ConfigDigest: configDigest, - SequenceNumber: seqNr, - Sigs: []commoncap.AttributedSignature{ - {Signer: 0, Signature: sig1}, - {Signer: 1, Signature: sig2}, - }, - }, + capResponse.OCRAttestation = &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + SequenceNumber: seqNr, + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, }, - Payload: payloadAsAny, - }) + } + + rawResponseWithAttestation, err := pb.MarshalCapabilityResponse(capResponse) require.NoError(t, err) ocrSigners := [][]byte{kb1.PublicKey(), kb2.PublicKey()} @@ -396,6 +400,9 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { require.NoError(t, receivedValue.UnwrapTo(&receivedMap)) assert.Equal(t, 42, receivedMap["number"]) + require.Len(t, capResponse.Metadata.Metering, 1) + require.Equal(t, spendUnit, capResponse.Metadata.Metering[0].SpendUnit) + require.Equal(t, spendValue, capResponse.Metadata.Metering[0].SpendValue) } t.Run("succeeds on first peer with valid attestation", func(t *testing.T) { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index e9bfe3fcad9..9fdf2fa2456 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -46,13 +46,13 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-data-streams v0.1.13 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.5 github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose v0.1.20 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 9bc70547b54..5f72cce830e 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1634,10 +1634,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4 h1:NOUsjsMzNecbjiPWUQGlRSRAutEvCFrqqyETDJeh5q4= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4/go.mod h1:Zpvul9sTcZNAZOVzt5vBl1XZGNvQebFpnpn3/KOQvOQ= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1672,8 +1672,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= diff --git a/deployment/go.mod b/deployment/go.mod index ae8310abb96..aa2a25d77d2 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -44,14 +44,14 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260224214816-cb23ec38649f github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317175207-e9ff89561326 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.0 github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260320011913-f2205f8506c7 diff --git a/deployment/go.sum b/deployment/go.sum index c796d23721a..5f3e94dfcdc 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1387,10 +1387,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317175207-e9ff github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317175207-e9ff89561326/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1425,8 +1425,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= diff --git a/go.mod b/go.mod index f10008ed69d..1032d07c021 100644 --- a/go.mod +++ b/go.mod @@ -85,8 +85,8 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260224214816-cb23ec38649f github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 github.com/smartcontractkit/chainlink-data-streams v0.1.13 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce @@ -97,7 +97,7 @@ require ( github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317132927-e8bc2c7b01f1 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4 - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.0 diff --git a/go.sum b/go.sum index 5b7f016d157..2348783cd2a 100644 --- a/go.sum +++ b/go.sum @@ -1235,10 +1235,10 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1271,8 +1271,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 h1:oli+2uLU6jcrJGCuYFqk3475hiwL17SWlITWLv+tx/w= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d2db3aa1a76..6cb8f1db5c4 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -47,8 +47,8 @@ require ( github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd @@ -513,7 +513,7 @@ require ( github.com/smartcontractkit/chainlink-protos/chainlink-ccv/heartbeat v0.0.0-20260115142640-f6b99095c12e // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 445a89e7154..642d529a789 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1626,10 +1626,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1664,8 +1664,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index ed297b1a8f8..baac30312db 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -27,7 +27,7 @@ require ( github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.3 @@ -475,7 +475,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20260317185256-d5f7db87ae70 // indirect github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70 // indirect github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 // indirect - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 // indirect + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.13 // indirect github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 // indirect @@ -490,7 +490,7 @@ require ( github.com/smartcontractkit/chainlink-protos/chainlink-ccv/heartbeat v0.0.0-20260115142640-f6b99095c12e // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 7188eea47ab..a32654c3a84 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1596,10 +1596,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1634,8 +1634,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index 0117348cdc3..32df4648729 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -32,12 +32,12 @@ require ( github.com/sethvargo/go-retry v0.3.0 github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260217043601-5cc966896c4f github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260320011913-f2205f8506c7 diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index ec2b6b948d9..9c33efdb383 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1601,10 +1601,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1639,8 +1639,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index c078fa1f33b..b142462d659 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -56,13 +56,13 @@ require ( github.com/rs/zerolog v1.34.0 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.97 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 - github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d github.com/smartcontractkit/chainlink-data-streams v0.1.13 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260128151123-605e9540b706 github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260217043601-5cc966896c4f diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index aa0985b13a9..4c6f74475f0 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1785,10 +1785,10 @@ github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260317185256-d5f7db87ae70/go.mod h1:P0/tjeeIIxfsBupk5MneRjq5uI9mj+ZQpMpYnFla6WM= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2 h1:5HdH/A6yn8INZAltYDLb7UkUi5IKemhJzJkDW4Bgxyg= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260324000441-d4cfddc9f7d2/go.mod h1:wDHq2E0KwUWG0lQ9f5frW1a7CKVW17MJLPuvKmtSRDg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573 h1:3Hu8r5XQ47aZG1i+PyN+h+tvKmr7tiU1178Noijw628= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260325182120-41d370c17573/go.mod h1:9W8E7tfchAsrSNHdMM1mzLmle+bL1P8Ou0I4LG1qNxw= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573 h1:QoDBe2gde71MzHd9fdm7LHtVhIkKkZ4LgQImyZ6hYfM= -github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260325182120-41d370c17573/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d h1:mAtk5TF1+aV6ZFpcrKh/kGEPlDr39UcZahASBAZOyFI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260327155511-2ee811e8103d/go.mod h1:6tlxlsiWypGdpaZI+Kz5gFm53gCAcU/pTU3PR9CiFB8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d h1:0wJn478TD2ZFsOUc4gL8WcCmQIrE5wLXmivc7kIPaDQ= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260327155511-2ee811e8103d/go.mod h1:vIX8ZkBosdvvVRJkxJV4qQjFXbRyDz/D2V/DJAv0lu8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4 h1:NOUsjsMzNecbjiPWUQGlRSRAutEvCFrqqyETDJeh5q4= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4/go.mod h1:Zpvul9sTcZNAZOVzt5vBl1XZGNvQebFpnpn3/KOQvOQ= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -1823,8 +1823,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4 h1:fkS5FJpSozwxL2FA6OJDi7az2DrtMNiK1X5DWuHDyfA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f h1:8p3vE987AHM3Of1JvnNJXNE/AtWtfNvJhk3TeeAG3Qw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= From 438a5c878bc1a865b580f83caff9b12f94e90eb5 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 27 Mar 2026 17:59:43 +0100 Subject: [PATCH 5/6] test fix --- .../remote/executable/request/client_request_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/capabilities/remote/executable/request/client_request_test.go b/core/capabilities/remote/executable/request/client_request_test.go index afa66a43dba..273e05203eb 100644 --- a/core/capabilities/remote/executable/request/client_request_test.go +++ b/core/capabilities/remote/executable/request/client_request_test.go @@ -249,7 +249,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, - workflowDonInfo, dispatcher, 10*time.Minute, nil, "") + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", nil) require.NoError(t, err) defer req.Cancel(errors.New("test end")) From 8d9bd0c0d99326b5a17c35f706c541e4a6bec882 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 27 Mar 2026 18:57:49 +0100 Subject: [PATCH 6/6] Proper error handing and OCRAttestation --- .../executable/request/client_request.go | 15 ++--- .../executable/request/client_request_test.go | 55 ++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/core/capabilities/remote/executable/request/client_request.go b/core/capabilities/remote/executable/request/client_request.go index 6d9a8a2baad..e78bf39b08c 100644 --- a/core/capabilities/remote/executable/request/client_request.go +++ b/core/capabilities/remote/executable/request/client_request.go @@ -383,13 +383,6 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err } } else { c.lggr.Debugw("received error from peer", "error", msg.Error, "errorMsg", msg.ErrorMsg, "peer", sender) - c.errorCount[msg.ErrorMsg]++ - c.totalErrorCount++ - - if len(c.errorCount) > 1 { - c.lggr.Warnw("received multiple different errors for the same request", "numDifferentErrors", len(c.errorCount)) - } - if commoncap.ErrResponsePayloadNotAvailable.Is(errors.New(msg.ErrorMsg)) { c.payloadNotAvailableCount++ if c.payloadNotAvailableCount == c.remoteNodeCount-c.requiredResponseConfirmations+1 { @@ -400,6 +393,13 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err return nil } + c.errorCount[msg.ErrorMsg]++ + c.totalErrorCount++ + + if len(c.errorCount) > 1 { + c.lggr.Warnw("received multiple different errors for the same request", "numDifferentErrors", len(c.errorCount)) + } + if c.errorCount[msg.ErrorMsg] == c.requiredResponseConfirmations { c.sendResponse(clientResponse{Err: newRemoteCapabilityExecuteError(msg.Error, msg.ErrorMsg)}) } else if c.totalErrorCount == c.remoteNodeCount-c.requiredResponseConfirmations+1 { @@ -480,6 +480,7 @@ func (c *ClientRequest) getMessageHash(msg commoncap.CapabilityResponse) ([32]by // clear metadata to ensure it doesn't affect the hash, as different nodes might have different metadata (e.g. different metering values) // since msg is passed as value, this won't affect the original message msg.Metadata = commoncap.ResponseMetadata{} + msg.OCRAttestation = nil payload, err := pb.MarshalCapabilityResponse(msg) if err != nil { return [32]byte{}, err diff --git a/core/capabilities/remote/executable/request/client_request_test.go b/core/capabilities/remote/executable/request/client_request_test.go index 273e05203eb..e9c25255938 100644 --- a/core/capabilities/remote/executable/request/client_request_test.go +++ b/core/capabilities/remote/executable/request/client_request_test.go @@ -453,7 +453,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { require.NoError(t, receivedValue.UnwrapTo(&receivedMap)) assert.Equal(t, 42, receivedMap["number"]) - require.Len(t, capResponse.Metadata.Metering, 1) + require.GreaterOrEqual(t, len(capResponse.Metadata.Metering), 1) require.Equal(t, spendUnit, capResponse.Metadata.Metering[0].SpendUnit) require.Equal(t, spendValue, capResponse.Metadata.Metering[0].SpendValue) } @@ -488,6 +488,59 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { response := <-req.ResponseChan() assertValidResponse(t, response.Result) }) + t.Run("attestation is not valid, but we fallback to identical responses", func(t *testing.T) { + ctx := t.Context() + + dispatcher := &clientRequestTestDispatcher{msgs: make(chan *types.MessageBody, 100)} + req, err := request.NewClientExecuteRequest(ctx, logger.Test(t), capabilityRequest, capInfo, + workflowDonInfo, dispatcher, 10*time.Minute, nil, "", ocrSigners) + require.NoError(t, err) + defer req.Cancel(errors.New("test end")) + + for range N { + <-dispatcher.msgs + } + + assert.Empty(t, dispatcher.msgs) + + for i := range F + 1 { + respInvalidAtt := commoncap.CapabilityResponse{ + Metadata: commoncap.ResponseMetadata{ + Metering: []commoncap.MeteringNodeDetail{ + {SpendUnit: spendUnit, SpendValue: spendValue}, + }, + }, + OCRAttestation: &commoncap.OCRAttestation{ + ConfigDigest: configDigest, + // make the sequence number invalid + SequenceNumber: seqNr + uint64(i) + 1, // #nosec G115 -- i is non-negative and within uint64 range + Sigs: []commoncap.AttributedSignature{ + {Signer: 0, Signature: sig1}, + {Signer: 1, Signature: sig2}, + }, + }, + Payload: payloadAsAny, + } + + rawRespInvalidAtt, err := pb.MarshalCapabilityResponse(respInvalidAtt) + require.NoError(t, err) + + msg := &types.MessageBody{ + CapabilityId: capInfo.ID, + CapabilityDonId: capDonInfo.ID, + CallerDonId: workflowDonInfo.ID, + Method: types.MethodExecute, + Payload: rawRespInvalidAtt, + MessageId: []byte("messageID"), + } + msg.Sender = capabilityPeers[i][:] + err = req.OnMessage(ctx, msg) + require.NoError(t, err) + } + + response := <-req.ResponseChan() + assertValidResponse(t, response.Result) + }) t.Run("2F peers return ErrResponsePayloadNotAvailable then success", func(t *testing.T) { ctx := t.Context()