mirror of
https://github.com/status-im/status-go.git
synced 2025-01-09 06:12:55 +00:00
c2f22f1fbc
- In order to avoid handling of the reorganized blocks we use an offset from the latest known block when start listening to new blocks. Before this commit the offset was 15 blocks for all networks. This offset is too big for mainnet and causes noticeable delay of marking a transfer as confirmed in Status (comparing to etherscan). So it was changed to be 5 blocks on mainnet and is still 15 blocks on other networks. - Also before this commit all new blocks were handled one by one with network specific interval (10s for mainnet), which means that in case of lost internet connection or application suspension (happens on iOS) receiving of new blocks would be paused and then resumed with the same "speed" - 1 blocks per 10s. In case if that pause is big enough the application would never catch up with the latest block in the network, and this also causes the state of transfers to be delayed in the application. In this commit in case if there was more than 40s delay after receiving of the previous block the whole history in range between the previous received block and ("latest"-reorgeSafetyDepth) block is checked at once and app catches up with a recent state of the chain.
131 lines
3.1 KiB
Go
131 lines
3.1 KiB
Go
package wallet
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"math/big"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
"github.com/ethereum/go-ethereum/event"
|
|
|
|
"github.com/status-im/status-go/params"
|
|
)
|
|
|
|
// pow block on main chain is mined once per ~14 seconds
|
|
// but for tests we are using clique chain with immediate block signer
|
|
// hence we can use different polling periods for methods that depend on mining time.
|
|
func pollingPeriodByChain(chain *big.Int) time.Duration {
|
|
switch chain.Int64() {
|
|
case int64(params.MainNetworkID):
|
|
return 10 * time.Second
|
|
case int64(params.RopstenNetworkID):
|
|
return 4 * time.Second
|
|
default:
|
|
return 500 * time.Millisecond
|
|
}
|
|
}
|
|
|
|
func reorgSafetyDepth(chain *big.Int) *big.Int {
|
|
switch chain.Int64() {
|
|
case int64(params.MainNetworkID):
|
|
return big.NewInt(5)
|
|
case int64(params.RopstenNetworkID):
|
|
return big.NewInt(15)
|
|
default:
|
|
return big.NewInt(15)
|
|
}
|
|
}
|
|
|
|
var (
|
|
erc20BatchSize = big.NewInt(100000)
|
|
errAlreadyRunning = errors.New("already running")
|
|
)
|
|
|
|
// HeaderReader interface for reading headers using block number or hash.
|
|
type HeaderReader interface {
|
|
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
|
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
|
}
|
|
|
|
// BalanceReader interface for reading balance at a specifeid address.
|
|
type BalanceReader interface {
|
|
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
|
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
|
|
}
|
|
|
|
type reactorClient interface {
|
|
HeaderReader
|
|
BalanceReader
|
|
}
|
|
|
|
// NewReactor creates instance of the Reactor.
|
|
func NewReactor(db *Database, feed *event.Feed, client *ethclient.Client, chain *big.Int) *Reactor {
|
|
return &Reactor{
|
|
db: db,
|
|
client: client,
|
|
feed: feed,
|
|
chain: chain,
|
|
}
|
|
}
|
|
|
|
// Reactor listens to new blocks and stores transfers into the database.
|
|
type Reactor struct {
|
|
client *ethclient.Client
|
|
db *Database
|
|
feed *event.Feed
|
|
chain *big.Int
|
|
|
|
mu sync.Mutex
|
|
group *Group
|
|
}
|
|
|
|
func (r *Reactor) newControlCommand(accounts []common.Address) *controlCommand {
|
|
signer := types.NewEIP155Signer(r.chain)
|
|
ctl := &controlCommand{
|
|
db: r.db,
|
|
chain: r.chain,
|
|
client: r.client,
|
|
accounts: accounts,
|
|
eth: ÐTransferDownloader{
|
|
client: r.client,
|
|
accounts: accounts,
|
|
signer: signer,
|
|
db: r.db,
|
|
},
|
|
erc20: NewERC20TransfersDownloader(r.client, accounts, signer),
|
|
feed: r.feed,
|
|
safetyDepth: reorgSafetyDepth(r.chain),
|
|
}
|
|
|
|
return ctl
|
|
}
|
|
|
|
// Start runs reactor loop in background.
|
|
func (r *Reactor) Start(accounts []common.Address) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if r.group != nil {
|
|
return errAlreadyRunning
|
|
}
|
|
r.group = NewGroup(context.Background())
|
|
ctl := r.newControlCommand(accounts)
|
|
r.group.Add(ctl.Command())
|
|
return nil
|
|
}
|
|
|
|
// Stop stops reactor loop and waits till it exits.
|
|
func (r *Reactor) Stop() {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if r.group == nil {
|
|
return
|
|
}
|
|
r.group.Stop()
|
|
r.group.Wait()
|
|
r.group = nil
|
|
}
|