Bug: Refresh recipients model (#4076)

This commit is contained in:
Cuteivist 2023-10-02 13:46:05 +02:00 committed by GitHub
parent 382fcde74e
commit cff96f99e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 200 additions and 94 deletions

View File

@ -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

View File

@ -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))

View 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

View 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 ?;

View File

@ -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)

View File

@ -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
}