2023-05-19 08:19:48 +00:00
|
|
|
package transfer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/status-im/status-go/services/wallet/bigint"
|
|
|
|
)
|
|
|
|
|
|
|
|
type BlockRangeSequentialDAO struct {
|
|
|
|
db *sql.DB
|
|
|
|
}
|
|
|
|
|
|
|
|
type BlockRange struct {
|
|
|
|
Start *big.Int // Block of first transfer
|
|
|
|
FirstKnown *big.Int // Oldest scanned block
|
|
|
|
LastKnown *big.Int // Last scanned block
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewBlockRange() *BlockRange {
|
|
|
|
return &BlockRange{Start: &big.Int{}, FirstKnown: &big.Int{}, LastKnown: &big.Int{}}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *BlockRangeSequentialDAO) getBlockRange(chainID uint64, address common.Address) (blockRange *BlockRange, err error) {
|
|
|
|
query := `SELECT blk_start, blk_first, blk_last FROM blocks_ranges_sequential
|
|
|
|
WHERE address = ?
|
|
|
|
AND network_id = ?`
|
|
|
|
|
|
|
|
rows, err := b.db.Query(query, address, chainID)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
if rows.Next() {
|
|
|
|
blockRange = NewBlockRange()
|
|
|
|
err = rows.Scan((*bigint.SQLBigInt)(blockRange.Start), (*bigint.SQLBigInt)(blockRange.FirstKnown), (*bigint.SQLBigInt)(blockRange.LastKnown))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return blockRange, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO call it when account is removed
|
|
|
|
//
|
|
|
|
//lint:ignore U1000 Ignore unused function temporarily
|
|
|
|
func (b *BlockRangeSequentialDAO) deleteRange(chainID uint64, account common.Address) error {
|
2023-05-26 08:27:48 +00:00
|
|
|
log.Debug("delete blocks range", "account", account, "network", chainID)
|
2023-05-19 08:19:48 +00:00
|
|
|
delete, err := b.db.Prepare(`DELETE FROM blocks_ranges_sequential
|
|
|
|
WHERE address = ?
|
|
|
|
AND network_id = ?`)
|
|
|
|
if err != nil {
|
2023-05-26 08:27:48 +00:00
|
|
|
log.Error("Failed to prepare deletion of sequential block range", "error", err)
|
2023-05-19 08:19:48 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = delete.Exec(account, chainID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
func (b *BlockRangeSequentialDAO) upsertRange(chainID uint64, account common.Address,
|
|
|
|
newBlockRange *BlockRange) (err error) {
|
2023-05-19 08:19:48 +00:00
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
log.Debug("upsert blocks range", "account", account, "chainID", chainID,
|
|
|
|
"start", newBlockRange.Start, "first", newBlockRange.FirstKnown, "last", newBlockRange.LastKnown)
|
2023-05-19 08:19:48 +00:00
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
blockRange, err := b.getBlockRange(chainID, account)
|
2023-05-19 08:19:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
// Update existing range
|
|
|
|
if blockRange != nil {
|
|
|
|
// Ovewrite start block if there was not any or if new one is older, because it can be precised only
|
|
|
|
// to a greater value, because no history can be before some block that is considered
|
|
|
|
// as a start of history, but due to concurrent block range checks, a newer greater block
|
|
|
|
// can be found that matches criteria of a start block (nonce is zero, balances are equal)
|
|
|
|
if newBlockRange.Start != nil || (blockRange.Start != nil && newBlockRange.Start != nil &&
|
|
|
|
blockRange.Start.Cmp(newBlockRange.Start) < 0) {
|
|
|
|
blockRange.Start = newBlockRange.Start
|
|
|
|
}
|
2023-05-19 08:19:48 +00:00
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
// Overwrite first known block if there was not any or if new one is older
|
|
|
|
if (blockRange.FirstKnown == nil && newBlockRange.FirstKnown != nil) ||
|
|
|
|
(blockRange.FirstKnown != nil && newBlockRange.FirstKnown != nil && blockRange.FirstKnown.Cmp(newBlockRange.FirstKnown) > 0) {
|
|
|
|
blockRange.FirstKnown = newBlockRange.FirstKnown
|
|
|
|
}
|
2023-05-19 08:19:48 +00:00
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
// Overwrite last known block if there was not any or if new one is newer
|
|
|
|
if (blockRange.LastKnown == nil && newBlockRange.LastKnown != nil) ||
|
|
|
|
(blockRange.LastKnown != nil && newBlockRange.LastKnown != nil && blockRange.LastKnown.Cmp(newBlockRange.LastKnown) < 0) {
|
|
|
|
blockRange.LastKnown = newBlockRange.LastKnown
|
|
|
|
}
|
2023-05-19 08:19:48 +00:00
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
log.Debug("update blocks range", "account", account, "chainID", chainID,
|
|
|
|
"start", blockRange.Start, "first", blockRange.FirstKnown, "last", blockRange.LastKnown)
|
|
|
|
} else {
|
|
|
|
blockRange = newBlockRange
|
2023-05-19 08:19:48 +00:00
|
|
|
}
|
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
upsert, err := b.db.Prepare(`REPLACE INTO blocks_ranges_sequential
|
|
|
|
(network_id, address, blk_start, blk_first, blk_last) VALUES (?, ?, ?, ?, ?)`)
|
2023-05-19 08:19:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-26 08:27:48 +00:00
|
|
|
_, err = upsert.Exec(chainID, account, (*bigint.SQLBigInt)(blockRange.Start), (*bigint.SQLBigInt)(blockRange.FirstKnown),
|
|
|
|
(*bigint.SQLBigInt)(blockRange.LastKnown))
|
2023-05-19 08:19:48 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|