Skip to content

Commit e5100b2

Browse files
committed
backend: replace ratelimiter with x/time/rate.
Removes the ratelimiter package and replaces its usages with x/time/rate.
1 parent 4815ca3 commit e5100b2

File tree

50 files changed

+1658
-458
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1658
-458
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- Display the hide amount button by default and remove its settings
1010
- Linux: add support for Wayland
1111
- Fix the copy buttons in the Pocket order confirmation page
12+
- Android: handle device disconnect while the app is in the background
13+
- Improve send result view show relevant infos and options to make a new transaction or go back
1214

1315
# 4.46.3
1416
- Fix camera access on linux

backend/backend.go

+13-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import (
5151
"github.com/BitBoxSwiss/bitbox-wallet-app/util/logging"
5252
"github.com/BitBoxSwiss/bitbox-wallet-app/util/observable"
5353
"github.com/BitBoxSwiss/bitbox-wallet-app/util/observable/action"
54-
"github.com/BitBoxSwiss/bitbox-wallet-app/util/ratelimit"
5554
"github.com/BitBoxSwiss/bitbox-wallet-app/util/socksproxy"
5655
"github.com/btcsuite/btcd/chaincfg"
5756
"github.com/ethereum/go-ethereum/params"
@@ -193,6 +192,8 @@ type Backend struct {
193192

194193
devices map[string]device.Interface
195194

195+
usbManager *usb.Manager
196+
196197
accountsAndKeystoreLock locker.Locker
197198
accounts AccountsList
198199
// keystore is nil if no keystore is connected.
@@ -277,7 +278,7 @@ func NewBackend(arguments *arguments.Arguments, environment Environment) (*Backe
277278
backend.notifier = notifier
278279
backend.socksProxy = backendProxy
279280
backend.httpClient = hclient
280-
backend.etherScanHTTPClient = ratelimit.FromTransport(hclient.Transport, etherscan.CallInterval)
281+
backend.etherScanHTTPClient = hclient
281282

282283
ratesCache := filepath.Join(arguments.CacheDirectoryPath(), "exchangerates")
283284
if err := os.MkdirAll(ratesCache, 0700); err != nil {
@@ -612,13 +613,14 @@ func (backend *Backend) OnDeviceUninit(f func(string)) {
612613
// Start starts the background services. It returns a channel of events to handle by the library
613614
// client.
614615
func (backend *Backend) Start() <-chan interface{} {
615-
usb.NewManager(
616+
backend.usbManager = usb.NewManager(
616617
backend.arguments.MainDirectoryPath(),
617618
backend.arguments.BitBox02DirectoryPath(),
618619
backend.socksProxy,
619620
backend.environment.DeviceInfos,
620621
backend.Register,
621-
backend.Deregister).Start()
622+
backend.Deregister)
623+
backend.usbManager.Start()
622624

623625
httpClient, err := backend.socksProxy.GetHTTPClient()
624626
if err != nil {
@@ -638,6 +640,13 @@ func (backend *Backend) Start() <-chan interface{} {
638640
return backend.events
639641
}
640642

643+
// UsbUpdate triggers a scan of the USB devices to detect connects/disconnects.
644+
func (backend *Backend) UsbUpdate() {
645+
if backend.usbManager != nil {
646+
backend.usbManager.Update()
647+
}
648+
}
649+
641650
// DevicesRegistered returns a map of device IDs to device of registered devices.
642651
func (backend *Backend) DevicesRegistered() map[string]device.Interface {
643652
return backend.devices

backend/bridgecommon/bridgecommon.go

+10
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,13 @@ func Shutdown() {
359359
log.Info("Shutdown called, but backend not running")
360360
}
361361
}
362+
363+
// UsbUpdate wraps backend.UsbUpdate.
364+
func UsbUpdate() {
365+
mu.RLock()
366+
defer mu.RUnlock()
367+
if globalBackend == nil {
368+
return
369+
}
370+
globalBackend.UsbUpdate()
371+
}

backend/coins/eth/etherscan/etherscan.go

+25-18
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ import (
3535
"github.com/ethereum/go-ethereum/common"
3636
"github.com/ethereum/go-ethereum/common/hexutil"
3737
"github.com/ethereum/go-ethereum/core/types"
38+
"golang.org/x/time/rate"
3839
)
3940

40-
// CallInterval is the duration between etherscan requests.
41+
// CallsPerSec is thenumber of etherscanr equests allowed
42+
// per second.
4143
// Etherscan rate limits to one request per 0.2 seconds.
42-
var CallInterval = 260 * time.Millisecond
44+
var CallsPerSec = 3.8
4345

4446
const apiKey = "X3AFAGQT2QCAFTFPIH9VJY88H9PIQ2UWP7"
4547

@@ -50,17 +52,22 @@ const ERC20GasErr = "insufficient funds for gas * price + value"
5052
type EtherScan struct {
5153
url string
5254
httpClient *http.Client
55+
limiter *rate.Limiter
5356
}
5457

5558
// NewEtherScan creates a new instance of EtherScan.
5659
func NewEtherScan(url string, httpClient *http.Client) *EtherScan {
5760
return &EtherScan{
5861
url: url,
5962
httpClient: httpClient,
63+
limiter: rate.NewLimiter(rate.Limit(CallsPerSec), 1),
6064
}
6165
}
6266

63-
func (etherScan *EtherScan) call(params url.Values, result interface{}) error {
67+
func (etherScan *EtherScan) call(ctx context.Context, params url.Values, result interface{}) error {
68+
if err := etherScan.limiter.Wait(ctx); err != nil {
69+
return errp.WithStack(err)
70+
}
6471
params.Set("apikey", apiKey)
6572
response, err := etherScan.httpClient.Get(etherScan.url + "?" + params.Encode())
6673
if err != nil {
@@ -323,7 +330,7 @@ func (etherScan *EtherScan) Transactions(
323330
result := struct {
324331
Result []*Transaction
325332
}{}
326-
if err := etherScan.call(params, &result); err != nil {
333+
if err := etherScan.call(context.TODO(), params, &result); err != nil {
327334
return nil, err
328335
}
329336
isERC20 := erc20Token != nil
@@ -338,7 +345,7 @@ func (etherScan *EtherScan) Transactions(
338345
resultInternal := struct {
339346
Result []*Transaction
340347
}{}
341-
if err := etherScan.call(params, &resultInternal); err != nil {
348+
if err := etherScan.call(context.TODO(), params, &resultInternal); err != nil {
342349
return nil, err
343350
}
344351
var err error
@@ -353,7 +360,7 @@ func (etherScan *EtherScan) Transactions(
353360

354361
// ----- RPC node proxy methods follow
355362

356-
func (etherScan *EtherScan) rpcCall(params url.Values, result interface{}) error {
363+
func (etherScan *EtherScan) rpcCall(ctx context.Context, params url.Values, result interface{}) error {
357364
params.Set("module", "proxy")
358365

359366
var wrapped struct {
@@ -364,7 +371,7 @@ func (etherScan *EtherScan) rpcCall(params url.Values, result interface{}) error
364371
} `json:"error"`
365372
Result *json.RawMessage `json:"result"`
366373
}
367-
if err := etherScan.call(params, &wrapped); err != nil {
374+
if err := etherScan.call(ctx, params, &wrapped); err != nil {
368375
return err
369376
}
370377
if wrapped.Error != nil {
@@ -389,7 +396,7 @@ func (etherScan *EtherScan) TransactionReceiptWithBlockNumber(
389396
params.Set("action", "eth_getTransactionReceipt")
390397
params.Set("txhash", hash.Hex())
391398
var result *rpcclient.RPCTransactionReceipt
392-
if err := etherScan.rpcCall(params, &result); err != nil {
399+
if err := etherScan.rpcCall(ctx, params, &result); err != nil {
393400
return nil, err
394401
}
395402
return result, nil
@@ -402,7 +409,7 @@ func (etherScan *EtherScan) TransactionByHash(
402409
params.Set("action", "eth_getTransactionByHash")
403410
params.Set("txhash", hash.Hex())
404411
var result rpcclient.RPCTransaction
405-
if err := etherScan.rpcCall(params, &result); err != nil {
412+
if err := etherScan.rpcCall(ctx, params, &result); err != nil {
406413
return nil, false, err
407414
}
408415
return &result.Transaction, result.BlockNumber == nil, nil
@@ -415,7 +422,7 @@ func (etherScan *EtherScan) BlockNumber(ctx context.Context) (*big.Int, error) {
415422
params.Set("tag", "latest")
416423
params.Set("boolean", "false")
417424
var header *types.Header
418-
if err := etherScan.rpcCall(params, &header); err != nil {
425+
if err := etherScan.rpcCall(ctx, params, &header); err != nil {
419426
return nil, err
420427
}
421428
return header.Number, nil
@@ -434,7 +441,7 @@ func (etherScan *EtherScan) Balance(ctx context.Context, account common.Address)
434441
params.Set("action", "balance")
435442
params.Set("address", account.Hex())
436443
params.Set("tag", "latest")
437-
if err := etherScan.call(params, &result); err != nil {
444+
if err := etherScan.call(ctx, params, &result); err != nil {
438445
return nil, err
439446
}
440447
if result.Status != "1" {
@@ -461,7 +468,7 @@ func (etherScan *EtherScan) ERC20Balance(account common.Address, erc20Token *erc
461468
params.Set("address", account.Hex())
462469
params.Set("contractaddress", erc20Token.ContractAddress().Hex())
463470
params.Set("tag", "latest")
464-
if err := etherScan.call(params, &result); err != nil {
471+
if err := etherScan.call(context.TODO(), params, &result); err != nil {
465472
return nil, err
466473
}
467474
if result.Status != "1" {
@@ -485,7 +492,7 @@ func (etherScan *EtherScan) CallContract(ctx context.Context, msg ethereum.CallM
485492
panic("not implemented")
486493
}
487494
var result hexutil.Bytes
488-
if err := etherScan.rpcCall(params, &result); err != nil {
495+
if err := etherScan.rpcCall(ctx, params, &result); err != nil {
489496
return nil, err
490497
}
491498
return result, nil
@@ -515,7 +522,7 @@ func (etherScan *EtherScan) EstimateGas(ctx context.Context, msg ethereum.CallMs
515522
callMsgParams(&params, msg)
516523

517524
var result hexutil.Uint64
518-
if err := etherScan.rpcCall(params, &result); err != nil {
525+
if err := etherScan.rpcCall(ctx, params, &result); err != nil {
519526
return 0, err
520527
}
521528
return uint64(result), nil
@@ -528,7 +535,7 @@ func (etherScan *EtherScan) PendingNonceAt(ctx context.Context, account common.A
528535
params.Set("address", account.Hex())
529536
params.Set("tag", "pending")
530537
var result hexutil.Uint64
531-
if err := etherScan.rpcCall(params, &result); err != nil {
538+
if err := etherScan.rpcCall(ctx, params, &result); err != nil {
532539
return 0, err
533540
}
534541
return uint64(result), nil
@@ -544,15 +551,15 @@ func (etherScan *EtherScan) SendTransaction(ctx context.Context, tx *types.Trans
544551
params := url.Values{}
545552
params.Set("action", "eth_sendRawTransaction")
546553
params.Set("hex", hexutil.Encode(encodedTx))
547-
return etherScan.rpcCall(params, nil)
554+
return etherScan.rpcCall(ctx, params, nil)
548555
}
549556

550557
// SuggestGasPrice implements rpc.Interface.
551558
func (etherScan *EtherScan) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
552559
params := url.Values{}
553560
params.Set("action", "eth_gasPrice")
554561
var result hexutil.Big
555-
if err := etherScan.rpcCall(params, &result); err != nil {
562+
if err := etherScan.rpcCall(ctx, params, &result); err != nil {
556563
return nil, err
557564
}
558565
return (*big.Int)(&result), nil
@@ -581,7 +588,7 @@ func (etherScan *EtherScan) FeeTargets(ctx context.Context) ([]*ethtypes.FeeTarg
581588
params := url.Values{}
582589
params.Set("module", "gastracker")
583590
params.Set("action", "gasoracle")
584-
if err := etherScan.call(params, &result); err != nil {
591+
if err := etherScan.call(ctx, params, &result); err != nil {
585592
return nil, err
586593
}
587594
// Convert string fields to int64

backend/devices/usb/manager.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ type Manager struct {
8686
onRegister func(device.Interface) error
8787
onUnregister func(string)
8888

89+
updateCh chan struct{}
90+
8991
socksProxy socksproxy.SocksProxy
9092

9193
log *logrus.Entry
@@ -111,6 +113,7 @@ func NewManager(
111113
deviceInfos: deviceInfos,
112114
onRegister: onRegister,
113115
onUnregister: onUnregister,
116+
updateCh: make(chan struct{}),
114117
socksProxy: socksProxy,
115118

116119
log: logging.Get().WithGroup("manager"),
@@ -255,8 +258,19 @@ func (manager *Manager) checkIfRemoved(deviceID string) bool {
255258
return true
256259
}
257260

261+
// Update triggers a scan of the USB devices to detect connects/disconnects.
262+
func (manager *Manager) Update() {
263+
go func() {
264+
manager.updateCh <- struct{}{}
265+
}()
266+
}
267+
258268
func (manager *Manager) listen() {
259269
for {
270+
select {
271+
case <-manager.updateCh:
272+
case <-time.After(time.Second):
273+
}
260274
for deviceID, device := range manager.devices {
261275
// Check if device was removed.
262276
if manager.checkIfRemoved(deviceID) {
@@ -310,11 +324,11 @@ func (manager *Manager) listen() {
310324
manager.log.WithError(err).Error("Failed to execute on-register")
311325
}
312326
}
313-
time.Sleep(time.Second)
314327
}
315328
}
316329

317330
// Start listens for inserted/removed devices forever.
318331
func (manager *Manager) Start() {
319332
go manager.listen()
333+
manager.Update()
320334
}

backend/mobileserver/mobileserver.go

+5
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ func UsingMobileDataChanged() {
155155
bridgecommon.UsingMobileDataChanged()
156156
}
157157

158+
// UsbUpdate exposes `bridgecommon.UsbUpdate` to Java/Kotlin.
159+
func UsbUpdate() {
160+
bridgecommon.UsbUpdate()
161+
}
162+
158163
type goLogHook struct {
159164
}
160165

backend/rates/gecko.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ const (
1616
maxGeckoRange = 364 * 24 * time.Hour
1717
)
1818

19-
// apiRateLimit specifies the minimal interval between equally spaced API calls
19+
// apiRateLimit specifies the maximum number of API calls per second
2020
// to one of the supported exchange rates providers.
21-
func apiRateLimit(baseURL string) time.Duration {
21+
func apiRateLimit(baseURL string) float64 {
2222
switch baseURL {
2323
default:
24-
return time.Second // arbitrary; localhost, staging, etc.
24+
return 1 // arbitrary; localhost, staging, etc.
2525
case coingeckoAPIV3:
2626
// API calls. From https://www.coingecko.com/en/api:
2727
// > Generous rate limits with up to 100 requests/minute
2828
// We use slightly lower value.
29-
return 2 * time.Second
29+
return 0.5
3030
case shiftGeckoMirrorAPIV3:
3131
// Avoid zero to prevent unexpected panics like in time.NewTicker
3232
// and leave some room to breathe.
33-
return 10 * time.Millisecond
33+
return 100
3434
}
3535
}
3636

0 commit comments

Comments
 (0)