Support for binance chains

This commit is contained in:
Roman Volosovskyi 2021-11-24 14:59:45 +02:00
parent 25e1c64ef5
commit 7ef2efaabd
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
8 changed files with 139 additions and 13 deletions

View File

@ -1 +1 @@
0.91.9 0.91.10

View File

@ -39,10 +39,16 @@ func (api *API) CheckRecentHistoryForChainIDs(ctx context.Context, chainIDs []ui
// GetTransfersByAddress returns transfers for a single address // GetTransfersByAddress returns transfers for a single address
func (api *API) GetTransfersByAddress(ctx context.Context, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]transfer.View, error) { func (api *API) GetTransfersByAddress(ctx context.Context, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]transfer.View, error) {
log.Debug("[WalletAPI:: GetTransfersByAddress] get transfers for an address", "address") log.Debug("[WalletAPI:: GetTransfersByAddress] get transfers for an address", "address", address)
return api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, address, toBlock, limit, fetchMore) return api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, address, toBlock, limit, fetchMore)
} }
// LoadTransferByHash loads transfer to the database
func (api *API) LoadTransferByHash(ctx context.Context, address common.Address, hash common.Hash) error {
log.Debug("[WalletAPI:: LoadTransferByHash] get transfer by hash", "address", address, "hash", hash)
return api.s.transferController.LoadTransferByHash(ctx, api.s.rpcClient, address, hash)
}
func (api *API) GetTransfersByAddressAndChainID(ctx context.Context, chainID uint64, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]transfer.View, error) { func (api *API) GetTransfersByAddressAndChainID(ctx context.Context, chainID uint64, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]transfer.View, error) {
log.Debug("[WalletAPI:: GetTransfersByAddressAndChainID] get transfers for an address", "address", address) log.Debug("[WalletAPI:: GetTransfersByAddressAndChainID] get transfers for an address", "address", address)
return api.s.transferController.GetTransfersByAddress(ctx, chainID, address, toBlock, limit, fetchMore) return api.s.transferController.GetTransfersByAddress(ctx, chainID, address, toBlock, limit, fetchMore)

View File

@ -14,7 +14,16 @@ import (
"github.com/status-im/status-go/services/wallet/chain" "github.com/status-im/status-go/services/wallet/chain"
) )
var numberOfBlocksCheckedPerIteration = 40 var (
// This will work only for binance testnet as mainnet doesn't support
// archival request.
binanceChainMaxInitialRange = big.NewInt(500000)
binanceChainErc20BatchSize = big.NewInt(5000)
erc20BatchSize = big.NewInt(100000)
binancChainID = uint64(56)
binanceTestChainID = uint64(97)
numberOfBlocksCheckedPerIteration = 40
)
type ethHistoricalCommand struct { type ethHistoricalCommand struct {
db *Database db *Database
@ -87,12 +96,20 @@ func (c *erc20HistoricalCommand) Command() async.Command {
}.Run }.Run
} }
func getErc20BatchSize(chainID uint64) *big.Int {
if isBinanceChain(chainID) {
return binanceChainErc20BatchSize
}
return erc20BatchSize
}
func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) { func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) {
start := time.Now() start := time.Now()
if c.iterator == nil { if c.iterator == nil {
c.iterator, err = SetupIterativeDownloader( c.iterator, err = SetupIterativeDownloader(
c.db, c.chainClient, c.address, c.db, c.chainClient, c.address,
c.erc20, erc20BatchSize, c.to, c.from) c.erc20, getErc20BatchSize(c.chainClient.ChainID), c.to, c.from, !isBinanceChain(c.chainClient.ChainID))
if err != nil { if err != nil {
log.Error("failed to setup historical downloader for erc20") log.Error("failed to setup historical downloader for erc20")
return err return err
@ -585,8 +602,21 @@ func loadTransfers(ctx context.Context, accounts []common.Address, block *Block,
} }
} }
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.Client) (*big.Int, error) { func isBinanceChain(chainID uint64) bool {
return chainID == binancChainID || chainID == binanceTestChainID
}
func getLowestFrom(chainID uint64, to *big.Int) *big.Int {
from := big.NewInt(0) from := big.NewInt(0)
if isBinanceChain(chainID) && big.NewInt(0).Sub(to, from).Cmp(binanceChainMaxInitialRange) == 1 {
from = big.NewInt(0).Sub(to, binanceChainMaxInitialRange)
}
return from
}
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.Client) (*big.Int, error) {
from := getLowestFrom(client.ChainID, initialTo)
to := initialTo to := initialTo
goal := uint64(20) goal := uint64(20)
@ -602,7 +632,7 @@ func findFirstRange(c context.Context, account common.Address, initialTo *big.In
} }
if firstNonce <= goal { if firstNonce <= goal {
return zero, nil return from, nil
} }
nonceDiff := firstNonce nonceDiff := firstNonce

View File

@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/multiaccounts/accounts"
@ -169,6 +170,35 @@ func mapToList(m map[common.Address]struct{}) []common.Address {
return rst return rst
} }
func (c *Controller) LoadTransferByHash(ctx context.Context, rpcClient *rpc.Client, address common.Address, hash common.Hash) error {
chainClient, err := chain.NewClient(rpcClient, rpcClient.UpstreamChainID)
if err != nil {
return err
}
signer := types.NewLondonSigner(chainClient.ToBigInt())
transfer, err := getTransferByHash(ctx, chainClient, signer, address, hash)
if err != nil {
return err
}
transfers := []Transfer{*transfer}
err = c.db.InsertBlock(rpcClient.UpstreamChainID, address, transfer.BlockNumber, transfer.BlockHash)
if err != nil {
return err
}
blocks := []*big.Int{transfer.BlockNumber}
err = c.db.SaveTranfers(rpcClient.UpstreamChainID, address, transfers, blocks)
if err != nil {
return err
}
return nil
}
func (c *Controller) GetTransfersByAddress(ctx context.Context, chainID uint64, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]View, error) { func (c *Controller) GetTransfersByAddress(ctx context.Context, chainID uint64, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]View, error) {
log.Debug("[WalletAPI:: GetTransfersByAddress] get transfers for an address", "address", address) log.Debug("[WalletAPI:: GetTransfersByAddress] get transfers for an address", "address", address)
var toBlockBN *big.Int var toBlockBN *big.Int

View File

@ -351,6 +351,31 @@ func deleteHeaders(creator statementCreator, headers []*DBHeader) error {
return nil return nil
} }
func (db *Database) InsertBlock(chainID uint64, account common.Address, blockNumber *big.Int, blockHash common.Hash) error {
var (
tx *sql.Tx
)
tx, err := db.client.Begin()
if err != nil {
return err
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
_ = tx.Rollback()
}()
insert, err := tx.Prepare("INSERT OR IGNORE INTO blocks(network_id, address, blk_number, blk_hash, loaded) VALUES (?, ?, ?, ?, ?)")
if err != nil {
return err
}
_, err = insert.Exec(chainID, account, (*bigint.SQLBigInt)(blockNumber), blockHash, true)
return err
}
func insertBlocksWithTransactions(chainID uint64, creator statementCreator, account common.Address, headers []*DBHeader) error { func insertBlocksWithTransactions(chainID uint64, creator statementCreator, account common.Address, headers []*DBHeader) error {
insert, err := creator.Prepare("INSERT OR IGNORE INTO blocks(network_id, address, blk_number, blk_hash, loaded) VALUES (?, ?, ?, ?, ?)") insert, err := creator.Prepare("INSERT OR IGNORE INTO blocks(network_id, address, blk_number, blk_hash, loaded) VALUES (?, ?, ?, ?, ?)")
if err != nil { if err != nil {

View File

@ -90,6 +90,44 @@ func (d *ETHDownloader) GetTransfersByNumber(ctx context.Context, number *big.In
return rst, err return rst, err
} }
func getTransferByHash(ctx context.Context, client *chain.Client, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
transaction, _, err := client.TransactionByHash(ctx, hash)
if err != nil {
return nil, err
}
receipt, err := client.TransactionReceipt(ctx, hash)
if err != nil {
return nil, err
}
transactionLog := getTokenLog(receipt.Logs)
transferType := ethTransfer
if transactionLog != nil {
transferType = erc20Transfer
}
from, err := types.Sender(signer, transaction)
if err != nil {
return nil, err
}
transfer := &Transfer{Type: transferType,
ID: hash,
Address: address,
BlockNumber: receipt.BlockNumber,
BlockHash: receipt.BlockHash,
Timestamp: uint64(time.Now().Unix()),
Transaction: transaction,
From: from,
Receipt: receipt,
Log: transactionLog}
return transfer, nil
}
func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Block, accounts []common.Address) (rst []Transfer, err error) { func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Block, accounts []common.Address) (rst []Transfer, err error) {
for _, address := range accounts { for _, address := range accounts {
preloadedTransfers, err := d.db.GetPreloadedTransactions(d.chainClient.ChainID, address, blk.Hash()) preloadedTransfers, err := d.db.GetPreloadedTransactions(d.chainClient.ChainID, address, blk.Hash())

View File

@ -12,14 +12,14 @@ import (
// SetupIterativeDownloader configures IterativeDownloader with last known synced block. // SetupIterativeDownloader configures IterativeDownloader with last known synced block.
func SetupIterativeDownloader( func SetupIterativeDownloader(
db *Database, client HeaderReader, address common.Address, db *Database, client HeaderReader, address common.Address,
downloader BatchDownloader, size *big.Int, to *big.Int, from *big.Int) (*IterativeDownloader, error) { downloader BatchDownloader, size *big.Int, to *big.Int, from *big.Int, isBatchSizeAdjustable bool) (*IterativeDownloader, error) {
if to == nil || from == nil { if to == nil || from == nil {
return nil, errors.New("to or from cannot be nil") return nil, errors.New("to or from cannot be nil")
} }
adjustedSize := big.NewInt(0).Div(big.NewInt(0).Sub(to, from), big.NewInt(10)) adjustedSize := big.NewInt(0).Div(big.NewInt(0).Sub(to, from), big.NewInt(10))
if adjustedSize.Cmp(size) == 1 { if isBatchSizeAdjustable && adjustedSize.Cmp(size) == 1 {
size = adjustedSize size = adjustedSize
} }
log.Info("iterative downloader", "address", address, "from", from, "to", to, "size", size) log.Info("iterative downloader", "address", address, "from", from, "to", to, "size", size)

View File

@ -13,10 +13,7 @@ import (
"github.com/status-im/status-go/services/wallet/chain" "github.com/status-im/status-go/services/wallet/chain"
) )
var ( var errAlreadyRunning = errors.New("already running")
erc20BatchSize = big.NewInt(100000)
errAlreadyRunning = errors.New("already running")
)
// HeaderReader interface for reading headers using block number or hash. // HeaderReader interface for reading headers using block number or hash.
type HeaderReader interface { type HeaderReader interface {