Skip to content

Commit 34c35de

Browse files
authored
Modify wallet migration (#1006)
* Do not delete target wallet, do not fail migration on item-error, tweak logs Signed-off-by: Patrik Stas <patrik.stas@absa.africa>
1 parent 725797b commit 34c35de

File tree

8 files changed

+170
-81
lines changed

8 files changed

+170
-81
lines changed

aries_vcx/tests/utils/migration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ where
5555
let old_wh = self.profile.wallet_handle().unwrap();
5656
let new_wh = migrate_to_new_wallet(old_wh).await;
5757
let wallet = Arc::new(IndySdkWallet::new(new_wh));
58-
let profile = dev_build_profile_modular(self.genesis_file_path.clone(), wallet.clone());
58+
let profile = dev_build_profile_modular(self.genesis_file_path.clone(), wallet);
5959

6060
TestAgent {
6161
profile,

libvcx_core/src/api_vcx/api_global/wallet.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use aries_vcx::{
1515
base_wallet::BaseWallet,
1616
indy::{
1717
internal::{close_search_wallet, fetch_next_records_wallet, open_search_wallet},
18-
wallet::{close_wallet, create_and_open_wallet, delete_wallet, import},
18+
wallet::{close_wallet, create_indy_wallet, import, open_wallet},
1919
IndySdkWallet, IssuerConfig, RestoreWalletConfigs, WalletConfig,
2020
},
2121
structs_io::UnpackMessageOutput,
@@ -282,7 +282,11 @@ pub async fn wallet_import(config: &RestoreWalletConfigs) -> LibvcxResult<()> {
282282

283283
pub async fn wallet_migrate(wallet_config: &WalletConfig) -> LibvcxResult<()> {
284284
let src_wallet_handle = get_main_wallet_handle()?;
285-
let dest_wallet_handle = create_and_open_wallet(wallet_config).await?;
285+
info!("Assuring target wallet exists.");
286+
create_indy_wallet(wallet_config).await?;
287+
info!("Opening target wallet.");
288+
let dest_wallet_handle = open_wallet(wallet_config).await?;
289+
info!("Target wallet is ready.");
286290

287291
let migration_res = wallet_migrator::migrate_wallet(
288292
src_wallet_handle,
@@ -291,18 +295,11 @@ pub async fn wallet_migrate(wallet_config: &WalletConfig) -> LibvcxResult<()> {
291295
)
292296
.await;
293297

294-
if let Err(e) = migration_res {
295-
close_wallet(dest_wallet_handle).await.ok();
296-
delete_wallet(wallet_config).await.ok();
297-
Err(LibvcxError::from_msg(
298-
LibvcxErrorKind::WalletMigrationFailed,
299-
e,
300-
))
301-
} else {
302-
setup_wallet(dest_wallet_handle)?;
303-
close_wallet(src_wallet_handle).await?;
304-
Ok(())
305-
}
298+
info!("Closing source and target wallets");
299+
close_wallet(src_wallet_handle).await.ok();
300+
close_wallet(dest_wallet_handle).await.ok();
301+
302+
migration_res.map_err(|e| LibvcxError::from_msg(LibvcxErrorKind::WalletMigrationFailed, e))
306303
}
307304

308305
#[allow(clippy::unwrap_used)]

libvdrtools/indy-api-types/src/domain/wallet/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::{collections::HashMap, fmt};
22

33
use serde_json::value::Value;
44

@@ -75,7 +75,7 @@ pub struct KeyConfig {
7575
pub seed: Option<String>,
7676
}
7777

78-
#[derive(Debug, Serialize, Deserialize)]
78+
#[derive(Serialize, Deserialize)]
7979
pub struct Record {
8080
// Wallet record type
8181
#[serde(rename = "type")]
@@ -88,6 +88,18 @@ pub struct Record {
8888
pub tags: HashMap<String, String>,
8989
}
9090

91+
impl fmt::Debug for Record {
92+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93+
f.debug_struct("Record")
94+
.field("type_", &self.type_)
95+
.field("id", &self.id)
96+
// Censor the value
97+
.field("value", &"******".to_string())
98+
.field("tags", &self.tags)
99+
.finish()
100+
}
101+
}
102+
91103
pub type Tags = HashMap<String, String>;
92104

93105
impl Validatable for Config {

libvdrtools/indy-wallet/src/export_import.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ where
305305
)?;
306306

307307
wallet
308-
.add(&record.type_, &record.id, &record.value, &record.tags)
308+
.add(&record.type_, &record.id, &record.value, &record.tags, true)
309309
.await?;
310310
}
311311

libvdrtools/indy-wallet/src/lib.rs

Lines changed: 121 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use std::{
44
collections::{HashMap, HashSet},
5-
fs,
5+
fmt, fs,
66
io::BufReader,
77
path::PathBuf,
88
sync::{Arc, Mutex},
@@ -18,7 +18,7 @@ use indy_utils::{
1818
crypto::chacha20poly1305_ietf::{self, Key as MasterKey},
1919
secret,
2020
};
21-
use log::{info, trace};
21+
use log::{error, info, trace, warn};
2222
use serde::{Deserialize, Serialize};
2323
use serde_json::Value as SValue;
2424

@@ -44,6 +44,14 @@ mod cache;
4444
mod export_import;
4545
mod wallet;
4646

47+
#[derive(Debug)]
48+
pub struct MigrationResult {
49+
migrated: u32,
50+
skipped: u32,
51+
duplicated: u32,
52+
failed: u32,
53+
}
54+
4755
pub struct WalletService {
4856
storage_types: Mutex<HashMap<String, Arc<dyn WalletStorageType>>>,
4957
wallets: Mutex<HashMap<WalletHandle, Arc<Wallet>>>,
@@ -354,7 +362,7 @@ impl WalletService {
354362
) -> IndyResult<()> {
355363
let wallet = self.get_wallet(wallet_handle).await?;
356364
wallet
357-
.add(type_, name, value, tags)
365+
.add(type_, name, value, tags, true)
358366
.await
359367
.map_err(|err| WalletService::_map_wallet_storage_error(err, type_, name))
360368
}
@@ -706,7 +714,7 @@ impl WalletService {
706714
old_wh: WalletHandle,
707715
new_wh: WalletHandle,
708716
mut migrate_fn: impl FnMut(Record) -> Result<Option<Record>, E>,
709-
) -> IndyResult<()>
717+
) -> IndyResult<MigrationResult>
710718
where
711719
E: std::fmt::Display,
712720
{
@@ -716,53 +724,111 @@ impl WalletService {
716724
let mut records = old_wallet.get_all().await?;
717725
let total = records.get_total_count()?;
718726
info!("Migrating {total:?} records");
719-
let mut num_records = 0;
727+
let mut num_record = 0;
728+
let mut migration_result = MigrationResult {
729+
migrated: 0,
730+
skipped: 0,
731+
duplicated: 0,
732+
failed: 0,
733+
};
720734

721-
while let Some(WalletRecord {
722-
type_,
723-
id,
724-
value,
725-
tags,
726-
}) = records.next().await?
727-
{
728-
num_records += 1;
729-
if num_records % 1000 == 1 {
730-
info!("Migrating wallet record number {num_records} / {total:?}");
735+
while let Some(source_record) = records.next().await? {
736+
num_record += 1;
737+
if num_record % 1000 == 1 {
738+
warn!(
739+
"Migrating wallet record number {num_record} / {total:?}, intermediary \
740+
migration result: ${migration_result:?}"
741+
);
731742
}
743+
trace!("Migrating record: {:?}", source_record);
744+
let unwrapped_type_ = match &source_record.type_ {
745+
None => {
746+
warn!(
747+
"Skipping item missing 'type' field, record ({num_record}): \
748+
{source_record:?}"
749+
);
750+
migration_result.skipped += 1;
751+
continue;
752+
}
753+
Some(type_) => type_.clone(),
754+
};
755+
let unwrapped_value = match &source_record.value {
756+
None => {
757+
warn!(
758+
"Skipping item missing 'value' field, record ({num_record}): \
759+
{source_record:?}"
760+
);
761+
migration_result.skipped += 1;
762+
continue;
763+
}
764+
Some(value) => value.clone(),
765+
};
766+
let unwrapped_tags = match &source_record.tags {
767+
None => HashMap::new(),
768+
Some(tags) => tags.clone(),
769+
};
770+
732771
let record = Record {
733-
type_: type_.ok_or_else(|| {
734-
err_msg(
735-
IndyErrorKind::InvalidState,
736-
"No type fetched for exported record",
737-
)
738-
})?,
739-
id,
740-
value: value.ok_or_else(|| {
741-
err_msg(
742-
IndyErrorKind::InvalidState,
743-
"No value fetched for exported record",
744-
)
745-
})?,
746-
tags: tags.ok_or_else(|| {
747-
err_msg(
748-
IndyErrorKind::InvalidState,
749-
"No tags fetched for exported record",
750-
)
751-
})?,
772+
type_: unwrapped_type_,
773+
id: source_record.id.clone(),
774+
value: unwrapped_value,
775+
tags: unwrapped_tags,
752776
};
753777

754-
if let Some(record) = migrate_fn(record)
755-
.map_err(|e| IndyError::from_msg(IndyErrorKind::InvalidStructure, e.to_string()))?
778+
let migrated_record = match migrate_fn(record) {
779+
Ok(record) => match record {
780+
None => {
781+
warn!("Skipping non-migratable record ({num_record}): {source_record:?}");
782+
migration_result.skipped += 1;
783+
continue;
784+
}
785+
Some(record) => record,
786+
},
787+
Err(err) => {
788+
warn!(
789+
"Skipping item due failed item migration, record ({num_record}): \
790+
{source_record:?}, err: {err}"
791+
);
792+
migration_result.failed += 1;
793+
continue;
794+
}
795+
};
796+
797+
match new_wallet
798+
.add(
799+
&migrated_record.type_,
800+
&migrated_record.id,
801+
&migrated_record.value,
802+
&migrated_record.tags,
803+
false,
804+
)
805+
.await
756806
{
757-
new_wallet
758-
.add(&record.type_, &record.id, &record.value, &record.tags)
759-
.await?;
807+
Err(err) => match err.kind() {
808+
IndyErrorKind::WalletItemAlreadyExists => {
809+
trace!(
810+
"Record type: {migrated_record:?} already exists in destination \
811+
wallet, skipping"
812+
);
813+
migration_result.duplicated += 1;
814+
continue;
815+
}
816+
_ => {
817+
error!(
818+
"Error adding record {migrated_record:?} to destination wallet: \
819+
{err:?}"
820+
);
821+
migration_result.failed += 1;
822+
return Err(err);
823+
}
824+
},
825+
Ok(()) => {
826+
migration_result.migrated += 1;
827+
}
760828
}
761829
}
762-
763-
info!("{num_records} / {total:?} records have been migrated!");
764-
765-
Ok(())
830+
warn!("Migration of total {total:?} records completed, result: ${migration_result:?}");
831+
Ok(migration_result)
766832
}
767833

768834
pub async fn export_wallet(
@@ -1073,7 +1139,7 @@ pub struct MetadataRaw {
10731139
pub keys: Vec<u8>,
10741140
}
10751141

1076-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1142+
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
10771143
pub struct WalletRecord {
10781144
#[serde(rename = "type")]
10791145
type_: Option<String>,
@@ -1082,6 +1148,17 @@ pub struct WalletRecord {
10821148
tags: Option<Tags>,
10831149
}
10841150

1151+
impl fmt::Debug for WalletRecord {
1152+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1153+
f.debug_struct("WalletRecord")
1154+
.field("type_", &self.type_)
1155+
.field("id", &self.id)
1156+
.field("value", &self.value.as_ref().map(|_| "******"))
1157+
.field("tags", &self.tags)
1158+
.finish()
1159+
}
1160+
}
1161+
10851162
impl Ord for WalletRecord {
10861163
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
10871164
(&self.type_, &self.id).cmp(&(&other.type_, &other.id))

libvdrtools/indy-wallet/src/wallet.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ impl Wallet {
165165
name: &str,
166166
value: &str,
167167
tags: &HashMap<String, String>,
168+
cache_record: bool,
168169
) -> IndyResult<()> {
169170
let etype = encrypt_as_searchable(
170171
type_.as_bytes(),
@@ -188,7 +189,9 @@ impl Wallet {
188189
);
189190

190191
self.storage.add(&etype, &ename, &evalue, &etags).await?;
191-
self.cache.add(type_, &etype, &ename, &evalue, &etags);
192+
if cache_record {
193+
self.cache.add(type_, &etype, &ename, &evalue, &etags);
194+
}
192195

193196
Ok(())
194197
}

0 commit comments

Comments
 (0)