diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index 968d92172e2..53ec6b80345 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" @@ -483,7 +484,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) } @@ -574,7 +575,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 @@ -600,7 +601,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 @@ -907,7 +908,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, @@ -962,7 +963,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 80ad01e50da..f7f4a033b26 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/core/scripts/go.mod b/core/scripts/go.mod index 023e6862a1a..a9c61395dec 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.10.1-0.20260317233127-178dd2eeaa87 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc github.com/smartcontractkit/chainlink-data-streams v0.1.12 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260318010722-59d4165024f1 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index fc6f5475c55..e42a2f341e7 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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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 00fe58b1676..7fb2a90b099 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.10.1-0.20260317233127-178dd2eeaa87 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260318010722-59d4165024f1 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260107191744-4b93f62cffe3 diff --git a/deployment/go.sum b/deployment/go.sum index acdecd591f0..1b0ba199ed8 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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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 77430cbacfa..ac2a45262dc 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-20260317124520-6b2931b8cd0a - github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319174204-76aac329aab9 + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319174204-76aac329aab9 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 github.com/smartcontractkit/chainlink-data-streams v0.1.12 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260318010722-59d4165024f1 diff --git a/go.sum b/go.sum index 126dab1cb32..3158fd02dae 100644 --- a/go.sum +++ b/go.sum @@ -1235,10 +1235,14 @@ 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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319174204-76aac329aab9 h1:XXrWZYw1BTTr1XefDqwGC3sTY9ABbaOCjIpPcCU2Fgs= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319174204-76aac329aab9/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319174204-76aac329aab9 h1:fovezWPMPqGV9hVD7CE9euqslSGZXMsNd5/nWSdbiVU= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319174204-76aac329aab9/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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 126d6cf6d10..2cf677482ce 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -50,8 +50,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.10.1-0.20260317233127-178dd2eeaa87 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260318010722-59d4165024f1 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 4315e91a94f..207f2ef937d 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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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 b7c50bcbf76..f83a91cfafb 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -31,7 +31,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.10.1-0.20260317233127-178dd2eeaa87 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260318010722-59d4165024f1 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd @@ -483,7 +483,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-20260317124520-6b2931b8cd0a // indirect - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 // indirect + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.12 // 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 81fa9f6eff0..2e7e4099f89 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1604,10 +1604,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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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 969b2f67df9..5c5cf8deb41 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.10.1-0.20260317233127-178dd2eeaa87 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260318010722-59d4165024f1 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 248ce1eef6b..973d0f7e2f8 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1597,10 +1597,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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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 6476ffef3ef..044ae1ae1f3 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -54,8 +54,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.10.1-0.20260317233127-178dd2eeaa87 - github.com/smartcontractkit/chainlink-common/keystore v1.0.2 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc + github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc github.com/smartcontractkit/chainlink-data-streams v0.1.12 github.com/smartcontractkit/chainlink-deployments-framework v0.86.3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index cf24fa02749..a441d1db4fb 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1781,10 +1781,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-20260317124520-6b2931b8cd0a h1:6c6WDGfZB2ehsw9/nBuuKNCw89+rCav2k9so41pIu4o= github.com/smartcontractkit/chainlink-ccv v0.0.0-20260317124520-6b2931b8cd0a/go.mod h1:4+ngpFXBJrxcKR0jd2CUZFYJVtL8pPJfBXHbGfSNJeA= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87 h1:nvv1kiv/7jwALkFztO//NhIq4Y9M4kmJ0UCgTZMC/qI= -github.com/smartcontractkit/chainlink-common v0.10.1-0.20260317233127-178dd2eeaa87/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.10.1-0.20260319170028-82a189c9c1bc h1:bwCq/ZbKHZdAtHVRczprOw4/Zqudlt5dAppHcZXffds= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260319170028-82a189c9c1bc/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc h1:9gHDzMJxQz+fi4QKwjiLbeKUOZ43bte6RHL7MTiEFi8= +github.com/smartcontractkit/chainlink-common/keystore v1.0.3-0.20260319170028-82a189c9c1bc/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= 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=