Skip to content

Commit

Permalink
feat(relay): check account balance is greater than a configured amount (
Browse files Browse the repository at this point in the history
#194)

* feat(relay): check account balance is greater than a configurable amount, rather than just >0

Right now, we relay messages for accouts with any balance. This fails to sybil-protect the relay
system as an attacker could send 1 wei to hundreds of accounts. This commit adds a field to the
relay server configuration specifying the minimum balance an account must have to get its messages
relayed.

* refactor(relay): edit minAccountBalance help message for consistency

* feat(relay): make minAccountBalance an uint64 instead of an int64

Co-authored-by: authcall <authcall@proton.me>
  • Loading branch information
therealbytes and authcall authored Oct 11, 2022
1 parent f230887 commit 1cbbd5b
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 17 deletions.
4 changes: 3 additions & 1 deletion packages/services/cmd/ecs-relay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ var (
idleTimeoutTime = flag.Int("idle-timeout-time", 30, "Time in seconds after which a client connection times out. Defaults to 30s")
idleDisconnectIterval = flag.Int("idle-disconnect-interval", 60, "Time in seconds for how oftern to disconnect idle clients. Defaults to 60s")
messsageDriftTime = flag.Int("message-drift-time", 5, "Time in seconds that is acceptable as drift before message is not relayed. Defaults to 5s")
minAccountBalance = flag.Uint64("min-account-balance", 1000000000000000, "Minimum balance in wei for an account to get its messages relayed. Defaults to 0.001 ETH")
verifyMessageSignature = flag.Bool("verify-msg-sig", false, "Whether to service-side verify the signature on each relayed message. Defaults to false.")
verifyAccountBalance = flag.Bool("verify-account-balance", false, "Whether to service-side verify that the account has balance when relaying message. Defaults to false.")
verifyAccountBalance = flag.Bool("verify-account-balance", false, "Whether to service-side verify that the account has sufficient balance when relaying message. Defaults to false.")
messageRateLimit = flag.Int("msg-rate-limit", 10, "Rate limit for messages per second that a single client can push to be relayed. Defaults to 10")
metricsPort = flag.Int("metrics-port", 6060, "Prometheus metrics http handler port. Defaults to port 6060")
)
Expand All @@ -35,6 +36,7 @@ func main() {
IdleTimeoutTime: *idleTimeoutTime,
IdleDisconnectIterval: *idleDisconnectIterval,
MessageDriftTime: *messsageDriftTime,
MinAccountBalance: *minAccountBalance,
VerifyMessageSignature: *verifyMessageSignature,
VerifyAccountBalance: *verifyAccountBalance,
MessageRateLimit: *messageRateLimit,
Expand Down
4 changes: 2 additions & 2 deletions packages/services/pkg/grpc/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,13 +299,13 @@ func (server *ecsRelayServer) VerifySufficientBalance(client *relay.Client, addr
server.logger.Info("fetched up-to-date balance for account", zap.String("address", address), zap.Uint64("balance", balance))

// Update the "cached" balance on the client, which helps us know whether to keep checking or not.
client.SetHasBalance(balance > 0)
client.SetHasSufficientBalance(balance > server.config.MinAccountBalance)

if balance == 0 {
return fmt.Errorf("client with address %s has insufficient balance to push messages via relay", address)
}
} else {
if !client.HasBalance() {
if !client.HasSufficientBalance() {
return fmt.Errorf("client with address %s has insufficient balance as of last check", address)
}
}
Expand Down
29 changes: 15 additions & 14 deletions packages/services/pkg/relay/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type RelayServerConfig struct {
IdleTimeoutTime int
IdleDisconnectIterval int
MessageDriftTime int
MinAccountBalance uint64

VerifyMessageSignature bool
VerifyAccountBalance bool
Expand All @@ -28,10 +29,10 @@ type Client struct {
connected bool
latestPingTimestamp int64

balancePresent bool
balancePresentLimiter *rate.Limiter
balanceNotPresentLimiter *rate.Limiter
messageRateLimiter *rate.Limiter
sufficientBalance bool
sufficientBalanceLimiter *rate.Limiter
insufficientBalanceLimiter *rate.Limiter
messageRateLimiter *rate.Limiter

mutex sync.Mutex
}
Expand Down Expand Up @@ -78,19 +79,19 @@ func (client *Client) GetLimiter() *rate.Limiter {
return client.messageRateLimiter
}

func (client *Client) HasBalance() bool {
return client.balancePresent
func (client *Client) HasSufficientBalance() bool {
return client.sufficientBalance
}

func (client *Client) SetHasBalance(hasBalance bool) {
client.balancePresent = hasBalance
func (client *Client) SetHasSufficientBalance(hasSufficientBalance bool) {
client.sufficientBalance = hasSufficientBalance
}

func (client *Client) ShouldCheckBalance() bool {
if client.balancePresent {
return client.balancePresentLimiter.Allow()
if client.sufficientBalance {
return client.sufficientBalanceLimiter.Allow()
} else {
return client.balanceNotPresentLimiter.Allow()
return client.insufficientBalanceLimiter.Allow()
}
}

Expand Down Expand Up @@ -179,10 +180,10 @@ func (registry *ClientRegistry) Register(identity *pb.Identity, config *RelaySer
newClient.messageRateLimiter = rate.NewLimiter(rate.Every(1*time.Second/time.Duration(config.MessageRateLimit)), config.MessageRateLimit)

// At most allow a check for balance every 60s if client has funds and every 10s if not.
newClient.balancePresentLimiter = rate.NewLimiter(rate.Limit(float64(1)/float64(60)), 1)
newClient.balanceNotPresentLimiter = rate.NewLimiter(rate.Limit(float64(1)/float64(10)), 1)
newClient.sufficientBalanceLimiter = rate.NewLimiter(rate.Limit(float64(1)/float64(60)), 1)
newClient.insufficientBalanceLimiter = rate.NewLimiter(rate.Limit(float64(1)/float64(10)), 1)

newClient.balancePresent = false
newClient.sufficientBalance = false

registry.clients = append(registry.clients, newClient)

Expand Down

0 comments on commit 1cbbd5b

Please sign in to comment.