Skip to content

Commit 4be78ff

Browse files
committed
Make the the software wallet support the old Store format.
1 parent 24fcb3b commit 4be78ff

File tree

3 files changed

+151
-16
lines changed

3 files changed

+151
-16
lines changed

crates/wallet/src/keys.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Cryptographic keys for digital signatures support for the wallet.
22
3-
use std::fmt::Display;
3+
use std::fmt::{Display, Error, Formatter};
44
use std::marker::PhantomData;
55
use std::str::FromStr;
66

@@ -24,6 +24,55 @@ pub type DatedViewingKey = DatedKeypair<ExtendedViewingKey>;
2424
/// Type alias for a spending key with a birthday.
2525
pub type DatedSpendingKey = DatedKeypair<ExtendedSpendingKey>;
2626

27+
/// Extended spending key with Borsh serialization compatible with
28+
/// DatedSpendingKey. This is necessary to facilitate reading the old Store
29+
/// format.
30+
#[derive(Clone, Debug)]
31+
pub struct StoreSpendingKey(ExtendedSpendingKey);
32+
33+
impl FromStr for StoreSpendingKey {
34+
type Err = <ExtendedSpendingKey as FromStr>::Err;
35+
36+
fn from_str(s: &str) -> Result<Self, Self::Err> {
37+
ExtendedSpendingKey::from_str(s).map(Self)
38+
}
39+
}
40+
41+
impl Display for StoreSpendingKey {
42+
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
43+
self.0.fmt(f)
44+
}
45+
}
46+
47+
impl BorshDeserialize for StoreSpendingKey {
48+
fn deserialize_reader<R: std::io::Read>(
49+
reader: &mut R,
50+
) -> std::io::Result<Self> {
51+
DatedSpendingKey::deserialize_reader(reader).map(|x| Self(x.key))
52+
}
53+
}
54+
55+
impl BorshSerialize for StoreSpendingKey {
56+
fn serialize<W: std::io::Write>(
57+
&self,
58+
writer: &mut W,
59+
) -> std::io::Result<()> {
60+
BorshSerialize::serialize(&DatedSpendingKey::new(self.0, None), writer)
61+
}
62+
}
63+
64+
impl From<ExtendedSpendingKey> for StoreSpendingKey {
65+
fn from(key: ExtendedSpendingKey) -> Self {
66+
Self(key)
67+
}
68+
}
69+
70+
impl From<StoreSpendingKey> for ExtendedSpendingKey {
71+
fn from(key: StoreSpendingKey) -> Self {
72+
key.0
73+
}
74+
}
75+
2776
/// A keypair stored in a wallet
2877
#[derive(Debug)]
2978
pub enum StoredKeypair<T: BorshSerialize + BorshDeserialize + Display + FromStr>
@@ -371,6 +420,14 @@ impl<T: BorshSerialize + BorshDeserialize> EncryptedKeypair<T> {
371420
T::try_from_slice(&decrypted_data)
372421
.map_err(|_| DecryptionError::DeserializingError)
373422
}
423+
424+
/// Change the type held by this encrypted key pair. This is only safe when
425+
/// the new and old types have the same Borsh serialization.
426+
pub fn map<U: BorshSerialize + BorshDeserialize>(
427+
self,
428+
) -> EncryptedKeypair<U> {
429+
EncryptedKeypair(self.0, PhantomData)
430+
}
374431
}
375432

376433
/// Keypair encryption salt

crates/wallet/src/lib.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use zeroize::Zeroizing;
3434
pub use self::derivation_path::{DerivationPath, DerivationPathError};
3535
pub use self::keys::{
3636
DatedKeypair, DatedSpendingKey, DatedViewingKey, DecryptionError,
37-
StoredKeypair,
37+
StoreSpendingKey, StoredKeypair,
3838
};
3939
pub use self::store::{ConfirmationResponse, ValidatorData, ValidatorKeys};
4040
use crate::store::{derive_hd_secret_key, derive_hd_spending_key};
@@ -520,7 +520,7 @@ impl<U> Wallet<U> {
520520
/// Get all known viewing keys by their alias
521521
pub fn get_spending_keys(
522522
&self,
523-
) -> HashMap<String, &StoredKeypair<ExtendedSpendingKey>> {
523+
) -> HashMap<String, &StoredKeypair<StoreSpendingKey>> {
524524
self.store
525525
.get_spending_keys()
526526
.iter()
@@ -905,7 +905,7 @@ impl<U: WalletIo> Wallet<U> {
905905
.ok_or_else(|| {
906906
FindKeyError::KeyNotFound(alias_pkh_or_pk.as_ref().to_string())
907907
})?;
908-
Self::decrypt_stored_key::<_>(
908+
Self::decrypt_stored_key::<_, _>(
909909
&mut self.decrypted_key_cache,
910910
stored_key,
911911
alias_pkh_or_pk.into(),
@@ -967,7 +967,7 @@ impl<U: WalletIo> Wallet<U> {
967967
.ok_or_else(|| {
968968
FindKeyError::KeyNotFound(alias.as_ref().to_string())
969969
})?;
970-
Self::decrypt_stored_key::<_>(
970+
Self::decrypt_stored_key::<_, _>(
971971
&mut self.decrypted_spendkey_cache,
972972
stored_spendkey,
973973
alias.into(),
@@ -1049,13 +1049,14 @@ impl<U: WalletIo> Wallet<U> {
10491049
/// supplied, then interactively prompt for password and if successfully
10501050
/// decrypted, store it in a cache.
10511051
fn decrypt_stored_key<
1052-
T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone,
1052+
V: Clone,
1053+
T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone + Into<V>,
10531054
>(
1054-
decrypted_key_cache: &mut HashMap<Alias, T>,
1055+
decrypted_key_cache: &mut HashMap<Alias, V>,
10551056
stored_key: &StoredKeypair<T>,
10561057
alias: Alias,
10571058
password: Option<Zeroizing<String>>,
1058-
) -> Result<T, FindKeyError>
1059+
) -> Result<V, FindKeyError>
10591060
where
10601061
<T as std::str::FromStr>::Err: Display,
10611062
{
@@ -1084,13 +1085,13 @@ impl<U: WalletIo> Wallet<U> {
10841085
}
10851086
.map_err(FindKeyError::KeyDecryptionError)?;
10861087

1087-
decrypted_key_cache.insert(alias.clone(), key);
1088+
decrypted_key_cache.insert(alias.clone(), key.into());
10881089
decrypted_key_cache
10891090
.get(&alias)
10901091
.cloned()
10911092
.ok_or_else(|| FindKeyError::KeyNotFound(alias.to_string()))
10921093
}
1093-
StoredKeypair::Raw(raw) => Ok(raw.clone()),
1094+
StoredKeypair::Raw(raw) => Ok(raw.clone().into()),
10941095
}
10951096
}
10961097

crates/wallet/src/store.rs

+83-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use zeroize::Zeroizing;
2222
use super::alias::{self, Alias};
2323
use super::derivation_path::DerivationPath;
2424
use super::pre_genesis;
25-
use crate::{StoredKeypair, WalletIo};
25+
use crate::{StoreSpendingKey, StoredKeypair, WalletIo};
2626

2727
/// Actions that can be taken when there is an alias conflict
2828
pub enum ConfirmationResponse {
@@ -67,7 +67,7 @@ pub struct Store {
6767
/// Known viewing keys
6868
view_keys: BTreeMap<Alias, ExtendedViewingKey>,
6969
/// Known spending keys
70-
spend_keys: BTreeMap<Alias, StoredKeypair<ExtendedSpendingKey>>,
70+
spend_keys: BTreeMap<Alias, StoredKeypair<StoreSpendingKey>>,
7171
/// Payment address book
7272
payment_addrs: BiBTreeMap<Alias, PaymentAddress>,
7373
/// Cryptographic keypairs
@@ -136,7 +136,7 @@ impl Store {
136136
pub fn find_spending_key(
137137
&self,
138138
alias: impl AsRef<str>,
139-
) -> Option<&StoredKeypair<ExtendedSpendingKey>> {
139+
) -> Option<&StoredKeypair<StoreSpendingKey>> {
140140
self.spend_keys.get(&alias.into())
141141
}
142142

@@ -283,7 +283,7 @@ impl Store {
283283
/// Get all known spending keys by their alias.
284284
pub fn get_spending_keys(
285285
&self,
286-
) -> &BTreeMap<Alias, StoredKeypair<ExtendedSpendingKey>> {
286+
) -> &BTreeMap<Alias, StoredKeypair<StoreSpendingKey>> {
287287
&self.spend_keys
288288
}
289289

@@ -422,7 +422,7 @@ impl Store {
422422
self.remove_alias(&alias);
423423

424424
let (spendkey_to_store, _raw_spendkey) =
425-
StoredKeypair::new(spendkey, password);
425+
StoredKeypair::new(spendkey.into(), password);
426426
self.spend_keys.insert(alias.clone(), spendkey_to_store);
427427
// Simultaneously add the derived viewing key to ease balance viewing
428428
birthday.map(|x| self.birthdays.insert(alias.clone(), x));
@@ -735,7 +735,13 @@ impl Store {
735735

736736
/// Decode a Store from the given bytes
737737
pub fn decode(data: Vec<u8>) -> Result<Self, toml::de::Error> {
738-
toml::from_slice(&data)
738+
// First try to decode Store from current version (with separate
739+
// birthdays)
740+
toml::from_slice(&data).or_else(
741+
// Otherwise try to decode Store from older version (with
742+
// integrated birthdays)
743+
|_| toml::from_slice::<StoreV0>(&data).map(Into::into),
744+
)
739745
}
740746

741747
/// Encode a store into a string of bytes
@@ -835,6 +841,77 @@ impl<'de> Deserialize<'de> for AddressVpType {
835841
}
836842
}
837843

844+
// A Storage area for keys and addresses. This is a deprecated format but it
845+
// is required for compatability purposes.
846+
#[derive(Serialize, Deserialize, Debug, Default)]
847+
pub struct StoreV0 {
848+
/// Known viewing keys
849+
view_keys: BTreeMap<Alias, crate::DatedViewingKey>,
850+
/// Known spending keys
851+
spend_keys: BTreeMap<Alias, StoredKeypair<crate::DatedSpendingKey>>,
852+
/// Payment address book
853+
payment_addrs: BiBTreeMap<Alias, PaymentAddress>,
854+
/// Cryptographic keypairs
855+
secret_keys: BTreeMap<Alias, StoredKeypair<common::SecretKey>>,
856+
/// Known public keys
857+
public_keys: BTreeMap<Alias, common::PublicKey>,
858+
/// Known derivation paths
859+
derivation_paths: BTreeMap<Alias, DerivationPath>,
860+
/// Namada address book
861+
addresses: BiBTreeMap<Alias, Address>,
862+
/// Known mappings of public key hashes to their aliases in the `keys`
863+
/// field. Used for look-up by a public key.
864+
pkhs: BTreeMap<PublicKeyHash, Alias>,
865+
/// Special keys if the wallet belongs to a validator
866+
pub(crate) validator_data: Option<ValidatorData>,
867+
/// Namada address vp type
868+
address_vp_types: BTreeMap<AddressVpType, HashSet<Address>>,
869+
}
870+
871+
impl From<StoreV0> for Store {
872+
fn from(store: StoreV0) -> Self {
873+
let mut to = Store {
874+
payment_addrs: store.payment_addrs,
875+
secret_keys: store.secret_keys,
876+
public_keys: store.public_keys,
877+
derivation_paths: store.derivation_paths,
878+
addresses: store.addresses,
879+
pkhs: store.pkhs,
880+
validator_data: store.validator_data,
881+
address_vp_types: store.address_vp_types,
882+
..Store::default()
883+
};
884+
for (alias, key) in store.view_keys {
885+
// Extract the birthday into the birthdays map
886+
to.birthdays.insert(alias.clone(), key.birthday);
887+
// Extrat the key into the viewing keys map
888+
to.view_keys.insert(alias, key.key);
889+
}
890+
for (alias, key) in store.spend_keys {
891+
match key {
892+
StoredKeypair::Raw(key) => {
893+
// Extract the birthday into the birthdays map
894+
to.birthdays.insert(alias.clone(), key.birthday);
895+
// Extract the key into the spending keys map
896+
to.spend_keys
897+
.insert(alias, StoredKeypair::Raw(key.key.into()));
898+
}
899+
StoredKeypair::Encrypted(key) => {
900+
// This map is fine because DatedSpendingKey has the same
901+
// Borsh serialization as StoreSpendingKey
902+
to.spend_keys.insert(
903+
alias,
904+
StoredKeypair::Encrypted(key.map::<StoreSpendingKey>()),
905+
);
906+
// Here we assume the birthday for the current alias is
907+
// already given in a viewing key with the same alias.
908+
}
909+
}
910+
}
911+
to
912+
}
913+
}
914+
838915
#[cfg(test)]
839916
mod test_wallet {
840917
use base58::FromBase58;

0 commit comments

Comments
 (0)