status-go/services/wallet/transfer/testutils.go

383 lines
11 KiB
Go
Raw Normal View History

package transfer
import (
"database/sql"
"fmt"
"math/big"
"testing"
eth_common "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
2023-09-21 06:58:36 +00:00
"github.com/status-im/status-go/services/wallet/bigint"
"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/token"
"github.com/stretchr/testify/require"
)
type TestTransaction struct {
2023-06-14 16:10:20 +00:00
Hash eth_common.Hash
ChainID common.ChainID
From eth_common.Address // [sender]
Timestamp int64
BlkNumber int64
Success bool
Nonce uint64
Contract eth_common.Address
2023-06-14 16:10:20 +00:00
MultiTransactionID MultiTransactionIDType
}
type TestTransfer struct {
TestTransaction
To eth_common.Address // [address]
Value int64
Token *token.Token
2023-06-14 16:10:20 +00:00
}
type TestMultiTransaction struct {
MultiTransactionID MultiTransactionIDType
MultiTransactionType MultiTransactionType
2023-06-14 16:10:20 +00:00
FromAddress eth_common.Address
ToAddress eth_common.Address
FromToken string
ToToken string
FromAmount int64
ToAmount int64
Timestamp int64
FromNetworkID *uint64
ToNetworkID *uint64
2023-06-14 16:10:20 +00:00
}
func SeedToToken(seed int) *token.Token {
tokenIndex := seed % len(TestTokens)
return TestTokens[tokenIndex]
}
func TestTrToToken(t *testing.T, tt *TestTransaction) (token *token.Token, isNative bool) {
// Sanity check that none of the markers changed and they should be equal to seed
require.Equal(t, tt.Timestamp, tt.BlkNumber)
tokenIndex := int(tt.Timestamp) % len(TestTokens)
isNative = testutils.SliceContains(NativeTokenIndices, tokenIndex)
return TestTokens[tokenIndex], isNative
}
2023-06-14 16:10:20 +00:00
func generateTestTransaction(seed int) TestTransaction {
token := SeedToToken(seed)
2023-06-14 16:10:20 +00:00
return TestTransaction{
chore(wallet) optimize the filer query Main changes: - Use tr_type instead of IN clause - Use binary (X'...' syntax) directly into the query instead of converting DB values to HEX - Found to be slightly faster than query parameters in the dedicated benchmark - Didn't see much improvement in filter benchmarks - Tried various combinations of optimizations but without impressive performance results Benchmark results: | Name | Original | tr_type | join | hex | no-db | db_only | last | net_j | |:-----------------------|:-----------|:----------|:---------|:---------|:----------|:----------|---------:|---------:| | RAM_NoFilter-10 | 49580229 | 51253242 | 51112462 | 50915133 | 121217817 | 141691008 | 50908642 | 50239712 | | SSD_NoFilter-10 | 49963604 | 51393588 | 51213038 | 50881483 | 120785679 | 141063467 | 50462767 | 49676867 | | SSD_MovingWindow-10 | 53695712 | 54155292 | 54161733 | 54061325 | 126966633 | 146866017 | 53479929 | 53350475 | | SSD_AllAddr_AllTos-10 | 41382804 | 41195225 | 51684175 | 52107262 | 64348100 | 97608833 | 50523529 | 49968321 | | SSD_OneAddress-10 | 34945275 | 35103850 | 31066429 | 31328762 | 50927300 | 54322971 | 30098529 | 30252546 | | FilterSend_AllAddr-10 | 39546808 | 37566604 | 38389725 | 38260738 | 114820458 | 125588408 | 37127625 | 36864575 | | FilterSend_6Addr-10 | 41221458 | 41111225 | 40848288 | 40135492 | 118629700 | 128200467 | 38942521 | 39012100 | | FilterThreeNetworks-10 | - | - | - | - | - | - | 50058929 | 49854450 | Update status-desktop: #11036
2023-09-14 21:50:51 +00:00
Hash: eth_common.HexToHash(fmt.Sprintf("0x1%d", seed)),
ChainID: common.ChainID(token.ChainID),
From: eth_common.HexToAddress(fmt.Sprintf("0x2%d", seed)),
Timestamp: int64(seed),
BlkNumber: int64(seed),
Success: true,
Nonce: uint64(seed),
// In practice this is last20Bytes(Keccak256(RLP(From, nonce)))
Contract: eth_common.HexToAddress(fmt.Sprintf("0x4%d", seed)),
2023-06-14 16:10:20 +00:00
MultiTransactionID: NoMultiTransactionID,
}
}
func generateTestTransfer(seed int) TestTransfer {
tokenIndex := seed % len(TestTokens)
token := TestTokens[tokenIndex]
2023-06-14 16:10:20 +00:00
return TestTransfer{
TestTransaction: generateTestTransaction(seed),
To: eth_common.HexToAddress(fmt.Sprintf("0x3%d", seed)),
Value: int64(seed),
Token: token,
2023-06-14 16:10:20 +00:00
}
}
func GenerateTestSendMultiTransaction(tr TestTransfer) TestMultiTransaction {
return TestMultiTransaction{
MultiTransactionType: MultiTransactionSend,
FromAddress: tr.From,
ToAddress: tr.To,
FromToken: tr.Token.Symbol,
ToToken: tr.Token.Symbol,
2023-06-14 16:10:20 +00:00
FromAmount: tr.Value,
ToAmount: 0,
2023-06-14 16:10:20 +00:00
Timestamp: tr.Timestamp,
}
}
func GenerateTestSwapMultiTransaction(tr TestTransfer, toToken string, toAmount int64) TestMultiTransaction {
return TestMultiTransaction{
MultiTransactionType: MultiTransactionSwap,
FromAddress: tr.From,
ToAddress: tr.To,
FromToken: tr.Token.Symbol,
2023-06-14 16:10:20 +00:00
ToToken: toToken,
FromAmount: tr.Value,
ToAmount: toAmount,
Timestamp: tr.Timestamp,
}
}
func GenerateTestBridgeMultiTransaction(fromTr, toTr TestTransfer) TestMultiTransaction {
return TestMultiTransaction{
MultiTransactionType: MultiTransactionBridge,
FromAddress: fromTr.From,
ToAddress: toTr.To,
FromToken: fromTr.Token.Symbol,
ToToken: toTr.Token.Symbol,
2023-06-14 16:10:20 +00:00
FromAmount: fromTr.Value,
ToAmount: toTr.Value,
Timestamp: fromTr.Timestamp,
}
}
// GenerateTestTransfers will generate transaction based on the TestTokens index and roll over if there are more than
// len(TestTokens) transactions
func GenerateTestTransfers(tb testing.TB, db *sql.DB, firstStartIndex int, count int) (result []TestTransfer, fromAddresses, toAddresses []eth_common.Address) {
for i := firstStartIndex; i < (firstStartIndex + count); i++ {
2023-06-14 16:10:20 +00:00
tr := generateTestTransfer(i)
fromAddresses = append(fromAddresses, tr.From)
toAddresses = append(toAddresses, tr.To)
result = append(result, tr)
}
return
}
2023-09-21 06:58:36 +00:00
type TestCollectible struct {
TokenAddress eth_common.Address
TokenID *big.Int
ChainID common.ChainID
}
var TestCollectibles = []TestCollectible{
TestCollectible{
TokenAddress: eth_common.HexToAddress("0x97a04fda4d97c6e3547d66b572e29f4a4ff40392"),
TokenID: big.NewInt(1),
ChainID: 1,
},
TestCollectible{ // Same token ID as above but different address
TokenAddress: eth_common.HexToAddress("0x2cec8879915cdbd80c88d8b1416aa9413a24ddfa"),
TokenID: big.NewInt(1),
ChainID: 1,
},
TestCollectible{
TokenAddress: eth_common.HexToAddress("0x1dea7a3e04849840c0eb15fd26a55f6c40c4a69b"),
TokenID: big.NewInt(11),
ChainID: 5,
},
TestCollectible{ // Same address as above but different token ID
TokenAddress: eth_common.HexToAddress("0x1dea7a3e04849840c0eb15fd26a55f6c40c4a69b"),
TokenID: big.NewInt(12),
ChainID: 5,
},
}
var EthMainnet = token.Token{
Address: eth_common.HexToAddress("0x"),
Name: "Ether",
Symbol: "ETH",
ChainID: 1,
}
var EthGoerli = token.Token{
Address: eth_common.HexToAddress("0x"),
Name: "Ether",
Symbol: "ETH",
ChainID: 5,
}
var EthOptimism = token.Token{
Address: eth_common.HexToAddress("0x"),
Name: "Ether",
Symbol: "ETH",
ChainID: 10,
}
var UsdcMainnet = token.Token{
Address: eth_common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
Name: "USD Coin",
Symbol: "USDC",
ChainID: 1,
}
var UsdcGoerli = token.Token{
Address: eth_common.HexToAddress("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"),
Name: "USD Coin",
Symbol: "USDC",
ChainID: 5,
}
var UsdcOptimism = token.Token{
Address: eth_common.HexToAddress("0x7f5c764cbc14f9669b88837ca1490cca17c31607"),
Name: "USD Coin",
Symbol: "USDC",
ChainID: 10,
}
var SntMainnet = token.Token{
Address: eth_common.HexToAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"),
Name: "Status Network Token",
Symbol: "SNT",
ChainID: 1,
}
var DaiMainnet = token.Token{
Address: eth_common.HexToAddress("0xf2edF1c091f683E3fb452497d9a98A49cBA84666"),
Name: "DAI Stablecoin",
Symbol: "DAI",
ChainID: 5,
}
var DaiGoerli = token.Token{
Address: eth_common.HexToAddress("0xf2edF1c091f683E3fb452497d9a98A49cBA84666"),
Name: "DAI Stablecoin",
Symbol: "DAI",
ChainID: 5,
}
// TestTokens contains ETH/Mainnet, ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet, DAI/Goerli
var TestTokens = []*token.Token{
&EthMainnet, &EthGoerli, &EthOptimism, &UsdcMainnet, &UsdcGoerli, &UsdcOptimism, &SntMainnet, &DaiMainnet, &DaiGoerli,
}
var NativeTokenIndices = []int{0, 1, 2}
func InsertTestTransfer(tb testing.TB, db *sql.DB, address eth_common.Address, tr *TestTransfer) {
token := TestTokens[int(tr.Timestamp)%len(TestTokens)]
InsertTestTransferWithOptions(tb, db, address, tr, &TestTransferOptions{
TokenAddress: token.Address,
})
}
type TestTransferOptions struct {
TokenAddress eth_common.Address
TokenID *big.Int
NullifyAddresses []eth_common.Address
Tx *types.Transaction
Receipt *types.Receipt
}
func GenerateTxField(data []byte) *types.Transaction {
return types.NewTx(&types.DynamicFeeTx{
Data: data,
})
}
func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common.Address, tr *TestTransfer, opt *TestTransferOptions) {
2023-06-20 02:50:49 +00:00
var (
tx *sql.Tx
)
tx, err := db.Begin()
require.NoError(tb, err)
2023-06-20 02:50:49 +00:00
defer func() {
if err == nil {
err = tx.Commit()
return
}
_ = tx.Rollback()
}()
blkHash := eth_common.HexToHash("4")
block := blockDBFields{
chainID: uint64(tr.ChainID),
account: address,
2023-06-20 02:50:49 +00:00
blockNumber: big.NewInt(tr.BlkNumber),
blockHash: blkHash,
}
// Respect `FOREIGN KEY(network_id,address,blk_hash)` of `transfers` table
err = insertBlockDBFields(tx, block)
require.NoError(tb, err)
2023-06-20 02:50:49 +00:00
receiptStatus := uint64(0)
if tr.Success {
receiptStatus = 1
}
tokenType := "eth"
if (opt.TokenAddress != eth_common.Address{}) {
if opt.TokenID == nil {
tokenType = "erc20"
} else {
tokenType = "erc721"
}
}
// Workaround to simulate writing of NULL values for addresses
txTo := &tr.To
txFrom := &tr.From
for i := 0; i < len(opt.NullifyAddresses); i++ {
if opt.NullifyAddresses[i] == tr.To {
txTo = nil
}
if opt.NullifyAddresses[i] == tr.From {
txFrom = nil
}
}
2023-06-20 02:50:49 +00:00
transfer := transferDBFields{
chainID: uint64(tr.ChainID),
id: tr.Hash,
txHash: &tr.Hash,
2023-06-20 02:50:49 +00:00
address: address,
blockHash: blkHash,
blockNumber: big.NewInt(tr.BlkNumber),
sender: tr.From,
transferType: common.Type(tokenType),
2023-06-20 02:50:49 +00:00
timestamp: uint64(tr.Timestamp),
multiTransactionID: tr.MultiTransactionID,
baseGasFees: "0x0",
receiptStatus: &receiptStatus,
txValue: big.NewInt(tr.Value),
txFrom: txFrom,
txTo: txTo,
txNonce: &tr.Nonce,
tokenAddress: &opt.TokenAddress,
contractAddress: &tr.Contract,
tokenID: opt.TokenID,
transaction: opt.Tx,
receipt: opt.Receipt,
2023-06-20 02:50:49 +00:00
}
err = updateOrInsertTransfersDBFields(tx, []transferDBFields{transfer})
require.NoError(tb, err)
}
func InsertTestPendingTransaction(tb testing.TB, db *sql.DB, tr *TestTransfer) {
_, err := db.Exec(`
INSERT INTO pending_transactions (network_id, hash, timestamp, from_address, to_address,
symbol, gas_price, gas_limit, value, data, type, additional_data, multi_transaction_id
) VALUES (?, ?, ?, ?, ?, 'ETH', 0, 0, ?, '', 'eth', '', ?)`,
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, (*bigint.SQLBigIntBytes)(big.NewInt(tr.Value)), tr.MultiTransactionID)
require.NoError(tb, err)
}
func InsertTestMultiTransaction(tb testing.TB, db *sql.DB, tr *TestMultiTransaction) MultiTransactionIDType {
fromTokenType := tr.FromToken
if tr.FromToken == "" {
fromTokenType = testutils.EthSymbol
}
toTokenType := tr.ToToken
if tr.ToToken == "" {
toTokenType = testutils.EthSymbol
}
fromAmount := (*hexutil.Big)(big.NewInt(tr.FromAmount))
toAmount := (*hexutil.Big)(big.NewInt(tr.ToAmount))
result, err := db.Exec(`
INSERT INTO multi_transactions (from_address, from_asset, from_amount, to_address, to_asset, to_amount, type, timestamp, from_network_id, to_network_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
tr.FromAddress, fromTokenType, fromAmount.String(), tr.ToAddress, toTokenType, toAmount.String(), tr.MultiTransactionType, tr.Timestamp, tr.FromNetworkID, tr.ToNetworkID)
require.NoError(tb, err)
rowID, err := result.LastInsertId()
require.NoError(tb, err)
2023-06-14 16:10:20 +00:00
tr.MultiTransactionID = MultiTransactionIDType(rowID)
return tr.MultiTransactionID
}
// For using in tests only outside the package
func SaveTransfersMarkBlocksLoaded(database *Database, chainID uint64, address eth_common.Address, transfers []Transfer, blocks []*big.Int) error {
return saveTransfersMarkBlocksLoaded(database.client, chainID, address, transfers, blocks)
}