mirror of
https://github.com/status-im/status-go.git
synced 2025-02-22 19:58:29 +00:00
Bug: Refresh recipients model (#4076)
This commit is contained in:
parent
382fcde74e
commit
cff96f99e0
@ -5,13 +5,23 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
// used for embedding the sql query in the binary
|
||||
_ "embed"
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
const NoLimitTimestampForPeriod = 0
|
||||
|
||||
//go:embed oldest_timestamp.sql
|
||||
var oldestTimestampQueryFormatString string
|
||||
|
||||
//go:embed recipients.sql
|
||||
var recipientsQueryFormatString string
|
||||
|
||||
type Period struct {
|
||||
StartTimestamp int64 `json:"startTimestamp"`
|
||||
EndTimestamp int64 `json:"endTimestamp"`
|
||||
@ -85,49 +95,22 @@ type Filter struct {
|
||||
FilterOutCollectibles bool `json:"filterOutCollectibles"`
|
||||
}
|
||||
|
||||
func GetRecipients(ctx context.Context, db *sql.DB, offset int, limit int) (addresses []eth.Address, hasMore bool, err error) {
|
||||
rows, err := db.QueryContext(ctx, `
|
||||
SELECT
|
||||
to_address,
|
||||
MIN(timestamp) AS min_timestamp
|
||||
FROM (
|
||||
SELECT
|
||||
transfers.tx_to_address as to_address,
|
||||
MIN(transfers.timestamp) AS timestamp
|
||||
FROM
|
||||
transfers
|
||||
WHERE
|
||||
transfers.multi_transaction_id = 0 AND transfers.tx_to_address NOT NULL
|
||||
GROUP BY
|
||||
transfers.tx_to_address
|
||||
func GetRecipients(ctx context.Context, db *sql.DB, chainIDs []common.ChainID, addresses []eth.Address, offset int, limit int) (recipients []eth.Address, hasMore bool, err error) {
|
||||
filterAllAddresses := len(addresses) == 0
|
||||
involvedAddresses := noEntriesInTmpTableSQLValues
|
||||
if !filterAllAddresses {
|
||||
involvedAddresses = joinAddresses(addresses)
|
||||
}
|
||||
|
||||
UNION
|
||||
includeAllNetworks := len(chainIDs) == 0
|
||||
networks := noEntriesInTmpTableSQLValues
|
||||
if !includeAllNetworks {
|
||||
networks = joinItems(chainIDs, nil)
|
||||
}
|
||||
|
||||
SELECT
|
||||
pending_transactions.to_address AS to_address,
|
||||
MIN(pending_transactions.timestamp) AS timestamp
|
||||
FROM
|
||||
pending_transactions
|
||||
WHERE
|
||||
pending_transactions.multi_transaction_id = 0 AND pending_transactions.to_address NOT NULL
|
||||
GROUP BY
|
||||
pending_transactions.to_address
|
||||
queryString := fmt.Sprintf(recipientsQueryFormatString, involvedAddresses, networks)
|
||||
|
||||
UNION
|
||||
|
||||
SELECT
|
||||
multi_transactions.to_address AS to_address,
|
||||
MIN(multi_transactions.timestamp) AS timestamp
|
||||
FROM
|
||||
multi_transactions
|
||||
GROUP BY
|
||||
multi_transactions.to_address
|
||||
) AS combined_result
|
||||
GROUP BY
|
||||
to_address
|
||||
ORDER BY
|
||||
min_timestamp DESC
|
||||
LIMIT ? OFFSET ?;`, limit, offset)
|
||||
rows, err := db.QueryContext(ctx, queryString, filterAllAddresses, includeAllNetworks, transactions.Pending, limit, offset)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -154,47 +137,13 @@ func GetRecipients(ctx context.Context, db *sql.DB, offset int, limit int) (addr
|
||||
}
|
||||
|
||||
func GetOldestTimestamp(ctx context.Context, db *sql.DB, addresses []eth.Address) (timestamp int64, err error) {
|
||||
queryFormatString := `
|
||||
WITH filter_conditions AS (SELECT ? AS filterAllAddresses),
|
||||
filter_addresses(address) AS (
|
||||
SELECT * FROM (VALUES %s) WHERE (SELECT filterAllAddresses FROM filter_conditions) = 0
|
||||
)
|
||||
|
||||
SELECT
|
||||
transfers.tx_from_address AS from_address,
|
||||
transfers.tx_to_address AS to_address,
|
||||
transfers.timestamp AS timestamp
|
||||
FROM transfers, filter_conditions
|
||||
WHERE transfers.multi_transaction_id = 0
|
||||
AND (filterAllAddresses OR from_address IN filter_addresses OR to_address IN filter_addresses)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
pending_transactions.from_address AS from_address,
|
||||
pending_transactions.to_address AS to_address,
|
||||
pending_transactions.timestamp AS timestamp
|
||||
FROM pending_transactions, filter_conditions
|
||||
WHERE pending_transactions.multi_transaction_id = 0
|
||||
AND (filterAllAddresses OR from_address IN filter_addresses OR to_address IN filter_addresses)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
multi_transactions.from_address AS from_address,
|
||||
multi_transactions.to_address AS to_address,
|
||||
multi_transactions.timestamp AS timestamp
|
||||
FROM multi_transactions, filter_conditions
|
||||
WHERE filterAllAddresses OR from_address IN filter_addresses OR to_address IN filter_addresses
|
||||
ORDER BY timestamp ASC
|
||||
LIMIT 1`
|
||||
|
||||
filterAllAddresses := len(addresses) == 0
|
||||
involvedAddresses := noEntriesInTmpTableSQLValues
|
||||
if !filterAllAddresses {
|
||||
involvedAddresses = joinAddresses(addresses)
|
||||
}
|
||||
queryString := fmt.Sprintf(queryFormatString, involvedAddresses)
|
||||
|
||||
queryString := fmt.Sprintf(oldestTimestampQueryFormatString, involvedAddresses)
|
||||
|
||||
row := db.QueryRowContext(ctx, queryString, filterAllAddresses)
|
||||
var fromAddress, toAddress sql.NullString
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/testutils"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
@ -69,16 +70,6 @@ func insertTestData(t *testing.T, db *sql.DB, nullifyToForIndexes []int) (trs []
|
||||
return
|
||||
}
|
||||
|
||||
func TestGetRecipientsEmptyDB(t *testing.T) {
|
||||
db, close := setupTestFilterDB(t)
|
||||
defer close()
|
||||
|
||||
entries, hasMore, err := GetRecipients(context.Background(), db, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(entries))
|
||||
require.False(t, hasMore)
|
||||
}
|
||||
|
||||
func TestGetRecipients(t *testing.T) {
|
||||
db, close := setupTestFilterDB(t)
|
||||
defer close()
|
||||
@ -103,7 +94,7 @@ func TestGetRecipients(t *testing.T) {
|
||||
dupTrs[3].To = trs[5].To
|
||||
transfer.InsertTestPendingTransaction(t, db, &dupTrs[3])
|
||||
|
||||
entries, hasMore, err := GetRecipients(context.Background(), db, 0, 15)
|
||||
entries, hasMore, err := GetRecipients(context.Background(), db, []common.ChainID{}, []eth.Address{}, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.False(t, hasMore)
|
||||
require.Equal(t, 6, len(entries))
|
||||
@ -118,10 +109,27 @@ func TestGetRecipients(t *testing.T) {
|
||||
require.True(t, found, fmt.Sprintf("recipient %s not found in toTrs", entries[i].Hex()))
|
||||
}
|
||||
|
||||
entries, hasMore, err = GetRecipients(context.Background(), db, 0, 2)
|
||||
entries, hasMore, err = GetRecipients(context.Background(), db, []common.ChainID{}, []eth.Address{}, 0, 2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(entries))
|
||||
require.True(t, hasMore)
|
||||
|
||||
// Get Recipients from specific chains
|
||||
entries, hasMore, err = GetRecipients(context.Background(), db, []common.ChainID{10}, []eth.Address{}, 0, 15)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(entries))
|
||||
require.False(t, hasMore)
|
||||
require.Equal(t, trs[5].To, entries[0])
|
||||
require.Equal(t, trs[2].To, entries[1])
|
||||
|
||||
// Get Recipients from specific addresses
|
||||
entries, hasMore, err = GetRecipients(context.Background(), db, []common.ChainID{}, []eth.Address{trs[0].From}, 0, 15)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(entries))
|
||||
require.False(t, hasMore)
|
||||
require.Equal(t, trs[1].To, entries[0])
|
||||
}
|
||||
|
||||
func TestGetRecipients_NullAddresses(t *testing.T) {
|
||||
@ -130,7 +138,7 @@ func TestGetRecipients_NullAddresses(t *testing.T) {
|
||||
|
||||
insertTestData(t, db, []int{1, 2, 3, 5})
|
||||
|
||||
entries, hasMore, err := GetRecipients(context.Background(), db, 0, 15)
|
||||
entries, hasMore, err := GetRecipients(context.Background(), db, []common.ChainID{}, []eth.Address{}, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.False(t, hasMore)
|
||||
require.Equal(t, 3, len(entries))
|
||||
|
35
services/wallet/activity/oldest_timestamp.sql
Normal file
35
services/wallet/activity/oldest_timestamp.sql
Normal file
@ -0,0 +1,35 @@
|
||||
-- Get oldest timestamp query
|
||||
|
||||
WITH filter_conditions AS
|
||||
(SELECT ? AS filterAllAddresses),
|
||||
filter_addresses(address) AS (
|
||||
SELECT * FROM (VALUES %s) WHERE (SELECT filterAllAddresses FROM filter_conditions) = 0
|
||||
)
|
||||
SELECT
|
||||
transfers.tx_from_address AS from_address,
|
||||
transfers.tx_to_address AS to_address,
|
||||
transfers.timestamp AS timestamp
|
||||
FROM transfers, filter_conditions
|
||||
WHERE transfers.multi_transaction_id = 0
|
||||
AND (filterAllAddresses OR from_address IN filter_addresses OR to_address IN filter_addresses)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
pending_transactions.from_address AS from_address,
|
||||
pending_transactions.to_address AS to_address,
|
||||
pending_transactions.timestamp AS timestamp
|
||||
FROM pending_transactions, filter_conditions
|
||||
WHERE pending_transactions.multi_transaction_id = 0
|
||||
AND (filterAllAddresses OR from_address IN filter_addresses OR to_address IN filter_addresses)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
multi_transactions.from_address AS from_address,
|
||||
multi_transactions.to_address AS to_address,
|
||||
multi_transactions.timestamp AS timestamp
|
||||
FROM multi_transactions, filter_conditions
|
||||
WHERE filterAllAddresses OR from_address IN filter_addresses OR to_address IN filter_addresses
|
||||
ORDER BY timestamp ASC
|
||||
LIMIT 1
|
114
services/wallet/activity/recipients.sql
Normal file
114
services/wallet/activity/recipients.sql
Normal file
@ -0,0 +1,114 @@
|
||||
-- Query to retrive all recipients for selected addresses and networks
|
||||
WITH filter_conditions AS (
|
||||
SELECT
|
||||
? AS filterAllAddresses,
|
||||
? AS includeAllNetworks,
|
||||
? AS pendingStatus
|
||||
),
|
||||
filter_addresses(address) AS (
|
||||
VALUES
|
||||
%s
|
||||
),
|
||||
filter_networks(network_id) AS (
|
||||
VALUES
|
||||
%s
|
||||
),
|
||||
pending_network_ids AS (
|
||||
SELECT
|
||||
multi_transaction_id
|
||||
FROM
|
||||
pending_transactions,
|
||||
filter_conditions
|
||||
WHERE
|
||||
pending_transactions.multi_transaction_id != 0
|
||||
AND pending_transactions.status = pendingStatus
|
||||
AND pending_transactions.network_id IN filter_networks
|
||||
GROUP BY
|
||||
pending_transactions.multi_transaction_id
|
||||
),
|
||||
tr_network_ids AS (
|
||||
SELECT
|
||||
multi_transaction_id
|
||||
FROM
|
||||
transfers
|
||||
WHERE
|
||||
transfers.loaded == 1
|
||||
AND transfers.multi_transaction_id != 0
|
||||
AND network_id IN filter_networks
|
||||
GROUP BY
|
||||
transfers.multi_transaction_id
|
||||
)
|
||||
SELECT
|
||||
to_address,
|
||||
MIN(timestamp) AS min_timestamp
|
||||
FROM (
|
||||
SELECT
|
||||
transfers.tx_to_address as to_address,
|
||||
MIN(transfers.timestamp) AS timestamp
|
||||
FROM
|
||||
transfers, filter_conditions
|
||||
WHERE
|
||||
transfers.multi_transaction_id = 0 AND transfers.tx_to_address NOT NULL
|
||||
AND (filterAllAddresses OR transfers.address IN filter_addresses)
|
||||
AND (includeAllNetworks OR transfers.network_id IN filter_networks)
|
||||
GROUP BY
|
||||
transfers.tx_to_address
|
||||
|
||||
UNION
|
||||
|
||||
SELECT
|
||||
pending_transactions.to_address AS to_address,
|
||||
MIN(pending_transactions.timestamp) AS timestamp
|
||||
FROM
|
||||
pending_transactions, filter_conditions
|
||||
WHERE
|
||||
pending_transactions.multi_transaction_id = 0 AND pending_transactions.to_address NOT NULL
|
||||
AND (filterAllAddresses OR pending_transactions.from_address IN filter_addresses)
|
||||
AND (includeAllNetworks OR pending_transactions.network_id IN filter_networks)
|
||||
GROUP BY
|
||||
pending_transactions.to_address
|
||||
|
||||
UNION
|
||||
|
||||
SELECT
|
||||
multi_transactions.to_address AS to_address,
|
||||
MIN(multi_transactions.timestamp) AS timestamp
|
||||
FROM
|
||||
multi_transactions, filter_conditions
|
||||
WHERE
|
||||
(filterAllAddresses OR multi_transactions.from_address IN filter_addresses)
|
||||
AND (
|
||||
includeAllNetworks
|
||||
OR (multi_transactions.from_network_id IN filter_networks)
|
||||
OR (multi_transactions.to_network_id IN filter_networks)
|
||||
OR (
|
||||
COALESCE(multi_transactions.from_network_id, 0) = 0
|
||||
AND COALESCE(multi_transactions.to_network_id, 0) = 0
|
||||
AND (
|
||||
EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
tr_network_ids
|
||||
WHERE
|
||||
multi_transactions.ROWID = tr_network_ids.multi_transaction_id
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
pending_network_ids
|
||||
WHERE
|
||||
multi_transactions.ROWID = pending_network_ids.multi_transaction_id
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
GROUP BY
|
||||
multi_transactions.to_address
|
||||
) AS combined_result
|
||||
GROUP BY
|
||||
to_address
|
||||
ORDER BY
|
||||
min_timestamp DESC
|
||||
LIMIT ? OFFSET ?;
|
@ -190,14 +190,14 @@ type GetRecipientsResponse struct {
|
||||
// GetRecipientsAsync returns true if a task is already running or scheduled due to a previous call; meaning that
|
||||
// this call won't receive an answer but client should rely on the answer from the previous call.
|
||||
// If no task is already scheduled false will be returned
|
||||
func (s *Service) GetRecipientsAsync(requestID int32, offset int, limit int) bool {
|
||||
func (s *Service) GetRecipientsAsync(requestID int32, chainIDs []w_common.ChainID, addresses []common.Address, offset int, limit int) bool {
|
||||
return s.scheduler.Enqueue(requestID, getRecipientsTask, func(ctx context.Context) (interface{}, error) {
|
||||
var err error
|
||||
result := &GetRecipientsResponse{
|
||||
Offset: offset,
|
||||
ErrorCode: ErrorCodeSuccess,
|
||||
}
|
||||
result.Addresses, result.HasMore, err = GetRecipients(ctx, s.db, offset, limit)
|
||||
result.Addresses, result.HasMore, err = GetRecipients(ctx, s.db, chainIDs, addresses, offset, limit)
|
||||
return result, err
|
||||
}, func(result interface{}, taskType async.TaskType, err error) {
|
||||
res := result.(*GetRecipientsResponse)
|
||||
|
@ -603,10 +603,10 @@ func (api *API) GetTxDetails(ctx context.Context, id string) (*activity.EntryDet
|
||||
return api.s.activity.GetTxDetails(ctx, id)
|
||||
}
|
||||
|
||||
func (api *API) GetRecipientsAsync(requestID int32, offset int, limit int) (ignored bool, err error) {
|
||||
log.Debug("wallet.api.GetRecipientsAsync", "offset", offset, "limit", limit)
|
||||
func (api *API) GetRecipientsAsync(requestID int32, chainIDs []wcommon.ChainID, addresses []common.Address, offset int, limit int) (ignored bool, err error) {
|
||||
log.Debug("wallet.api.GetRecipientsAsync", "addresses.len", len(addresses), "chainIDs.len", len(chainIDs), "offset", offset, "limit", limit)
|
||||
|
||||
ignored = api.s.activity.GetRecipientsAsync(requestID, offset, limit)
|
||||
ignored = api.s.activity.GetRecipientsAsync(requestID, chainIDs, addresses, offset, limit)
|
||||
return ignored, err
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user