-
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
Functions: tracking subscriptions #10613
Changes from 14 commits
a334b43
1af8420
d55b93e
9d352a7
cdd2a47
4530867
8206cf1
0ab8ee1
1c2477a
793c2c1
77d953c
c5ae2e5
fa7eb52
19aadcc
c54e357
a344936
13984f9
a235b39
ebed8ee
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 |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"crypto/ecdsa" | ||
"encoding/json" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/smartcontractkit/chainlink/v2/core/logger" | ||
"github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" | ||
|
@@ -21,31 +22,37 @@ import ( | |
type functionsConnectorHandler struct { | ||
utils.StartStopOnce | ||
|
||
connector connector.GatewayConnector | ||
signerKey *ecdsa.PrivateKey | ||
nodeAddress string | ||
storage s4.Storage | ||
allowlist functions.OnchainAllowlist | ||
rateLimiter *hc.RateLimiter | ||
lggr logger.Logger | ||
connector connector.GatewayConnector | ||
signerKey *ecdsa.PrivateKey | ||
nodeAddress string | ||
storage s4.Storage | ||
allowlist functions.OnchainAllowlist | ||
rateLimiter *hc.RateLimiter | ||
subscriptions functions.OnchainSubscriptions | ||
minBalanceWei *big.Int | ||
lggr logger.Logger | ||
} | ||
|
||
var ( | ||
_ connector.Signer = &functionsConnectorHandler{} | ||
_ connector.GatewayConnectorHandler = &functionsConnectorHandler{} | ||
) | ||
|
||
func NewFunctionsConnectorHandler(nodeAddress string, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, lggr logger.Logger) (*functionsConnectorHandler, error) { | ||
if signerKey == nil || storage == nil || allowlist == nil || rateLimiter == nil { | ||
return nil, fmt.Errorf("signerKey, storage, allowlist and rateLimiter must be non-nil") | ||
func NewFunctionsConnectorHandler(nodeAddress string, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions functions.OnchainSubscriptions, minimumBalance float64, lggr logger.Logger) (*functionsConnectorHandler, error) { | ||
if signerKey == nil || storage == nil || allowlist == nil || rateLimiter == nil || subscriptions == nil { | ||
return nil, fmt.Errorf("signerKey, storage, allowlist, rateLimiter and subscriptions must be non-nil") | ||
} | ||
minBalanceWei := new(big.Int) | ||
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. It's not Wei but Juels. |
||
big.NewFloat(0).Mul(big.NewFloat(minimumBalance), big.NewFloat(1e18)).Int(minBalanceWei) | ||
return &functionsConnectorHandler{ | ||
nodeAddress: nodeAddress, | ||
signerKey: signerKey, | ||
storage: storage, | ||
allowlist: allowlist, | ||
rateLimiter: rateLimiter, | ||
lggr: lggr.Named("FunctionsConnectorHandler"), | ||
nodeAddress: nodeAddress, | ||
signerKey: signerKey, | ||
storage: storage, | ||
allowlist: allowlist, | ||
rateLimiter: rateLimiter, | ||
subscriptions: subscriptions, | ||
minBalanceWei: minBalanceWei, | ||
lggr: lggr.Named("FunctionsConnectorHandler"), | ||
}, nil | ||
} | ||
|
||
|
@@ -69,6 +76,11 @@ func (h *functionsConnectorHandler) HandleGatewayMessage(ctx context.Context, ga | |
return | ||
} | ||
|
||
if balance, err := h.subscriptions.GetMaxUserBalance(fromAddr); err != nil || balance.Cmp(h.minBalanceWei) < 0 { | ||
h.lggr.Errorw("request is not backed with a funded subscription", "id", gatewayId, "address", fromAddr) | ||
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. Also log user balance and min required balance. |
||
return | ||
bolekk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
h.lggr.Debugw("handling gateway request", "id", gatewayId, "method", body.Method) | ||
|
||
switch body.Method { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"context" | ||
"encoding/json" | ||
"errors" | ||
"math/big" | ||
"time" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
|
@@ -20,7 +21,11 @@ import ( | |
type FunctionsHandlerConfig struct { | ||
OnchainAllowlistChainID string `json:"onchainAllowlistChainId"` | ||
// Not specifying OnchainAllowlist config disables allowlist checks | ||
OnchainAllowlist *OnchainAllowlistConfig `json:"onchainAllowlist"` | ||
OnchainAllowlist *OnchainAllowlistConfig `json:"onchainAllowlist"` | ||
OnchainSubscriptionsChainID string `json:"onchainSubscriptionsChainId"` | ||
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 think we can have a single chainID config field for both the allowlist and subscriptions. Maybe rename the existing one to just "ChainId"? |
||
// Not specifying OnchainSubscriptions config disables minimum balance checks | ||
OnchainSubscriptions *OnchainSubscriptionsConfig `json:"onchainSubscriptions"` | ||
MinimumSubscriptionBalanceLink float64 `json:"minimumSubscriptionBalanceLink"` | ||
// Not specifying RateLimiter config disables rate limiting | ||
UserRateLimiter *hc.RateLimiterConfig `json:"userRateLimiter"` | ||
NodeRateLimiter *hc.RateLimiterConfig `json:"nodeRateLimiter"` | ||
|
@@ -31,15 +36,17 @@ type FunctionsHandlerConfig struct { | |
type functionsHandler struct { | ||
utils.StartStopOnce | ||
|
||
handlerConfig FunctionsHandlerConfig | ||
donConfig *config.DONConfig | ||
don handlers.DON | ||
pendingRequests hc.RequestCache[PendingSecretsRequest] | ||
allowlist OnchainAllowlist | ||
userRateLimiter *hc.RateLimiter | ||
nodeRateLimiter *hc.RateLimiter | ||
chStop utils.StopChan | ||
lggr logger.Logger | ||
handlerConfig FunctionsHandlerConfig | ||
donConfig *config.DONConfig | ||
don handlers.DON | ||
pendingRequests hc.RequestCache[PendingSecretsRequest] | ||
allowlist OnchainAllowlist | ||
subscriptions OnchainSubscriptions | ||
minimumBalanceWei *big.Int | ||
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. Juels |
||
userRateLimiter *hc.RateLimiter | ||
nodeRateLimiter *hc.RateLimiter | ||
chStop utils.StopChan | ||
lggr logger.Logger | ||
} | ||
|
||
type PendingSecretsRequest struct { | ||
|
@@ -62,11 +69,11 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con | |
if cfg.OnchainAllowlist != nil { | ||
chain, err2 := legacyChains.Get(cfg.OnchainAllowlistChainID) | ||
if err2 != nil { | ||
return nil, err | ||
return nil, err2 | ||
} | ||
allowlist, err2 = NewOnchainAllowlist(chain.Client(), *cfg.OnchainAllowlist, lggr) | ||
if err2 != nil { | ||
return nil, err | ||
return nil, err2 | ||
bolekk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
var userRateLimiter, nodeRateLimiter *hc.RateLimiter | ||
|
@@ -82,8 +89,21 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con | |
return nil, err | ||
} | ||
} | ||
var subscriptions OnchainSubscriptions | ||
if cfg.OnchainSubscriptions != nil { | ||
chain, err2 := legacyChains.Get(cfg.OnchainSubscriptionsChainID) | ||
if err2 != nil { | ||
return nil, err2 | ||
} | ||
subscriptions, err2 = NewOnchainSubscriptions(chain.Client(), *cfg.OnchainSubscriptions, lggr) | ||
if err2 != nil { | ||
return nil, err2 | ||
} | ||
} | ||
minimumBalanceWei := new(big.Int) | ||
big.NewFloat(0).Mul(big.NewFloat(cfg.MinimumSubscriptionBalanceLink), big.NewFloat(1e18)).Int(minimumBalanceWei) | ||
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. Maybe better to have Juels in config? I'm worried about precision. 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. changed to what we use in other cases like this: assets.Link. |
||
pendingRequestsCache := hc.NewRequestCache[PendingSecretsRequest](time.Millisecond*time.Duration(cfg.RequestTimeoutMillis), cfg.MaxPendingRequests) | ||
return NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, userRateLimiter, nodeRateLimiter, lggr), nil | ||
return NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, minimumBalanceWei, userRateLimiter, nodeRateLimiter, lggr), nil | ||
} | ||
|
||
func NewFunctionsHandler( | ||
|
@@ -92,19 +112,23 @@ func NewFunctionsHandler( | |
don handlers.DON, | ||
pendingRequestsCache hc.RequestCache[PendingSecretsRequest], | ||
allowlist OnchainAllowlist, | ||
subscriptions OnchainSubscriptions, | ||
minimumBalanceWei *big.Int, | ||
userRateLimiter *hc.RateLimiter, | ||
nodeRateLimiter *hc.RateLimiter, | ||
lggr logger.Logger) handlers.Handler { | ||
return &functionsHandler{ | ||
handlerConfig: cfg, | ||
donConfig: donConfig, | ||
don: don, | ||
pendingRequests: pendingRequestsCache, | ||
allowlist: allowlist, | ||
userRateLimiter: userRateLimiter, | ||
nodeRateLimiter: nodeRateLimiter, | ||
chStop: make(utils.StopChan), | ||
lggr: lggr, | ||
handlerConfig: cfg, | ||
donConfig: donConfig, | ||
don: don, | ||
pendingRequests: pendingRequestsCache, | ||
allowlist: allowlist, | ||
subscriptions: subscriptions, | ||
minimumBalanceWei: minimumBalanceWei, | ||
userRateLimiter: userRateLimiter, | ||
nodeRateLimiter: nodeRateLimiter, | ||
chStop: make(utils.StopChan), | ||
lggr: lggr, | ||
} | ||
} | ||
|
||
|
@@ -118,6 +142,12 @@ func (h *functionsHandler) HandleUserMessage(ctx context.Context, msg *api.Messa | |
h.lggr.Debug("rate-limited", "sender", msg.Body.Sender) | ||
return errors.New("rate-limited") | ||
} | ||
if h.subscriptions != nil { | ||
if balance, err := h.subscriptions.GetMaxUserBalance(sender); err != nil || balance.Cmp(h.minimumBalanceWei) < 0 { | ||
h.lggr.Debug("received a message from a user having insufficient balance", "sender", msg.Body.Sender, "balance", balance.String()) | ||
return errors.New("sender has insufficient balance") | ||
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. Change the message to "insufficient subscription balance: [balance] [unit]" |
||
} | ||
} | ||
switch msg.Body.Method { | ||
case MethodSecretsSet, MethodSecretsList: | ||
return h.handleSecretsRequest(ctx, msg, callbackCh) | ||
|
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.
outdated