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

Supporting FOG enabled accounts #254

Merged
merged 19 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
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