Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CAPPL-217] expose workflow key to clo #15287

Merged
merged 8 commits into from
Dec 3, 2024
1 change: 1 addition & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ packages:
config:
filename: starknet.go
VRF:
Workflow:
github.com/smartcontractkit/chainlink/v2/core/services/ocr:
interfaces:
OCRContractTrackerDB:
Expand Down
2 changes: 1 addition & 1 deletion core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ require (
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241114154055-8d29ea018b57 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.2 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241127201057-3c9282e39749 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
Expand Down
4 changes: 2 additions & 2 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1104,8 +1104,8 @@ github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6An
github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.2 h1:onBe3DqNrbtOAzKS4PrPIiJX65BGo1aYiYZxFVEW+jc=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.2/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241127201057-3c9282e39749 h1:gkrjGJAtbKMOliJPaZ73EyJmO8AyDVi80+PEJocRMn4=
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241127201057-3c9282e39749/go.mod h1:nkIegLHodyrrZguxkYEHcNw2vAXv8H8xlCoLzwylcL0=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA=
Expand Down
18 changes: 18 additions & 0 deletions core/services/feeds/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ type service struct {
p2pKeyStore keystore.P2P
ocr1KeyStore keystore.OCR
ocr2KeyStore keystore.OCR2
workflowKeyStore keystore.Workflow
jobSpawner job.Spawner
gCfg GeneralConfig
featCfg FeatureConfig
Expand Down Expand Up @@ -170,6 +171,7 @@ func NewService(
csaKeyStore: keyStore.CSA(),
ocr1KeyStore: keyStore.OCR(),
ocr2KeyStore: keyStore.OCR2(),
workflowKeyStore: keyStore.Workflow(),
gCfg: gCfg,
featCfg: fCfg,
insecureCfg: insecureCfg,
Expand Down Expand Up @@ -277,9 +279,11 @@ func (s *service) SyncNodeInfo(ctx context.Context, id int64) error {
cfgMsgs = append(cfgMsgs, cfgMsg)
}

workflowKey := s.getWorkflowPublicKey()
if _, err = fmsClient.UpdateNode(ctx, &pb.UpdateNodeRequest{
Version: s.version,
ChainConfigs: cfgMsgs,
WorkflowKey: &workflowKey,
}); err != nil {
return err
}
Expand Down Expand Up @@ -1152,6 +1156,20 @@ func (s *service) getCSAPrivateKey() (privkey []byte, err error) {
return keys[0].Raw(), nil
}

// getWorkflowPublicKey retrieves the server's Workflow public key.
// Since there will be at most one key, it returns the first key found.
// If an error occurs or no keys are found, it returns blank.
func (s *service) getWorkflowPublicKey() string {
keys, err := s.workflowKeyStore.GetAll()
if err != nil {
return ""
}
if len(keys) < 1 {
return ""
}
agparadiso marked this conversation as resolved.
Show resolved Hide resolved
return keys[0].PublicKeyString()
}

// observeJobProposalCounts is a helper method that queries the repository for the count of
// job proposals by status and then updates prometheus gauges.
func (s *service) observeJobProposalCounts(ctx context.Context) error {
Expand Down
89 changes: 59 additions & 30 deletions core/services/feeds/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocrkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey"
ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
"github.com/smartcontractkit/chainlink/v2/core/services/versioning"
Expand Down Expand Up @@ -183,16 +184,17 @@ chainID = 1337

type TestService struct {
feeds.Service
orm *mocks.ORM
jobORM *jobmocks.ORM
connMgr *mocks.ConnectionsManager
spawner *jobmocks.Spawner
fmsClient *mocks.FeedsManagerClient
csaKeystore *ksmocks.CSA
p2pKeystore *ksmocks.P2P
ocr1Keystore *ksmocks.OCR
ocr2Keystore *ksmocks.OCR2
legacyChains legacyevm.LegacyChainContainer
orm *mocks.ORM
jobORM *jobmocks.ORM
connMgr *mocks.ConnectionsManager
spawner *jobmocks.Spawner
fmsClient *mocks.FeedsManagerClient
csaKeystore *ksmocks.CSA
p2pKeystore *ksmocks.P2P
ocr1Keystore *ksmocks.OCR
ocr2Keystore *ksmocks.OCR2
workflowKeystore *ksmocks.Workflow
legacyChains legacyevm.LegacyChainContainer
}

func setupTestService(t *testing.T) *TestService {
Expand All @@ -205,15 +207,16 @@ func setupTestServiceCfg(t *testing.T, overrideCfg func(c *chainlink.Config, s *
t.Helper()

var (
orm = mocks.NewORM(t)
jobORM = jobmocks.NewORM(t)
connMgr = mocks.NewConnectionsManager(t)
spawner = jobmocks.NewSpawner(t)
fmsClient = mocks.NewFeedsManagerClient(t)
csaKeystore = ksmocks.NewCSA(t)
p2pKeystore = ksmocks.NewP2P(t)
ocr1Keystore = ksmocks.NewOCR(t)
ocr2Keystore = ksmocks.NewOCR2(t)
orm = mocks.NewORM(t)
jobORM = jobmocks.NewORM(t)
connMgr = mocks.NewConnectionsManager(t)
spawner = jobmocks.NewSpawner(t)
fmsClient = mocks.NewFeedsManagerClient(t)
csaKeystore = ksmocks.NewCSA(t)
p2pKeystore = ksmocks.NewP2P(t)
ocr1Keystore = ksmocks.NewOCR(t)
ocr2Keystore = ksmocks.NewOCR2(t)
workflowKeystore = ksmocks.NewWorkflow(t)
)

lggr := logger.TestLogger(t)
Expand All @@ -229,21 +232,23 @@ func setupTestServiceCfg(t *testing.T, overrideCfg func(c *chainlink.Config, s *
keyStore.On("P2P").Return(p2pKeystore)
keyStore.On("OCR").Return(ocr1Keystore)
keyStore.On("OCR2").Return(ocr2Keystore)
keyStore.On("Workflow").Return(workflowKeystore)
svc := feeds.NewService(orm, jobORM, db, spawner, keyStore, gcfg, gcfg.Feature(), gcfg.Insecure(), gcfg.JobPipeline(), gcfg.OCR(), gcfg.OCR2(), legacyChains, lggr, "1.0.0", nil)
svc.SetConnectionsManager(connMgr)

return &TestService{
Service: svc,
orm: orm,
jobORM: jobORM,
connMgr: connMgr,
spawner: spawner,
fmsClient: fmsClient,
csaKeystore: csaKeystore,
p2pKeystore: p2pKeystore,
ocr1Keystore: ocr1Keystore,
ocr2Keystore: ocr2Keystore,
legacyChains: legacyChains,
Service: svc,
orm: orm,
jobORM: jobORM,
connMgr: connMgr,
spawner: spawner,
fmsClient: fmsClient,
csaKeystore: csaKeystore,
p2pKeystore: p2pKeystore,
ocr1Keystore: ocr1Keystore,
ocr2Keystore: ocr2Keystore,
workflowKeystore: workflowKeystore,
legacyChains: legacyChains,
}
}

Expand Down Expand Up @@ -613,10 +618,15 @@ func Test_Service_CreateChainConfig(t *testing.T) {
svc = setupTestService(t)
)

workflowKey, err := workflowkey.New()
require.NoError(t, err)
svc.workflowKeystore.On("GetAll").Return([]workflowkey.Key{workflowKey}, nil)

svc.orm.On("CreateChainConfig", mock.Anything, cfg).Return(int64(1), nil)
svc.orm.On("GetManager", mock.Anything, mgr.ID).Return(&mgr, nil)
svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil)
svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{cfg}, nil)
wkID := workflowKey.ID()
svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{
Version: nodeVersion.Version,
ChainConfigs: []*proto.ChainConfig{
Expand All @@ -633,6 +643,7 @@ func Test_Service_CreateChainConfig(t *testing.T) {
Ocr2Config: &proto.OCR2Config{Enabled: false},
},
},
WorkflowKey: &wkID,
}).Return(&proto.UpdateNodeResponse{}, nil)

actual, err := svc.CreateChainConfig(testutils.Context(t), cfg)
Expand Down Expand Up @@ -677,14 +688,20 @@ func Test_Service_DeleteChainConfig(t *testing.T) {
svc = setupTestService(t)
)

workflowKey, err := workflowkey.New()
require.NoError(t, err)
svc.workflowKeystore.On("GetAll").Return([]workflowkey.Key{workflowKey}, nil)

svc.orm.On("GetChainConfig", mock.Anything, cfg.ID).Return(&cfg, nil)
svc.orm.On("DeleteChainConfig", mock.Anything, cfg.ID).Return(cfg.ID, nil)
svc.orm.On("GetManager", mock.Anything, mgr.ID).Return(&mgr, nil)
svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil)
svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{}, nil)
wkID := workflowKey.ID()
svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{
Version: nodeVersion.Version,
ChainConfigs: []*proto.ChainConfig{},
WorkflowKey: &wkID,
}).Return(&proto.UpdateNodeResponse{}, nil)

actual, err := svc.DeleteChainConfig(testutils.Context(t), cfg.ID)
Expand Down Expand Up @@ -762,10 +779,15 @@ func Test_Service_UpdateChainConfig(t *testing.T) {
svc = setupTestService(t)
)

workflowKey, err := workflowkey.New()
require.NoError(t, err)
svc.workflowKeystore.On("GetAll").Return([]workflowkey.Key{workflowKey}, nil)

svc.orm.On("UpdateChainConfig", mock.Anything, cfg).Return(int64(1), nil)
svc.orm.On("GetChainConfig", mock.Anything, cfg.ID).Return(&cfg, nil)
svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil)
svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{cfg}, nil)
wkID := workflowKey.ID()
svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{
Version: nodeVersion.Version,
ChainConfigs: []*proto.ChainConfig{
Expand All @@ -782,6 +804,7 @@ func Test_Service_UpdateChainConfig(t *testing.T) {
Ocr2Config: &proto.OCR2Config{Enabled: false},
},
},
WorkflowKey: &wkID,
}).Return(&proto.UpdateNodeResponse{}, nil)

actual, err := svc.UpdateChainConfig(testutils.Context(t), cfg)
Expand Down Expand Up @@ -1662,6 +1685,9 @@ func Test_Service_SyncNodeInfo(t *testing.T) {
ocrKey, err := ocrkey.NewV2()
require.NoError(t, err)

workflowKey, err := workflowkey.New()
require.NoError(t, err)

var (
multiaddr = "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"
mgr = &feeds.FeedsManager{ID: 1}
Expand Down Expand Up @@ -1709,6 +1735,8 @@ func Test_Service_SyncNodeInfo(t *testing.T) {
svc.p2pKeystore.On("Get", p2pKey.PeerID()).Return(p2pKey, nil)
svc.ocr1Keystore.On("Get", ocrKey.GetID()).Return(ocrKey, nil)

svc.workflowKeystore.On("GetAll").Return([]workflowkey.Key{workflowKey}, nil)
wkID := workflowKey.ID()
svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{
Version: nodeVersion.Version,
ChainConfigs: []*proto.ChainConfig{
Expand Down Expand Up @@ -1749,6 +1777,7 @@ func Test_Service_SyncNodeInfo(t *testing.T) {
},
},
},
WorkflowKey: &wkID,
}).Return(&proto.UpdateNodeResponse{}, nil)

err = svc.SyncNodeInfo(testutils.Context(t), mgr.ID)
Expand Down
16 changes: 16 additions & 0 deletions core/services/keystore/keys/workflowkey/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,18 @@ func New() (Key, error) {
}

func (k Key) PublicKey() [curve25519.PointSize]byte {
agparadiso marked this conversation as resolved.
Show resolved Hide resolved
if k.publicKey == nil {
return [curve25519.PointSize]byte{}
}

return *k.publicKey
}

func (k Key) PublicKeyString() string {
if k.publicKey == nil {
return ""
}

return hex.EncodeToString(k.publicKey[:])
}

Expand All @@ -78,6 +86,10 @@ func (k Key) GoString() string {
// Encrypt encrypts a message using the public key
func (k Key) Encrypt(plaintext []byte) ([]byte, error) {
publicKey := k.PublicKey()
if publicKey == [curve25519.PointSize]byte{} {
return nil, errors.New("public key is empty")
}

encrypted, err := box.SealAnonymous(nil, plaintext, &publicKey, cryptorand.Reader)
if err != nil {
return nil, err
Expand All @@ -89,6 +101,10 @@ func (k Key) Encrypt(plaintext []byte) ([]byte, error) {
// Decrypt decrypts a message that was encrypted using the private key
func (k Key) Decrypt(ciphertext []byte) (plaintext []byte, err error) {
publicKey := k.PublicKey()
if publicKey == [curve25519.PointSize]byte{} {
return nil, errors.New("public key is empty")
}

decrypted, success := box.OpenAnonymous(nil, ciphertext, &publicKey, k.privateKey)
if !success {
return nil, errors.New("decryption failed")
Expand Down
Loading
Loading