-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
WS URL can be optional when LogBroadcaster is disabled #14364
base: develop
Are you sure you want to change the base?
Conversation
core/chains/evm/client/evm_client.go
Outdated
@@ -20,7 +20,7 @@ func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, cli | |||
var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] | |||
largePayloadRPCTimeout, defaultRPCTimeout := getRPCTimeouts(chainType) | |||
for i, node := range nodes { | |||
if node.SendOnly != nil && *node.SendOnly { | |||
if node.WSURL == nil || (node.SendOnly != nil && *node.SendOnly) { |
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.
A secondary or SendOnly node has limited functionality. It's only used to broadcast transactions (fire and forget), and we are not running health checks for it.
We should not change the type of the node. Instead, introduce changes to the RPC client.
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.
Moving the enforcement check to Dial()
at rpc_client.go
@@ -76,18 +85,37 @@ func NewClientConfigs( | |||
return chainConfig, nodePoolCfg, nodes, nil | |||
} | |||
|
|||
// verifyLogBroadcasterFlag checks node config and return error if LogBroadcaster enabled but some node missing WSURL | |||
func verifyLogBroadcasterFlag(nodes []*toml.Node, LogBroadcasterEnabled bool) error { |
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.
Config Builder is built for external applications like Atlas, where LogBroadcaster does not exist. The CL Node does not use it.
verifyLogBroadcasterFlag
should be moved to CL Node config validation, which is located in toml pkg
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.
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.
Not sure why you've moved the config validation to the rpc_client.go
, the link I've provided in my previous msg points to core/chains/evm/config/toml/config.go
.
The validation must be part of the config validation step to ensure CL Node won't start with the invalid configs
…BCFR-451/make-ws-url-optional
core/chains/evm/client/rpc_client.go
Outdated
@@ -174,6 +179,7 @@ func NewRPCClient( | |||
r.tier = tier | |||
r.ws.uri = wsuri | |||
r.finalizedBlockPollInterval = finalizedBlockPollInterval | |||
r.logBroadcasterEnabled = logBroadcasterEnabled | |||
if httpuri != nil { |
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.
RPC Client does not need to know about LogBroadcaster, this breaks abstraction
@@ -76,18 +85,37 @@ func NewClientConfigs( | |||
return chainConfig, nodePoolCfg, nodes, nil | |||
} | |||
|
|||
// verifyLogBroadcasterFlag checks node config and return error if LogBroadcaster enabled but some node missing WSURL | |||
func verifyLogBroadcasterFlag(nodes []*toml.Node, LogBroadcasterEnabled bool) error { |
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.
Not sure why you've moved the config validation to the rpc_client.go
, the link I've provided in my previous msg points to core/chains/evm/config/toml/config.go
.
The validation must be part of the config validation step to ensure CL Node won't start with the invalid configs
httpUrl := commonconfig.MustParseURL(*nodeCfg.HTTPURL) | ||
|
||
if nodeCfg.HTTPURL == nil { | ||
return nil, fmt.Errorf("node config [%d]: HTTP URL", i) |
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.
return nil, fmt.Errorf("node config [%d]: HTTP URL", i) | |
return nil, fmt.Errorf("node config [%d]: missing HTTP URL", i) |
httpURL := url.URL{} | ||
observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) | ||
rpcClient := client.NewRPCClient(observedLggr, url.URL{}, &httpURL, "rpc", 1, chainId, commonclient.Primary, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") |
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.
Could httpURL
be omitted?
httpURL := url.URL{} | |
observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) | |
rpcClient := client.NewRPCClient(observedLggr, url.URL{}, &httpURL, "rpc", 1, chainId, commonclient.Primary, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") | |
observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) | |
rpcClient := client.NewRPCClient(observedLggr, url.URL{}, nil, "rpc", 1, chainId, commonclient.Primary, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") |
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.
For this specific test it can't, since Dial() will fail due to errWSAndHTTPBothMissing
. I can add another test that covers when both WS and HTTPURL are empty
@@ -310,6 +313,12 @@ func (c *EVMConfig) ValidateConfig() (err error) { | |||
if len(c.Nodes) == 0 { | |||
err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) | |||
} else { | |||
if c.LogBroadcasterEnabled != nil { | |||
if err = verifyLogBroadcasterFlag(c.Nodes, *c.LogBroadcasterEnabled); err != nil { | |||
err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "must have at least one ws uri when LogBroadcaster is enabled"}) |
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.
err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "must have at least one ws uri when LogBroadcaster is enabled"}) | |
err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "must have at least one node with a WSURL when LogBroadcaster is enabled"}) |
- logbroadcaster cannot be enabled unless all nodes have WSURL provided | ||
- Nodes: missing: must have at least one ws uri when LogBroadcaster is enabled |
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.
These seem contradictory. Must all nodes have WSURL, or just one?
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.
Thanks for catching it. According to the requirement, I was intended to enforce Must all nodes have WSURL when LogBroadcaster is enabled
, so the second error message is incorrect. But I will double check if the requirement is correct, as it might not backward compatible
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.
Updated the error message. We going to require all primary nodes to provide valid WS URL when LogBroadcaster is enabled.
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.
Could you try starting the CL node locally without WS URL? It seems like in the current version, it should fail with nil pointer deference. Due to this
.changeset/silly-lies-boil.md
Outdated
"chainlink": minor | ||
--- | ||
|
||
Make websocket URL flag `WSURL` for `EVM.Nodes`, and apply logic so that: |
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.
Make websocket URL flag `WSURL` for `EVM.Nodes`, and apply logic so that: | |
Make websocket URL `WSURL` for `EVM.Nodes` optional, and apply logic so that: |
if nodeCfg.WSURL == nil || nodeCfg.HTTPURL == nil { | ||
return nil, fmt.Errorf("node config [%d]: missing WS or HTTP URL", i) | ||
var wsURL, httpURL *commonconfig.URL | ||
// wsUrl is optional, if LogBroadcaster is enabled, at least one wsURL is required and this will be checked in EVMConfig validation |
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.
LogBroadcaster
is not relevant to the context of evm/client
. It's one of many users, we should not reference it here.
lggr := r.rpcLog.With("wsuri", r.ws.uri.Redacted()) | ||
if r.http != nil { | ||
lggr = lggr.With("httpuri", r.http.uri.Redacted()) | ||
if r.ws.uri.String() == "" && r.http == nil { |
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.
WS is optional. Can't we make it a *url.URL
instead of url.URL
?
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.
We can, but it requires a lot refactor and adding nil ptr check and not sure if we want to do in this PR
core/chains/evm/client/rpc_client.go
Outdated
r.ws.rpc = wsrpc | ||
r.ws.geth = ethclient.NewClient(wsrpc) | ||
} else { | ||
lggr = lggr.With("wsuri", "empty WS URI") |
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.
There is no need to include empty was URI
into the log
@@ -45,6 +45,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { | |||
|
|||
chainId := big.NewInt(123456) | |||
lggr := logger.Test(t) | |||
var missingBothWSAndHTTPErr = errors.New("cannot dial rpc client when both ws and http info are missing") |
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.
We should try minimizing the lifespan of a variable. This one is only used in a single test case. There is no need to define it globally for the testing function.
require.Nil(t, rpcClient.Dial(ctx)) | ||
|
||
_, err := rpcClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) | ||
require.Error(t, err) |
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.
Could you ensure that we return proper error msg?
core/chains/evm/client/rpc_client.go
Outdated
@@ -75,6 +75,8 @@ var ( | |||
float64(8 * time.Second), | |||
}, | |||
}, []string{"evmChainID", "nodeName", "rpcHost", "isSendOnly", "success", "rpcCallName"}) | |||
errSubscribeFilterLogsNotAllowedWithoutWS = errors.New("SubscribeFilterLogs is not allowed without ws url") |
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.
Are we reusing these variables across multiple functions? If not, it might make sense to define them inline or make them public and use in tests
hasPrimary = true | ||
break | ||
|
||
if !hasPrimary { |
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: if statement is redundant as it's fine to set hasPrimary = true
if it's already true
|
||
// if the node is a primary node, then the WS URL is required when LogBroadcaster is enabled | ||
if logBroadcasterEnabled && (n.WSURL == nil || n.WSURL.IsZero()) { | ||
err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "all primary nodes must provide a valid WSURL when LogBroadcaster is enabled"}) |
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.
It might be a good idea to specify failed node index
Quality Gate failedFailed conditions See analysis details on SonarQube Catch issues before they fail your Quality Gate with our IDE extension SonarLint |
Description
WS URL can be optional (empty string) when LogBroadcaster is disabled. If WS URL was not provided, SubscribeFilterLogs should fail with an explicit error
Tickets:
BCFR-451