use deadpool_postgres::Pool; use sha3::{Sha3_384, Digest}; use sha2::Sha256; use hmac::{Hmac, Mac}; use pbkdf2::pbkdf2_hmac; use aes_gcm::{ aead::{Aead, AeadCore, KeyInit, OsRng}, Aes256Gcm, Key, Nonce, }; use rand::RngCore; use zeroize::Zeroize; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use tracing::{info, warn}; use crate::errors::AppError; use crate::models::wallet::{CreateWalletRequest, WalletResponse, AssetInfo}; /// NAC原生地址类型 (32字节) pub struct NacAddress([u8; 32]); impl NacAddress { /// 从公钥派生NAC原生地址 /// 使用SHA3-384哈希后取前32字节,符合NAC原生类型系统 pub fn from_public_key(public_key: &[u8]) -> Self { let mut hasher = Sha3_384::new(); hasher.update(b"NAC_ADDRESS_V1"); // 域分隔符,防止哈希碰撞 hasher.update(public_key); let hash = hasher.finalize(); let mut addr = [0u8; 32]; addr.copy_from_slice(&hash[..32]); NacAddress(addr) } pub fn to_hex(&self) -> String { format!("0x{}", hex::encode(self.0)) } pub fn as_bytes(&self) -> &[u8; 32] { &self.0 } } /// 助记词加密结果 struct EncryptedMnemonic { ciphertext: Vec, salt: Vec, iv: Vec, } /// 使用用户密码加密助记词 /// 算法: PBKDF2(SHA256, password, salt, 100000次) -> AES-256-GCM fn encrypt_mnemonic(mnemonic: &str, password: &str) -> Result { // 生成随机盐值 (32字节) let mut salt = vec![0u8; 32]; OsRng.fill_bytes(&mut salt); // PBKDF2派生密钥 (100000次迭代,符合NIST推荐) let mut key_bytes = [0u8; 32]; pbkdf2_hmac::( password.as_bytes(), &salt, 100_000, &mut key_bytes, ); // AES-256-GCM加密 let key = Key::::from_slice(&key_bytes); let cipher = Aes256Gcm::new(key); let nonce = Aes256Gcm::generate_nonce(&mut OsRng); let ciphertext = cipher .encrypt(&nonce, mnemonic.as_bytes()) .map_err(|_| AppError::Internal)?; // 清零密钥材料(安全要求) let mut key_bytes_mut = key_bytes; key_bytes_mut.zeroize(); Ok(EncryptedMnemonic { ciphertext, salt, iv: nonce.to_vec(), }) } /// 生成12个单词的BIP39助记词 fn generate_mnemonic() -> Result { // 生成128位熵 (对应12个单词) let mut entropy = [0u8; 16]; OsRng.fill_bytes(&mut entropy); // 使用bip39库生成助记词 let mnemonic = bip39::Mnemonic::from_entropy(&entropy) .map_err(|_| AppError::Internal)?; Ok(mnemonic.to_string()) } /// 从助记词派生NAC原生密钥对 fn derive_nac_keypair(mnemonic_str: &str) -> Result<(Vec, Vec), AppError> { let mnemonic = bip39::Mnemonic::parse_normalized(mnemonic_str) .map_err(|_| AppError::Internal)?; // 生成种子 let seed = mnemonic.to_seed(""); // NAC原生派生路径: m/44'/9999'/0'/0/0 // 9999是NAC公链的BIP44 coin type let path = "m/44'/9999'/0'/0/0"; // 使用HMAC-SHA3-384进行密钥派生(NAC原生,不使用HMAC-SHA512) use hmac::Mac; let mut mac = as Mac>::new_from_slice(b"NAC seed") .map_err(|_| AppError::Internal)?; mac.update(&seed); let result = mac.finalize().into_bytes(); // 私钥取前32字节 let mut private_key = vec![0u8; 32]; private_key.copy_from_slice(&result[..32]); // 使用ed25519-dalek生成公钥 let signing_key = ed25519_dalek::SigningKey::from_bytes( private_key.as_slice().try_into().map_err(|_| AppError::Internal)? ); let public_key = signing_key.verifying_key().to_bytes().to_vec(); // 清零私钥(公钥派生完成后) private_key.zeroize(); // 返回公钥和签名密钥的字节(加密后存储) Ok((public_key, signing_key.to_bytes().to_vec())) } /// 创建新钱包 pub async fn create_wallet( pool: &Pool, req: &CreateWalletRequest, ) -> Result { let client = pool.get().await.map_err(|e| { tracing::error!("数据库连接获取失败: {}", e); AppError::Database })?; // 检查用户是否已有钱包 let existing = client .query_opt( "SELECT id FROM wallets WHERE user_id = $1", &[&req.user_id], ) .await .map_err(|_| AppError::Database)?; if existing.is_some() { return Err(AppError::WalletAlreadyExists); } // 生成助记词 let mnemonic = generate_mnemonic()?; // 派生密钥对 let (public_key, mut signing_key_bytes) = derive_nac_keypair(&mnemonic)?; // 派生NAC原生地址 let nac_address = NacAddress::from_public_key(&public_key); // 加密助记词 let encrypted = encrypt_mnemonic(&mnemonic, &req.encryption_password)?; // 存储到数据库 let row = client .query_one( r#" INSERT INTO wallets ( user_id, address, address_hex, public_key, encrypted_mnemonic, encryption_salt, encryption_iv, wallet_type, kyc_level ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, address_hex, created_at "#, &[ &req.user_id, &nac_address.as_bytes().as_slice(), &nac_address.to_hex(), &public_key.as_slice(), &BASE64.encode(&encrypted.ciphertext), &encrypted.salt.as_slice(), &encrypted.iv.as_slice(), &"standard", &"none", ], ) .await .map_err(|e| { tracing::error!("钱包创建数据库写入失败: {}", e); AppError::Database })?; // 初始化NAC原生双币资产记录 client .execute( r#" INSERT INTO assets (wallet_id, chain, asset_symbol, asset_type, balance) VALUES ($1, 'nac', 'XIC', 'native', 0), ($1, 'nac', 'XTZH', 'native', 0) "#, &[&row.get::<_, i64>("id")], ) .await .map_err(|_| AppError::Database)?; // 记录审计日志 client .execute( r#" INSERT INTO audit_logs (wallet_id, action, actor, actor_type, details, result) VALUES ($1, 'create_wallet', $2, 'user', $3, 'success') "#, &[ &row.get::<_, i64>("id"), &req.user_id.to_string(), &serde_json::json!({ "address": nac_address.to_hex(), "wallet_type": "standard" }), ], ) .await .map_err(|_| AppError::Database)?; // 清零签名密钥 signing_key_bytes.zeroize(); info!("钱包创建成功: user_id={}, address={}", req.user_id, nac_address.to_hex()); Ok(WalletResponse { wallet_id: row.get("id"), address: row.get("address_hex"), // 助记词仅在创建时返回一次,之后不可再获取 mnemonic: Some(mnemonic), created_at: row.get("created_at"), }) } /// 获取钱包信息(不含敏感数据) pub async fn get_wallet( pool: &Pool, user_id: i64, ) -> Result { let client = pool.get().await.map_err(|_| AppError::Database)?; let row = client .query_opt( r#" SELECT w.id, w.address_hex, w.kyc_level, w.vip_level, w.created_at, json_agg(json_build_object( 'chain', a.chain, 'symbol', a.asset_symbol, 'balance', a.balance::text, 'frozen', a.frozen_balance::text )) as assets FROM wallets w LEFT JOIN assets a ON a.wallet_id = w.id WHERE w.user_id = $1 AND w.is_active = true GROUP BY w.id "#, &[&user_id], ) .await .map_err(|_| AppError::Database)?; match row { None => Err(AppError::NotFound), Some(r) => Ok(WalletResponse { wallet_id: r.get("id"), address: r.get("address_hex"), mnemonic: None, // 查询时不返回助记词 created_at: r.get("created_at"), }), } }