Skip to content

Commit

Permalink
Supporting FOG enabled accounts (#254)
Browse files Browse the repository at this point in the history
Co-authored-by: Colin Carey <colin.carey1@gmail.com>
  • Loading branch information
2 people authored and christian-oudard committed Feb 18, 2022
1 parent 6811814 commit 58bc086
Show file tree
Hide file tree
Showing 18 changed files with 343 additions and 103 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions full-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mc-util-parse = { path = "../mobilecoin/util/parse" }
mc-util-serial = { path = "../mobilecoin/util/serial", default-features = false }
mc-util-uri = { path = "../mobilecoin/util/uri" }

base64 = "0.13.0"
chrono = { version = "0.4", default-features = false, features = ["alloc"] }
crossbeam-channel = "0.5"
diesel = { version = "1.4.6", features = ["sqlcipher-bundled"] }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE accounts DROP COLUMN fog_enabled;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE accounts ADD COLUMN fog_enabled BOOLEAN NOT NULL DEFAULT FALSE;
131 changes: 106 additions & 25 deletions full-service/src/db/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub trait AccountModel {
import_block_index: Option<u64>,
next_subaddress_index: Option<u64>,
name: &str,
fog_enabled: bool,
conn: &PooledConnection<ConnectionManager<SqliteConnection>>,
) -> Result<(AccountID, String), WalletDbError>;

Expand Down Expand Up @@ -183,14 +184,13 @@ impl AccountModel for Account {
fog_authority_spki: Option<String>,
conn: &PooledConnection<ConnectionManager<SqliteConnection>>,
) -> Result<(AccountID, String), WalletDbError> {
let account_key = Slip10Key::from(mnemonic.clone())
.try_into_account_key(
&fog_report_url.unwrap_or_else(|| "".to_string()),
&fog_report_id.unwrap_or_else(|| "".to_string()),
&hex::decode(fog_authority_spki.unwrap_or_else(|| "".to_string()))
.expect("invalid spki"),
)
.unwrap();
let fog_enabled = fog_report_url.is_some();

let account_key = Slip10Key::from(mnemonic.clone()).try_into_account_key(
&fog_report_url.unwrap_or_else(|| "".to_string()),
&fog_report_id.unwrap_or_else(|| "".to_string()),
&base64::decode(fog_authority_spki.unwrap_or_else(|| "".to_string()))?,
)?;

Account::create(
mnemonic.entropy(),
Expand All @@ -200,6 +200,7 @@ impl AccountModel for Account {
import_block_index,
next_subaddress_index,
name,
fog_enabled,
conn,
)
}
Expand All @@ -215,12 +216,16 @@ impl AccountModel for Account {
fog_authority_spki: Option<String>,
conn: &PooledConnection<ConnectionManager<SqliteConnection>>,
) -> Result<(AccountID, String), WalletDbError> {
let fog_enabled = fog_report_url.is_some();

let root_id = RootIdentity {
root_entropy: entropy.clone(),
fog_report_url: fog_report_url.unwrap_or_else(|| "".to_string()),
fog_report_id: fog_report_id.unwrap_or_else(|| "".to_string()),
fog_authority_spki: hex::decode(fog_authority_spki.unwrap_or_else(|| "".to_string()))
.expect("invalid spki"),
fog_authority_spki: base64::decode(
fog_authority_spki.unwrap_or_else(|| "".to_string()),
)
.expect("invalid spki"),
};
let account_key = AccountKey::from(&root_id);

Expand All @@ -232,6 +237,7 @@ impl AccountModel for Account {
import_block_index,
next_subaddress_index,
name,
fog_enabled,
conn,
)
}
Expand All @@ -244,27 +250,40 @@ impl AccountModel for Account {
import_block_index: Option<u64>,
next_subaddress_index: Option<u64>,
name: &str,
fog_enabled: bool,
conn: &PooledConnection<ConnectionManager<SqliteConnection>>,
) -> Result<(AccountID, String), WalletDbError> {
use crate::db::schema::accounts;

let account_id = AccountID::from(account_key);
let fb = first_block_index.unwrap_or(DEFAULT_FIRST_BLOCK_INDEX);

let change_subaddress_index = if fog_enabled {
DEFAULT_SUBADDRESS_INDEX as i64
} else {
DEFAULT_CHANGE_SUBADDRESS_INDEX as i64
};

let next_subaddress_index = if fog_enabled {
1
} else {
next_subaddress_index.unwrap_or(DEFAULT_NEXT_SUBADDRESS_INDEX) as i64
};

let new_account = NewAccount {
account_id_hex: &account_id.to_string(),
account_key: &mc_util_serial::encode(account_key), /* FIXME: WS-6 - add
* encryption */
entropy,
key_derivation_version: key_derivation_version as i32,
main_subaddress_index: DEFAULT_SUBADDRESS_INDEX as i64,
change_subaddress_index: DEFAULT_CHANGE_SUBADDRESS_INDEX as i64,
next_subaddress_index: next_subaddress_index.unwrap_or(DEFAULT_NEXT_SUBADDRESS_INDEX)
as i64,
change_subaddress_index,
next_subaddress_index,
first_block_index: fb as i64,
next_block_index: fb as i64,
import_block_index: import_block_index.map(|i| i as i64),
name,
fog_enabled,
};

diesel::insert_into(accounts::table)
Expand All @@ -279,18 +298,19 @@ impl AccountModel for Account {
"Main",
conn,
)?;

let _change_subaddress_b58 = AssignedSubaddress::create(
account_key,
None, /* FIXME: WS-8 - Address Book Entry if details provided, or None
* always for main? */
DEFAULT_CHANGE_SUBADDRESS_INDEX,
"Change",
conn,
)?;

for subaddress_index in 2..next_subaddress_index.unwrap_or(DEFAULT_NEXT_SUBADDRESS_INDEX) {
AssignedSubaddress::create(account_key, None, subaddress_index, "", conn)?;
if !fog_enabled {
AssignedSubaddress::create(
account_key,
None, /* FIXME: WS-8 - Address Book Entry if details provided, or None
* always for main? */
DEFAULT_CHANGE_SUBADDRESS_INDEX,
"Change",
conn,
)?;

for subaddress_index in 2..next_subaddress_index {
AssignedSubaddress::create(account_key, None, subaddress_index as u64, "", conn)?;
}
}

Ok((account_id, main_subaddress_b58))
Expand Down Expand Up @@ -501,6 +521,7 @@ mod tests {
next_block_index: 0,
import_block_index: None,
name: "Alice's Main Account".to_string(),
fog_enabled: false,
};
assert_eq!(expected_account, acc);

Expand Down Expand Up @@ -563,6 +584,7 @@ mod tests {
next_block_index: 51,
import_block_index: Some(50),
name: "".to_string(),
fog_enabled: false,
};
assert_eq!(expected_account_secondary, acc_secondary);

Expand Down Expand Up @@ -630,4 +652,63 @@ mod tests {
let decoded_account_key: AccountKey = mc_util_serial::decode(&account.account_key).unwrap();
assert_eq!(decoded_account_key, account_key);
}

#[test_with_logger]
fn test_create_fog_account(logger: Logger) {
let mut rng: StdRng = SeedableRng::from_seed([20u8; 32]);

let db_test_context = WalletDbTestContext::default();
let wallet_db = db_test_context.get_db_instance(logger);

let root_id = RootIdentity::from_random(&mut rng);
let account_id_hex = {
let conn = wallet_db.get_conn().unwrap();
let (account_id_hex, _public_address_b58) = Account::create_from_root_entropy(
&root_id.root_entropy,
Some(0),
None,
None,
"Alice's FOG Account",
Some("fog//some.fog.url".to_string()),
Some("".to_string()),
Some("DefinitelyARealFOGAuthoritySPKI".to_string()),
&conn,
)
.unwrap();
account_id_hex
};

{
let conn = wallet_db.get_conn().unwrap();
let res = Account::list_all(&conn).unwrap();
assert_eq!(res.len(), 1);
}

let acc = Account::get(&account_id_hex, &wallet_db.get_conn().unwrap()).unwrap();
let expected_account = Account {
id: 1,
account_id_hex: account_id_hex.to_string(),
account_key: [
10, 34, 10, 32, 129, 223, 141, 215, 200, 104, 120, 117, 123, 154, 151, 210, 253,
23, 148, 151, 2, 18, 182, 100, 83, 138, 144, 99, 225, 74, 214, 14, 175, 68, 167, 4,
18, 34, 10, 32, 24, 98, 18, 92, 9, 50, 142, 184, 114, 99, 34, 125, 211, 54, 146,
33, 98, 71, 179, 56, 136, 67, 98, 97, 230, 228, 31, 194, 119, 169, 189, 8, 26, 17,
102, 111, 103, 47, 47, 115, 111, 109, 101, 46, 102, 111, 103, 46, 117, 114, 108,
42, 23, 13, 231, 226, 158, 43, 94, 151, 32, 17, 121, 169, 69, 56, 96, 46, 182, 26,
43, 138, 220, 146, 60, 162,
]
.to_vec(),
entropy: root_id.root_entropy.bytes.to_vec(),
key_derivation_version: 1,
main_subaddress_index: 0,
change_subaddress_index: 0,
next_subaddress_index: 1,
first_block_index: 0,
next_block_index: 0,
import_block_index: None,
name: "Alice's FOG Account".to_string(),
fog_enabled: true,
};
assert_eq!(expected_account, acc);
}
}
4 changes: 4 additions & 0 deletions full-service/src/db/assigned_subaddress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ impl AssignedSubaddressModel for AssignedSubaddress {

let account = Account::get(&AccountID(account_id_hex.to_string()), conn)?;

if account.fog_enabled {
return Err(WalletDbError::SubaddressesNotSupportedForFOGEnabledAccounts);
}

let account_key: AccountKey = mc_util_serial::decode(&account.account_key)?;
let account_view_key = account_key.view_key();
let subaddress_index = account.next_subaddress_index;
Expand Down
3 changes: 3 additions & 0 deletions full-service/src/db/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ pub struct Account {
pub import_block_index: Option<i64>,
/// Name of this account.
pub name: String, /* empty string for nullable */
/// Fog enabled address
pub fog_enabled: bool,
}

/// A structure that can be inserted to create a new entity in the `accounts`
Expand All @@ -118,6 +120,7 @@ pub struct NewAccount<'a> {
pub next_block_index: i64,
pub import_block_index: Option<i64>,
pub name: &'a str,
pub fog_enabled: bool,
}

/// A transaction output entity that either was received to an Account in this
Expand Down
1 change: 1 addition & 0 deletions full-service/src/db/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ table! {
next_block_index -> BigInt,
import_block_index -> Nullable<BigInt>,
name -> Text,
fog_enabled -> Bool,
}
}

Expand Down
21 changes: 21 additions & 0 deletions full-service/src/db/wallet_db_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ pub enum WalletDbError {

/// Error converting to/from API protos: {0}
ProtoConversion(mc_api::ConversionError),

/// Error while generating a Slip10Key: {0}
Slip10Key(mc_account_keys_slip10::Error),

/// Decode from Base64 error: {0}
Base64Decode(base64::DecodeError),

/// Subaddresses are not supported for FOG enabled accounts
SubaddressesNotSupportedForFOGEnabledAccounts,
}

impl From<diesel::result::Error> for WalletDbError {
Expand Down Expand Up @@ -168,3 +177,15 @@ impl From<mc_ledger_db::Error> for WalletDbError {
Self::LedgerDB(src)
}
}

impl From<mc_account_keys_slip10::Error> for WalletDbError {
fn from(src: mc_account_keys_slip10::Error) -> Self {
Self::Slip10Key(src)
}
}

impl From<base64::DecodeError> for WalletDbError {
fn from(src: base64::DecodeError) -> Self {
Self::Base64Decode(src)
}
}
7 changes: 7 additions & 0 deletions full-service/src/json_rpc/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ pub struct Account {
/// found TXOs. It is recommended to move all MOB to another account after
/// recovery if the user is unsure of the assigned addresses.
pub recovery_mode: bool,

/// A flag that indicates if this account is FOG enabled, which means that
/// it will send any change to it's main subaddress (index 0) instead of
/// the default change subaddress (index 1). It also generates
/// PublicAddressB58's with fog credentials.
pub fog_enabled: bool,
}

impl TryFrom<&db::models::Account> for Account {
Expand All @@ -68,6 +74,7 @@ impl TryFrom<&db::models::Account> for Account {
first_block_index: (src.first_block_index as u64).to_string(),
next_block_index: (src.next_block_index as u64).to_string(),
recovery_mode: false,
fog_enabled: src.fog_enabled,
})
}
}
Loading

0 comments on commit 58bc086

Please sign in to comment.