-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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-19] Gateway Connector service wrapper #14356
Changes from 2 commits
e951bdb
d4ce0db
7784141
ac908dd
cb3088d
b5bafec
79d6e4c
a2d5ab6
be130db
ef63be6
2b66be0
6519107
493f94c
cfaf0ca
3b1e3ee
6b68bff
f945bd2
3971608
1cfa38f
5ac44fa
8f26d15
c3f5616
558f577
f2f8d9c
e119169
d572c97
5e0ae26
d4e0068
e256b2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,151 @@ | ||||||
package gateway_connector | ||||||
|
||||||
import ( | ||||||
"context" | ||||||
"crypto/ecdsa" | ||||||
"errors" | ||||||
"math/big" | ||||||
"reflect" | ||||||
"slices" | ||||||
|
||||||
"github.com/ethereum/go-ethereum/common" | ||||||
"github.com/jonboulle/clockwork" | ||||||
|
||||||
"github.com/smartcontractkit/chainlink-common/pkg/services" | ||||||
"github.com/smartcontractkit/chainlink/v2/core/config" | ||||||
gwConfig "github.com/smartcontractkit/chainlink/v2/core/config" | ||||||
"github.com/smartcontractkit/chainlink/v2/core/logger" | ||||||
gwCommon "github.com/smartcontractkit/chainlink/v2/core/services/gateway/common" | ||||||
"github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" | ||||||
"github.com/smartcontractkit/chainlink/v2/core/services/gateway/network" | ||||||
"github.com/smartcontractkit/chainlink/v2/core/services/keystore" | ||||||
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" | ||||||
) | ||||||
|
||||||
type serviceWrapper struct { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realized that the Wrapper also needs to have a getter for the underlying Connector object (we need to extract it once it's created). Please add it and also make ServiceWrapper public (uppercase) so we can pass it around in application.go. |
||||||
services.StateMachine | ||||||
|
||||||
config *config.GatewayConnector | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No pointer here - GatewayConnector in an interface. |
||||||
keystore keystore.Eth | ||||||
connector connector.GatewayConnector | ||||||
lggr logger.Logger | ||||||
} | ||||||
|
||||||
type workflowConnectorSigner struct { | ||||||
services.StateMachine | ||||||
|
||||||
connector connector.GatewayConnector | ||||||
signerKey *ecdsa.PrivateKey | ||||||
nodeAddress string | ||||||
lggr logger.Logger | ||||||
} | ||||||
|
||||||
var ( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: you can simplify to one line by removing the parentheses |
||||||
_ connector.Signer = &workflowConnectorSigner{} | ||||||
) | ||||||
|
||||||
func NewWorkflowConnectorSigner(config *config.GatewayConnector, signerKey *ecdsa.PrivateKey, lggr logger.Logger) (*workflowConnectorSigner, error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's remove the word "workflow" from everywhere. Just ConnectorSigner. |
||||||
return &workflowConnectorSigner{ | ||||||
nodeAddress: (*config).NodeAddress(), | ||||||
signerKey: signerKey, | ||||||
lggr: lggr.Named("WorkflowConnectorSigner"), | ||||||
}, nil | ||||||
} | ||||||
|
||||||
func (h *workflowConnectorSigner) Sign(data ...[]byte) ([]byte, error) { | ||||||
return gwCommon.SignData(h.signerKey, data...) | ||||||
} | ||||||
|
||||||
func translateConfigs(f config.GatewayConnector) connector.ConnectorConfig { | ||||||
r := connector.ConnectorConfig{} | ||||||
if f.NodeAddress != nil { | ||||||
r.NodeAddress = f.NodeAddress() | ||||||
} | ||||||
|
||||||
if f.DonID != nil { | ||||||
r.DonId = f.DonID() | ||||||
} | ||||||
|
||||||
if f.Gateways != nil { | ||||||
r.Gateways = make([]connector.ConnectorGatewayConfig, len(r.Gateways)) | ||||||
for index, element := range f.Gateways() { | ||||||
r.Gateways[index] = connector.ConnectorGatewayConfig{Id: element.ID(), URL: element.URL()} | ||||||
} | ||||||
} | ||||||
|
||||||
if !reflect.ValueOf(f.WSHandshakeTimeoutMillis).IsZero() { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||||||
r.WsClientConfig = network.WebSocketClientConfig{HandshakeTimeoutMillis: f.WSHandshakeTimeoutMillis()} | ||||||
} | ||||||
|
||||||
if f.AuthMinChallengeLen != nil { | ||||||
r.AuthMinChallengeLen = f.AuthMinChallengeLen() | ||||||
} | ||||||
|
||||||
if f.AuthTimestampToleranceSec != nil { | ||||||
r.AuthTimestampToleranceSec = f.AuthTimestampToleranceSec() | ||||||
} | ||||||
return r | ||||||
} | ||||||
|
||||||
// NOTE: this wrapper is needed to make sure that our services are started after Keystore. | ||||||
func NewGatewayConnectorServiceWrapper(config *gwConfig.GatewayConnector, keystore keystore.Eth, lggr logger.Logger) *serviceWrapper { | ||||||
return &serviceWrapper{ | ||||||
config: config, | ||||||
keystore: keystore, | ||||||
lggr: lggr, | ||||||
} | ||||||
} | ||||||
|
||||||
func (e *serviceWrapper) Start(ctx context.Context) error { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a unit test that creates the ServiceWrapper object and then calls Start() can Close() on it, please? Ideally you'd test valid NodeAddress and an invalid one (i.e. key doesn't exit). |
||||||
return e.StartOnce("GatewayConnectorServiceWrapper", func() error { | ||||||
conf := *e.config | ||||||
e.lggr.Infow("Starting GatewayConnectorServiceWrapper", "chainID", conf.ChainIDForNodeKey()) | ||||||
chainId, _ := new(big.Int).SetString(conf.ChainIDForNodeKey(), 0) | ||||||
enabledKeys, err := e.keystore.EnabledKeysForChain(ctx, chainId) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
if len(enabledKeys) == 0 { | ||||||
return errors.New("no available keys found") | ||||||
} | ||||||
configuredNodeAddress := common.HexToAddress(conf.NodeAddress()) | ||||||
idx := slices.IndexFunc(enabledKeys, func(key ethkey.KeyV2) bool { return key.Address == configuredNodeAddress }) | ||||||
if idx == -1 { | ||||||
return errors.New("key for configured node address not found") | ||||||
} | ||||||
signerKey := enabledKeys[idx].ToEcdsaPrivKey() | ||||||
if enabledKeys[idx].ID() != conf.NodeAddress() { | ||||||
return errors.New("node address mismatch") | ||||||
} | ||||||
|
||||||
signer, err := NewWorkflowConnectorSigner(e.config, signerKey, e.lggr) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
translated := translateConfigs(conf) | ||||||
// cannot use signer (variable of type *workflowConnectorSigner) as connector.GatewayConnectorHandler value in argument to connector.NewGatewayConnector: *workflowConnectorSigner does not implement connector.GatewayConnectorHandler (missing method Close)compilerInvalidIfaceAssign | ||||||
e.connector, err = connector.NewGatewayConnector(&translated, signer, signer, clockwork.NewRealClock(), e.lggr) | ||||||
Check failure on line 127 in core/capabilities/gateway_connector/service_wrapper.go
|
||||||
bolekk marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed the signature of NewGatewayConnector() in this PR: #14367 |
||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
return e.connector.Start(ctx) | ||||||
}) | ||||||
} | ||||||
|
||||||
func (e *serviceWrapper) Close() error { | ||||||
return e.StopOnce("GatewayConnectorServiceWrapper", func() (err error) { | ||||||
return e.connector.Close() | ||||||
}) | ||||||
} | ||||||
|
||||||
func (e *serviceWrapper) Ready() error { | ||||||
return nil | ||||||
} | ||||||
|
||||||
func (e *serviceWrapper) HealthReport() map[string]error { | ||||||
return nil | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be thorough, how about filling this in from
Suggested change
|
||||||
} | ||||||
|
||||||
func (e *serviceWrapper) Name() string { | ||||||
return "GatewayConnectorServiceWrapper" | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: stick to go conventional package naming
https://go.dev/blog/package-names