mirror of https://github.com/CympleTech/ESSE.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
391 lines
12 KiB
391 lines
12 KiB
use rand::Rng; |
|
use serde::{Deserialize, Serialize}; |
|
use std::time::{SystemTime, UNIX_EPOCH}; |
|
use tdn::types::{ |
|
group::EventId, |
|
primitives::{PeerId, PeerKey, Result}, |
|
}; |
|
use tdn_did::{generate_eth_account, generate_peer, Language}; |
|
use tdn_storage::local::{DStorage, DsValue}; |
|
use web3::signing::Key; |
|
|
|
use esse_primitives::{id_from_str, id_to_str}; |
|
|
|
use crate::apps::wallet::models::{Address, ChainToken}; |
|
use crate::utils::crypto::{ |
|
check_pin, decrypt, decrypt_key, encrypt_key, encrypt_multiple, hash_pin, |
|
}; |
|
|
|
fn _lang_to_i64(lang: Language) -> i64 { |
|
match lang { |
|
Language::English => 0, |
|
Language::SimplifiedChinese => 1, |
|
Language::TraditionalChinese => 2, |
|
Language::Czech => 3, |
|
Language::French => 4, |
|
Language::Italian => 5, |
|
Language::Japanese => 6, |
|
Language::Korean => 7, |
|
Language::Spanish => 8, |
|
Language::Portuguese => 9, |
|
} |
|
} |
|
|
|
pub fn lang_from_i64(u: i64) -> Language { |
|
match u { |
|
0 => Language::English, |
|
1 => Language::SimplifiedChinese, |
|
2 => Language::TraditionalChinese, |
|
3 => Language::Czech, |
|
4 => Language::French, |
|
5 => Language::Italian, |
|
6 => Language::Japanese, |
|
7 => Language::Korean, |
|
8 => Language::Spanish, |
|
9 => Language::Portuguese, |
|
_ => Language::English, |
|
} |
|
} |
|
|
|
pub(crate) struct Account { |
|
pub id: i64, |
|
pub pid: PeerId, |
|
pub index: i64, |
|
pub lang: i64, |
|
pub mnemonic: Vec<u8>, // encrypted value. |
|
pub pass: String, |
|
pub name: String, |
|
pub avatar: Vec<u8>, |
|
pub lock: String, // hashed-lock. |
|
pub secret: Vec<u8>, // encrypted value. |
|
pub encrypt: Vec<u8>, // encrypted encrypt key. |
|
pub wallet: String, // main wallet info. |
|
pub cloud: PeerId, // main cloud service. |
|
pub cloud_key: [u8; 32], // main cloud session key. |
|
pub pub_height: u64, // public information height. |
|
pub own_height: u64, // own data consensus height. |
|
pub event: EventId, |
|
pub datetime: i64, |
|
plainkey: Vec<u8>, |
|
} |
|
|
|
impl Account { |
|
pub fn new( |
|
pid: PeerId, |
|
index: i64, |
|
lang: i64, |
|
pass: String, |
|
name: String, |
|
lock: String, |
|
avatar: Vec<u8>, |
|
mnemonic: Vec<u8>, |
|
secret: Vec<u8>, |
|
encrypt: Vec<u8>, |
|
plainkey: Vec<u8>, |
|
wallet: String, |
|
cloud: PeerId, |
|
cloud_key: [u8; 32], |
|
) -> Self { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
Account { |
|
id: 0, |
|
pub_height: 1, |
|
own_height: 0, |
|
event: EventId::default(), |
|
pid, |
|
index, |
|
lang, |
|
pass, |
|
name, |
|
lock, |
|
mnemonic, |
|
secret, |
|
encrypt, |
|
wallet, |
|
cloud, |
|
cloud_key, |
|
plainkey, |
|
avatar, |
|
datetime, |
|
} |
|
} |
|
|
|
pub fn lang(&self) -> Language { |
|
lang_from_i64(self.lang) |
|
} |
|
|
|
pub fn generate( |
|
index: u32, |
|
salt: &[u8], // &[u8; 32] |
|
rlang: i64, |
|
mnemonic: &str, |
|
pass: &str, |
|
name: &str, |
|
lock: &str, |
|
avatar: Vec<u8>, |
|
) -> Result<(Account, PeerKey, Address)> { |
|
let lang = lang_from_i64(rlang); |
|
let sk = generate_peer( |
|
lang, |
|
mnemonic, |
|
index, |
|
0, // account default multiple address index is 0. |
|
if pass.len() > 0 { Some(pass) } else { None }, |
|
)?; |
|
|
|
// Default ETH wallet account. |
|
let wallet_pass = if pass.len() > 0 { Some(pass) } else { None }; |
|
let wallet_sk = generate_eth_account(lang, mnemonic, index, 0, wallet_pass)?; |
|
let wallet_address = format!("{:?}", (&wallet_sk).address()); |
|
let wallet = ChainToken::ETH.update_main(&wallet_address, ""); |
|
let w = Address::new(ChainToken::ETH, 0, wallet_address, true); |
|
|
|
let key = rand::thread_rng().gen::<[u8; 32]>(); |
|
let ckey = encrypt_key(salt, lock, &key)?; |
|
let mut ebytes = encrypt_multiple( |
|
salt, |
|
lock, |
|
&ckey, |
|
vec![&sk.to_db_bytes(), mnemonic.as_bytes()], |
|
)?; |
|
let mnemonic = ebytes.pop().unwrap_or(vec![]); |
|
let secret = ebytes.pop().unwrap_or(vec![]); |
|
let index = index as i64; |
|
|
|
Ok(( |
|
Account::new( |
|
sk.peer_id(), |
|
index, |
|
rlang, |
|
pass.to_string(), |
|
name.to_string(), |
|
hash_pin(lock)?, |
|
avatar, |
|
mnemonic, |
|
secret, |
|
ckey, |
|
key.to_vec(), |
|
wallet, |
|
PeerId::default(), |
|
[0u8; 32], |
|
), |
|
sk, |
|
w, |
|
)) |
|
} |
|
|
|
pub fn check_lock(&self, lock: &str) -> Result<()> { |
|
if check_pin(lock, &self.lock)? { |
|
Ok(()) |
|
} else { |
|
Err(anyhow!("lock is invalid!")) |
|
} |
|
} |
|
|
|
// when success login, cache plain encrypt key for database use. |
|
pub fn cache_plainkey(&mut self, salt: &[u8], lock: &str) -> Result<()> { |
|
self.plainkey = decrypt_key(salt, lock, &self.encrypt)?; |
|
Ok(()) |
|
} |
|
|
|
pub fn plainkey(&self) -> String { |
|
hex::encode(&self.plainkey) |
|
} |
|
|
|
pub fn pin(&mut self, salt: &[u8], old: &str, new: &str) -> Result<()> { |
|
self.check_lock(old)?; |
|
self.lock = hash_pin(new)?; |
|
let key = decrypt_key(salt, old, &self.encrypt)?; |
|
self.plainkey = key; |
|
self.encrypt = encrypt_key(salt, new, &self.plainkey)?; |
|
|
|
Ok(()) |
|
} |
|
|
|
pub fn mnemonic(&self, salt: &[u8], lock: &str) -> Result<String> { |
|
self.check_lock(lock)?; |
|
let pbytes = decrypt(salt, lock, &self.encrypt, &self.mnemonic)?; |
|
String::from_utf8(pbytes).or(Err(anyhow!("mnemonic unlock invalid."))) |
|
} |
|
|
|
pub fn secret(&self, salt: &[u8], lock: &str) -> Result<PeerKey> { |
|
self.check_lock(lock)?; |
|
let pbytes = decrypt(salt, lock, &self.encrypt, &self.secret)?; |
|
PeerKey::from_db_bytes(&pbytes).or(Err(anyhow!("secret unlock invalid."))) |
|
} |
|
|
|
/// here is zero-copy and unwrap is safe. checked. |
|
fn from_values(mut v: Vec<DsValue>) -> Account { |
|
Account { |
|
datetime: v.pop().unwrap().as_i64(), |
|
event: EventId::from_hex(v.pop().unwrap().as_str()).unwrap_or(EventId::default()), |
|
own_height: v.pop().unwrap().as_i64() as u64, |
|
pub_height: v.pop().unwrap().as_i64() as u64, |
|
cloud_key: hex::decode(v.pop().unwrap().as_str()) |
|
.map(|bytes| { |
|
let mut key = [0u8; 32]; |
|
key.copy_from_slice(&bytes); |
|
key |
|
}) |
|
.unwrap_or([0u8; 32]), |
|
cloud: PeerId::from_hex(v.pop().unwrap().as_str()).unwrap_or(PeerId::default()), |
|
wallet: v.pop().unwrap().as_string(), |
|
avatar: base64::decode(v.pop().unwrap().as_str()).unwrap_or(vec![]), |
|
encrypt: base64::decode(v.pop().unwrap().as_str()).unwrap_or(vec![]), |
|
secret: base64::decode(v.pop().unwrap().as_str()).unwrap_or(vec![]), |
|
mnemonic: base64::decode(v.pop().unwrap().as_str()).unwrap_or(vec![]), |
|
lock: v.pop().unwrap().as_string(), |
|
name: v.pop().unwrap().as_string(), |
|
pass: v.pop().unwrap().as_string(), |
|
lang: v.pop().unwrap().as_i64(), |
|
index: v.pop().unwrap().as_i64(), |
|
pid: id_from_str(v.pop().unwrap().as_str()).unwrap_or(PeerId::default()), |
|
id: v.pop().unwrap().as_i64(), |
|
plainkey: vec![], |
|
} |
|
} |
|
|
|
pub fn get(db: &DStorage, pid: &PeerId) -> Result<Account> { |
|
let sql = format!( |
|
"SELECT id, pid, indx, lang, pass, name, lock, mnemonic, secret, encrypt, avatar, wallet, cloud, cloud_key, pub_height, own_height, event, datetime FROM accounts WHERE pid = '{}'", |
|
id_to_str(pid) |
|
); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
Ok(Account::from_values(values)) |
|
} else { |
|
Err(anyhow!("account is missing.")) |
|
} |
|
} |
|
|
|
pub fn all(db: &DStorage) -> Result<Vec<Account>> { |
|
let matrix = db.query( |
|
"SELECT id, pid, indx, lang, pass, name, lock, mnemonic, secret, encrypt, avatar, wallet, cloud, cloud_key, pub_height, own_height, event, datetime FROM accounts ORDER BY datetime DESC", |
|
)?; |
|
let mut accounts = vec![]; |
|
for values in matrix { |
|
accounts.push(Account::from_values(values)); |
|
} |
|
Ok(accounts) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let mut unique_check = db.query(&format!( |
|
"SELECT id from accounts WHERE pid = '{}'", |
|
id_to_str(&self.pid) |
|
))?; |
|
if unique_check.len() > 0 { |
|
let id = unique_check.pop().unwrap().pop().unwrap().as_i64(); |
|
self.id = id; |
|
self.update(db)?; |
|
} else { |
|
let sql = format!("INSERT INTO accounts (pid, indx, lang, pass, name, lock, mnemonic, secret, encrypt, avatar, wallet, cloud, cloud_key, pub_height, own_height, event, datetime) VALUES ('{}', {}, {}, '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', {}, {}, '{}', {})", |
|
id_to_str(&self.pid), |
|
self.index, |
|
self.lang, |
|
self.pass, |
|
self.name, |
|
self.lock, |
|
base64::encode(&self.mnemonic), |
|
base64::encode(&self.secret), |
|
base64::encode(&self.encrypt), |
|
base64::encode(&self.avatar), |
|
self.wallet, |
|
self.cloud.to_hex(), |
|
hex::encode(&self.cloud_key), |
|
self.pub_height, |
|
self.own_height, |
|
self.event.to_hex(), |
|
self.datetime, |
|
); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
} |
|
Ok(()) |
|
} |
|
|
|
pub fn update(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!("UPDATE accounts SET name='{}', lock='{}', encrypt='{}', avatar='{}', wallet='{}', cloud='{}', cloud_key='{}', pub_height={}, own_height={}, event='{}', datetime={} WHERE id = {}", |
|
self.name, |
|
self.lock, |
|
base64::encode(&self.encrypt), |
|
base64::encode(&self.avatar), |
|
self.wallet, |
|
self.cloud.to_hex(), |
|
hex::encode(&self.cloud_key), |
|
self.pub_height, |
|
self.own_height, |
|
self.datetime, |
|
self.event.to_hex(), |
|
self.id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn update_info(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE accounts SET name='{}', avatar='{}', wallet='{}', cloud='{}', cloud_key='{}', pub_height={} WHERE id = {}", |
|
self.name, |
|
base64::encode(&self.avatar), |
|
self.wallet, |
|
self.cloud.to_hex(), |
|
hex::encode(&self.cloud_key), |
|
self.pub_height, |
|
self.id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn _delete(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!("DELETE FROM accounts WHERE id = {}", self.id); |
|
db.delete(&sql) |
|
} |
|
|
|
pub fn update_consensus(&mut self, db: &DStorage, height: u64, eid: EventId) -> Result<usize> { |
|
self.own_height = height; |
|
self.event = eid; |
|
let sql = format!( |
|
"UPDATE accounts SET own_height={}, event='{}' WHERE id = {}", |
|
self.own_height, |
|
self.event.to_hex(), |
|
self.id, |
|
); |
|
db.update(&sql) |
|
} |
|
} |
|
|
|
#[derive(Serialize, Deserialize, Clone)] |
|
pub(crate) struct User { |
|
pub height: u64, |
|
pub name: String, |
|
pub wallet: String, |
|
pub cloud: PeerId, |
|
pub cloud_key: [u8; 32], |
|
pub avatar: Vec<u8>, |
|
} |
|
|
|
impl User { |
|
pub fn info( |
|
height: u64, |
|
name: String, |
|
wallet: String, |
|
cloud: PeerId, |
|
cloud_key: [u8; 32], |
|
avatar: Vec<u8>, |
|
) -> Self { |
|
Self { |
|
height, |
|
name, |
|
wallet, |
|
cloud, |
|
cloud_key, |
|
avatar, |
|
} |
|
} |
|
}
|
|
|