Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(nft): add token_id field to the tx history primary key, fix balance #2209

Merged
merged 23 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
51eb543
update multi_index in wasm NftTransferHistoryTable and PRIMARY KEY in…
laruh Sep 3, 2024
2bdee4f
fix erc1155_balance, check that "balanceOf" ERC11155 returns the exac…
laruh Sep 3, 2024
416b23c
Merge remote-tracking branch 'origin/dev' into fix-nft-tx-history-pri…
laruh Sep 12, 2024
a493f3f
require amount: Option<BigUint> in WithdrawErc1155
laruh Sep 12, 2024
48a7f5b
cover sql nft tx history migration
laruh Sep 12, 2024
1da9cb8
Merge remote-tracking branch 'origin/dev' into fix-nft-tx-history-pri…
laruh Sep 13, 2024
5cae2d4
make migration function name clearer
laruh Sep 13, 2024
9cebf62
wasm migration wip
laruh Sep 15, 2024
df164d4
copy_store_data_sync for NftTransferHistoryTable migration
laruh Sep 15, 2024
041f99c
change version check and add logs
laruh Sep 15, 2024
e8be556
use "else if old_version == 1 && new_version == 2" check
laruh Sep 16, 2024
c42e941
Merge remote-tracking branch 'origin/dev' into fix-nft-tx-history-pri…
laruh Sep 30, 2024
12bdb27
WIP: update on_upgrade_needed for wasm nft tables
laruh Oct 10, 2024
7511d94
Merge remote-tracking branch 'origin/dev' into fix-nft-tx-history-pri…
laruh Oct 11, 2024
14bef0d
update old/new versions handle, remove unused code
laruh Oct 11, 2024
a3ba820
avoid duplication when we crate tx history sql str, migrate_tx_histor…
laruh Oct 13, 2024
5c14c09
update comment in migrate_tx_history_table_to_schema_v2
laruh Oct 13, 2024
38ac974
cover schema_table deletion in RPC clear_nft_db
laruh Oct 13, 2024
43d7ba2
fix sql migration bug
laruh Oct 14, 2024
a5486e1
Merge remote-tracking branch 'origin/dev' into fix-nft-tx-history-pri…
laruh Oct 26, 2024
ef3dd1e
review: replace BigDecimal by BigUint in NFT withdraw
laruh Oct 26, 2024
7636464
review: use progressive upgrade pattern for IDB and sql schemas
laruh Oct 27, 2024
f44bc38
review: remove break to allow straightforward version-by-version upg…
laruh Nov 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion mm2src/coins/nft/storage/wasm/nft_idb.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::nft::storage::wasm::wasm_storage::{LastScannedBlockTable, NftListTable, NftTransferHistoryTable};
use crate::nft::storage::wasm::wasm_storage::{LastScannedBlockTable, NftListTable, NftMigrationTable,
NftTransferHistoryTable};
use async_trait::async_trait;
use mm2_db::indexed_db::InitDbResult;
use mm2_db::indexed_db::{DbIdentifier, DbInstance, DbLocked, IndexedDb, IndexedDbBuilder};
Expand Down Expand Up @@ -31,6 +32,7 @@ impl DbInstance for NftCacheIDB {
.with_table::<NftListTable>()
.with_table::<NftTransferHistoryTable>()
.with_table::<LastScannedBlockTable>()
.with_table::<NftMigrationTable>()
.build()
.await?;
Ok(NftCacheIDB { inner })
Expand Down
181 changes: 103 additions & 78 deletions mm2src/coins/nft/storage/wasm/wasm_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@ use crate::nft::storage::wasm::{WasmNftCacheError, WasmNftCacheResult};
use crate::nft::storage::{get_offset_limit, NftListStorageOps, NftTokenAddrId, NftTransferHistoryFilters,
NftTransferHistoryStorageOps, RemoveNftResult};
use async_trait::async_trait;
use common::is_initial_upgrade;
use ethereum_types::Address;
use mm2_db::indexed_db::{copy_store_data_sync, BeBigUint, DbTable, DbUpgrader, MultiIndex, OnUpgradeResult,
TableSignature};
use mm2_db::indexed_db::{BeBigUint, DbTable, DbUpgrader, MultiIndex, OnUpgradeError, OnUpgradeResult, TableSignature};
use mm2_err_handle::map_to_mm::MapToMmResult;
use mm2_err_handle::prelude::MmError;
use mm2_err_handle::prelude::MmResult;
use mm2_number::BigUint;
use num_traits::ToPrimitive;
use serde_json::{self as json, Value as Json};
use std::collections::HashSet;
use std::num::NonZeroUsize;
use wasm_bindgen::JsValue;
use web_sys::console;

const CHAIN_TOKEN_ADD_TOKEN_ID_INDEX: &str = "chain_token_add_token_id_index";
const CHAIN_BLOCK_NUMBER_INDEX: &str = "chain_block_number_index";
Expand Down Expand Up @@ -910,8 +907,8 @@ impl NftListTable {
impl TableSignature for NftListTable {
const TABLE_NAME: &'static str = "nft_list_cache_table";

fn on_upgrade_needed(upgrader: &DbUpgrader, old_version: u32, _new_version: u32) -> OnUpgradeResult<()> {
if is_initial_upgrade(old_version) {
fn on_upgrade_needed(upgrader: &DbUpgrader, old_version: u32, new_version: u32) -> OnUpgradeResult<()> {
if old_version == 0 && new_version == 2 {
borngraced marked this conversation as resolved.
Show resolved Hide resolved
let table = upgrader.create_table(Self::TABLE_NAME)?;
table.create_multi_index(
CHAIN_TOKEN_ADD_TOKEN_ID_INDEX,
Expand Down Expand Up @@ -959,9 +956,9 @@ pub(crate) struct NftTransferHistoryTable {
}

impl NftTransferHistoryTable {
// old prim key index for DB_VERSION: u32 = 1
const CHAIN_TX_HASH_LOG_INDEX_INDEX: &'static str = "chain_tx_hash_log_index_index";
// this is prim key multi index for DB_VERSION = 2
// old prim key index for DB_VERSION = 1
const _CHAIN_TX_HASH_LOG_INDEX_INDEX: &'static str = "chain_tx_hash_log_index_index";
borngraced marked this conversation as resolved.
Show resolved Hide resolved
// prim key multi index for DB_VERSION = 2
const CHAIN_TX_HASH_LOG_INDEX_TOKEN_ID_INDEX: &'static str = "chain_tx_hash_log_index_token_idindex";

fn from_transfer_history(transfer: &NftTransferHistory) -> WasmNftCacheResult<NftTransferHistoryTable> {
Expand Down Expand Up @@ -995,11 +992,11 @@ impl TableSignature for NftTransferHistoryTable {
const TABLE_NAME: &'static str = "nft_transfer_history_cache_table";

fn on_upgrade_needed(upgrader: &DbUpgrader, old_version: u32, new_version: u32) -> OnUpgradeResult<()> {
if is_initial_upgrade(old_version) {
// Initial creation of the table with the new schema
if old_version == 0 && new_version == 2 {
// Initial creation of the table with the new schema (v2)
let table = upgrader.create_table(Self::TABLE_NAME)?;
table.create_multi_index(
Self::CHAIN_TX_HASH_LOG_INDEX_TOKEN_ID_INDEX,
Self::CHAIN_TX_HASH_LOG_INDEX_TOKEN_ID_INDEX, // new unique index
&["chain", "transaction_hash", "log_index", "token_id"],
true,
)?;
Expand All @@ -1015,73 +1012,12 @@ impl TableSignature for NftTransferHistoryTable {
table.create_index("block_number", false)?;
table.create_index("chain", false)?;
} else if old_version == 1 && new_version == 2 {
// Migration from version 1 to version 2

console::log_1(&JsValue::from_str("Migrating from version 1 to version 2."));

// Step 1: Create a temporary table to hold data with the old schema
let temp_table_name = format!("{}_temp", Self::TABLE_NAME);
let temp_table = upgrader.create_table(&temp_table_name)?;
temp_table.create_multi_index(
Self::CHAIN_TX_HASH_LOG_INDEX_INDEX, // old primary key index
&["chain", "transaction_hash", "log_index"],
true,
)?;
temp_table.create_multi_index(
CHAIN_TOKEN_ADD_TOKEN_ID_INDEX,
&["chain", "token_address", "token_id"],
false,
)?;
temp_table.create_multi_index(CHAIN_BLOCK_NUMBER_INDEX, &["chain", "block_number"], false)?;
temp_table.create_multi_index(CHAIN_TOKEN_ADD_INDEX, &["chain", "token_address"], false)?;
temp_table.create_multi_index(CHAIN_TOKEN_DOMAIN_INDEX, &["chain", "token_domain"], false)?;
temp_table.create_multi_index(CHAIN_IMAGE_DOMAIN_INDEX, &["chain", "image_domain"], false)?;
temp_table.create_index("block_number", false)?;
temp_table.create_index("chain", false)?;

// Step 2: Copy data from the old store to the temp store
let old_store = upgrader.open_table(Self::TABLE_NAME)?;
let temp_store = upgrader.open_table(&temp_table_name)?;

// TODO copy data from old_store to temp_store
copy_store_data_sync(&old_store.object_store, &temp_store.object_store)?;

console::log_1(&JsValue::from_str("Copied data from old store to temp store"));

// Step 3: Delete the old object store
upgrader.delete_table(Self::TABLE_NAME)?;

console::log_1(&JsValue::from_str("Deleted old object store"));

// Step 4: Recreate the original object store with the new schema
let new_table = upgrader.create_table(Self::TABLE_NAME)?;
new_table.create_multi_index(
let table = upgrader.open_table(Self::TABLE_NAME)?;
table.create_multi_index(
Self::CHAIN_TX_HASH_LOG_INDEX_TOKEN_ID_INDEX,
&["chain", "transaction_hash", "log_index", "token_id"],
true,
)?;
new_table.create_multi_index(
CHAIN_TOKEN_ADD_TOKEN_ID_INDEX,
&["chain", "token_address", "token_id"],
false,
)?;
new_table.create_multi_index(CHAIN_BLOCK_NUMBER_INDEX, &["chain", "block_number"], false)?;
new_table.create_multi_index(CHAIN_TOKEN_ADD_INDEX, &["chain", "token_address"], false)?;
new_table.create_multi_index(CHAIN_TOKEN_DOMAIN_INDEX, &["chain", "token_domain"], false)?;
new_table.create_multi_index(CHAIN_IMAGE_DOMAIN_INDEX, &["chain", "image_domain"], false)?;
new_table.create_index("block_number", false)?;
new_table.create_index("chain", false)?;

// Step 5: Copy data back from the temp store to the new store
// TODO copy data from temp_store to new_table
copy_store_data_sync(&temp_store.object_store, &new_table.object_store)?;

console::log_1(&JsValue::from_str("Copied data from temp store to new store"));

// Step 6: Delete the temporary store
upgrader.delete_table(&temp_table_name)?;

console::log_1(&JsValue::from_str("Deleted temp store"));
}
Ok(())
}
Expand All @@ -1096,8 +1032,8 @@ pub(crate) struct LastScannedBlockTable {
impl TableSignature for LastScannedBlockTable {
const TABLE_NAME: &'static str = "last_scanned_block_table";

fn on_upgrade_needed(upgrader: &DbUpgrader, old_version: u32, _new_version: u32) -> OnUpgradeResult<()> {
if is_initial_upgrade(old_version) {
fn on_upgrade_needed(upgrader: &DbUpgrader, old_version: u32, new_version: u32) -> OnUpgradeResult<()> {
if old_version == 0 && new_version == 2 {
let table = upgrader.create_table(Self::TABLE_NAME)?;
table.create_index("chain", true)?;
}
Expand All @@ -1112,3 +1048,92 @@ fn nft_details_from_item(item: NftListTable) -> WasmNftCacheResult<Nft> {
fn transfer_details_from_item(item: NftTransferHistoryTable) -> WasmNftCacheResult<NftTransferHistory> {
json::from_value(item.details_json).map_to_mm(|e| WasmNftCacheError::ErrorDeserializing(e.to_string()))
}

#[derive(Deserialize, Serialize)]
pub(crate) struct NftMigrationTable {
tx_history_migration: u32,
}

impl TableSignature for NftMigrationTable {
const TABLE_NAME: &'static str = "nft_migration";

fn on_upgrade_needed(upgrader: &DbUpgrader, mut old_version: u32, new_version: u32) -> OnUpgradeResult<()> {
while old_version < new_version {
match old_version {
0 => {
// do nothing explicitly
},
1 => {
// this step covers both cases of (old version, new version): (0, 2) and (1, 2)
let table = upgrader.create_table(Self::TABLE_NAME)?;
table.create_index("tx_history_migration", true)?;
},
unsupported_version => {
return MmError::err(OnUpgradeError::UnsupportedVersion {
unsupported_version,
old_version,
new_version,
})
},
}

old_version += 1;
}
Ok(())
}
}

#[async_trait]
trait TxHistoryMigrationOps {
async fn current_tx_history_migration(&self) -> WasmNftCacheResult<u32>;

async fn migrate_tx_history_data(&self) -> WasmNftCacheResult<()>;
}

#[async_trait]
impl TxHistoryMigrationOps for NftCacheIDBLocked<'_> {
async fn current_tx_history_migration(&self) -> WasmNftCacheResult<u32> {
let db_transaction = self.get_inner().transaction().await?;
let table = db_transaction.table::<NftMigrationTable>().await?;
let maybe_item = table
.cursor_builder()
.bound("tx_history_migration", 0, u32::MAX)
.reverse()
.where_first()
.open_cursor("tx_history_migration")
.await
.map_err(|e| WasmNftCacheError::OpenCursorError(e.to_string()))?
.next()
.await
.map_err(|e| WasmNftCacheError::GetItemError(e.to_string()))?;
Ok(maybe_item
.map(|(_item_id, item)| item.tx_history_migration)
.unwrap_or(0))
}

async fn migrate_tx_history_data(&self) -> WasmNftCacheResult<()> {
let db_transaction = self.get_inner().transaction().await?;
let migration_table = db_transaction.table::<NftMigrationTable>().await?;

let mut migration = self.current_tx_history_migration().await?;
loop {
match migration {
0 => {
let _tx_history_table = db_transaction.table::<NftTransferHistoryTable>().await?;

},
1 => break,
unsupported => {
return MmError::err(WasmNftCacheError::InternalError(format!(
"Unsupported migration version: {}",
unsupported
)))
},
}
migration += 1;
migration_table.add_item(&NftMigrationTable {tx_history_migration: migration}).await?;
}

Ok(())
}
}
4 changes: 2 additions & 2 deletions mm2src/coins/z_coin/storage/walletdb/wasm/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ impl WalletRead for WalletIndexedDb {
let locked_db = self.lock_db().await?;
let db_transaction = locked_db.get_inner().transaction().await?;
let block_headers_db = db_transaction.table::<WalletDbBlocksTable>().await?;
let earlist_block = block_headers_db
let earliest_block = block_headers_db
.cursor_builder()
.only("ticker", &self.ticker)?
.bound("height", 0u32, u32::MAX)
Expand All @@ -830,7 +830,7 @@ impl WalletRead for WalletIndexedDb {
.next()
.await?;

if let (Some(min), Some(max)) = (earlist_block, latest_block) {
if let (Some(min), Some(max)) = (earliest_block, latest_block) {
Ok(Some((BlockHeight::from(min.1.height), BlockHeight::from(max.1.height))))
} else {
Ok(None)
Expand Down
2 changes: 1 addition & 1 deletion mm2src/common/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,7 @@ pub fn parse_rfc3339_to_timestamp(date_str: &str) -> Result<u64, ParseRfc3339Err
/// `is_initial_upgrade` function checks if the database is being upgraded from version 0 to 1.
/// This function returns a boolean indicating whether the database is being upgraded from version 0 to 1.
#[cfg(target_arch = "wasm32")]
pub fn is_initial_upgrade(old_version: u32) -> bool { old_version == 0 }
pub fn is_initial_upgrade(old_version: u32, new_version: u32) -> bool { old_version == 0 && new_version == 1 }

/// Takes `http:Uri` and converts it into `String` of websocket address
///
Expand Down
2 changes: 1 addition & 1 deletion mm2src/mm2_db/src/indexed_db/db_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use web_sys::{IdbDatabase, IdbTransactionMode};
pub use builder::{IdbDatabaseBuilder, InitDbError, InitDbResult};
pub use object_store::IdbObjectStoreImpl;
pub use transaction::{DbTransactionError, DbTransactionResult, IdbTransactionImpl};
pub use upgrader::{copy_store_data_sync, DbUpgrader, OnUpgradeError, OnUpgradeNeededCb, OnUpgradeResult};
pub use upgrader::{DbUpgrader, OnUpgradeError, OnUpgradeNeededCb, OnUpgradeResult};

lazy_static! {
static ref OPEN_DATABASES: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
Expand Down
Loading