use std::collections::HashSet;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
use cdk_common::mint_url::MintUrl;
use cdk_common::Id;
use redb::{
Database, MultimapTableDefinition, ReadableDatabase, ReadableMultimapTable, ReadableTable,
TableDefinition,
};
use super::Error;
use crate::wallet::{
KEYSETS_TABLE, KEYSET_COUNTER, KEYSET_U32_MAPPING, MINT_KEYS_TABLE, P2PK_SIGNING_KEYS_TABLE,
};
const MINTS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mints_table");
const MINT_KEYSETS_TABLE: MultimapTableDefinition<&str, &[u8]> =
MultimapTableDefinition::new("mint_keysets");
pub(crate) fn migrate_02_to_03(db: Arc<Database>) -> Result<u32, Error> {
let write_txn = db.begin_write().map_err(Error::from)?;
let mut duplicate = false;
{
let table = write_txn.open_table(MINT_KEYS_TABLE).map_err(Error::from)?;
let ids: Vec<Id> = table
.iter()
.map_err(Error::from)?
.flatten()
.flat_map(|(id, _)| Id::from_str(id.value()))
.collect();
let mut table = write_txn
.open_table(KEYSET_U32_MAPPING)
.map_err(Error::from)?;
let keysets_table = write_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
let keyset_ids: Vec<Id> = keysets_table
.iter()
.map_err(Error::from)?
.flatten()
.flat_map(|(id_bytes, _)| Id::from_bytes(id_bytes.value()))
.collect();
let ids: HashSet<Id> = ids.into_iter().chain(keyset_ids).collect();
for id in ids {
let t = table.insert(u32::from(id), id.to_string().as_str())?;
tracing::info!("Adding u32 {} for keyset {}", u32::from(id), id.to_string());
if t.is_some() {
duplicate = true;
}
}
}
if duplicate {
write_txn.abort()?;
return Err(Error::Duplicate);
}
write_txn.commit()?;
Ok(3)
}
pub fn migrate_01_to_02(db: Arc<Database>) -> Result<u32, Error> {
migrate_trim_mint_urls_01_to_02(db)?;
Ok(2)
}
fn migrate_mints_table_01_to_02(db: Arc<Database>) -> Result<(), Error> {
let mints: Vec<(String, String)>;
{
let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
mints = table
.iter()
.map_err(Error::from)?
.flatten()
.map(|(mint_url, mint_info)| {
(mint_url.value().to_string(), mint_info.value().to_string())
})
.collect();
}
let write_txn = db.begin_write()?;
{
let mut table = write_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
for (mint_url_str, info) in mints {
let mint_url = MintUrl::from_str(&mint_url_str).map_err(Error::from)?;
table.remove(mint_url_str.as_str())?;
table.insert(mint_url.to_string().as_str(), info.as_str())?;
}
}
write_txn.commit()?;
Ok(())
}
fn migrate_mint_keyset_table_01_to_02(db: Arc<Database>) -> Result<(), Error> {
let mut mints: Vec<(String, Vec<Vec<u8>>)> = vec![];
{
let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn
.open_multimap_table(MINT_KEYSETS_TABLE)
.map_err(Error::from)?;
let mint_keysets_range = table.iter().map_err(Error::from)?;
for (url, keysets) in mint_keysets_range.flatten() {
let keysets: Vec<Vec<u8>> = keysets
.into_iter()
.flatten()
.map(|k| k.value().to_vec())
.collect();
mints.push((url.value().to_string(), keysets));
}
}
let write_txn = db.begin_write()?;
{
let mut table = write_txn
.open_multimap_table(MINT_KEYSETS_TABLE)
.map_err(Error::from)?;
for (mint_url_str, keysets) in mints {
let mint_url = MintUrl::from_str(&mint_url_str).map_err(Error::from)?;
table.remove_all(mint_url_str.as_str())?;
for keyset in keysets {
table.insert(mint_url.to_string().as_str(), keyset.deref())?;
}
}
}
write_txn.commit()?;
Ok(())
}
fn migrate_trim_mint_urls_01_to_02(db: Arc<Database>) -> Result<(), Error> {
migrate_mints_table_01_to_02(Arc::clone(&db))?;
migrate_mint_keyset_table_01_to_02(Arc::clone(&db))?;
Ok(())
}
pub(crate) fn migrate_03_to_04(db: Arc<Database>) -> Result<u32, Error> {
let write_txn = db.begin_write().map_err(Error::from)?;
let keyset_ids_to_increment: Vec<(String, u32)>;
{
let table = write_txn.open_table(KEYSET_COUNTER).map_err(Error::from)?;
keyset_ids_to_increment = table
.iter()
.map_err(Error::from)?
.flatten()
.filter_map(|(keyset_id, counter)| {
let counter_value = counter.value();
if counter_value > 0 {
Some((keyset_id.value().to_string(), counter_value))
} else {
None
}
})
.collect();
}
{
let mut table = write_txn.open_table(KEYSET_COUNTER).map_err(Error::from)?;
for (keyset_id, current_counter) in keyset_ids_to_increment {
let new_counter = current_counter + 1;
table
.insert(keyset_id.as_str(), new_counter)
.map_err(Error::from)?;
tracing::info!(
"Incremented counter for keyset {} from {} to {}",
keyset_id,
current_counter,
new_counter
);
}
}
write_txn.commit()?;
Ok(4)
}
pub(crate) fn migrate_04_to_05(db: Arc<Database>) -> Result<u32, Error> {
tracing::info!("Starting migration from version 4 to 5: Initializing P2PK_SIGNING_KEYS_TABLE");
let write_txn = db.begin_write().map_err(Error::from)?;
{
let _ = write_txn
.open_table(P2PK_SIGNING_KEYS_TABLE)
.map_err(Error::from)?;
}
write_txn.commit()?;
tracing::info!("Finished migration from version 4 to 5: P2PK_SIGNING_KEYS_TABLE initialized");
Ok(5)
}