use anyhow::{Error, Result}; use js_sys::Date; use rand::{thread_rng, RngCore}; use serde::{Deserialize, Serialize}; use sp_client::bitcoin::hex::{DisplayHex, FromHex}; use sp_client::bitcoin::OutPoint; use tsify::Tsify; use crate::crypto::{Aes256Decryption, Purpose}; use crate::error::AnkError; #[derive(Debug, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub enum AnkFlag { NewTx, Faucet, Cipher, Unknown, } impl From<&str> for AnkFlag { fn from(value: &str) -> Self { match value { "NewTx" => Self::NewTx, "Faucet" => Self::Faucet, "Cipher" => Self::Cipher, _ => Self::Unknown, } } } impl From for AnkFlag { fn from(value: String) -> Self { (&value[..]).into() } } impl AnkFlag { pub fn new_from_byte(byte: u8) -> Self { match byte { 0 => Self::NewTx, 1 => Self::Faucet, 2 => Self::Cipher, _ => Self::Unknown, } } pub fn as_str(&self) -> &str { match self { Self::NewTx => "NewTx", Self::Faucet => "Faucet", Self::Cipher => "Cipher", Self::Unknown => "Unknown", } } } #[derive(Debug, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct FaucetMessage { pub sp_address: String, pub commitment: String, pub error: Option, } impl FaucetMessage { pub fn new(sp_address: String) -> Self { let mut buf = [0u8;64]; thread_rng().fill_bytes(&mut buf); Self { sp_address, commitment: buf.to_lower_hex_string(), error: None } } } #[derive(Debug, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct NewTxMessage { pub transaction: String, pub tweak_data: Option, pub error: Option, } impl NewTxMessage { pub fn new(transaction: String, tweak_data: Option) -> Self { Self { transaction, tweak_data, error: None, } } } #[derive(Debug, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct CipherMessage { pub sender: String, pub message: String, pub error: Option, } impl CipherMessage { pub fn new(sender: String, message: String) -> Self { Self { sender, message, error: None, } } } #[derive(Debug, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct AnkNetworkMsg { pub flag: AnkFlag, pub content: String, } impl AnkNetworkMsg { pub fn new(flag: AnkFlag, raw: &str) -> Self { Self { flag, content: raw.into(), } } } #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Tsify, Clone)] pub enum CachedMessageStatus { #[default] NoStatus, FaucetWaiting, FaucetComplete, CipherWaitingTx, TxWaitingCipher, SentWaitingConfirmation, // we're sender and wait for commited_in to be spent ReceivedMustConfirm, // we're receiver and we spend commited_in to sender MustSpendConfirmation, // we're sender and we must spend confirmed_by Complete, } /// Unique struct for both 3nk messages and notification/key exchange, both rust and ts /// 0. Faucet: commited_in with nothing else, status is NoStatus /// 1. notification: /// 0. sender: ciphertext, plaintext, commited_in, sender, recipient, shared_secret, key /// 1. receiver (without tx): ciphertext /// 2. receiver (tx without msg): commited_in, commitment, recipient, shared_secret /// 3. receiver (receive tx after msg): plaintext, key, sender, commited_in, commitment, recipient, shared_secret /// 4. receiver (msg after tx): ciphertext, key, plaintext, sender /// 2. confirmation: /// 0. receiver (spend the smallest vout that pays him in the first tx): confirmed_by /// 1. sender (detect a transaction that pays him and spend commited_by): confirmed_by /// 2. sender toggle status to complete when it spent confirmed_by, receiver when it detects the confirmed_by is spent #[derive(Debug, Default, Serialize, Deserialize, Tsify, Clone)] #[tsify(into_wasm_abi, from_wasm_abi)] #[allow(non_camel_case_types)] pub struct CachedMessage { pub id: u32, pub status: CachedMessageStatus, pub ciphertext: Option, // When we receive message we can't decrypt we only have this and commited_in_tx pub plaintext: Option, // Never None when message sent pub commited_in: Option, pub commitment: Option, // content of the op_return pub sender: Option, // Never None when message sent pub recipient: Option, // Never None when message sent pub shared_secret: Option, // Never None when message sent pub key: Option, // Never None when message sent pub confirmed_by: Option, // If this None, Sender keeps sending pub timestamp: u64, pub error: Option, } impl CachedMessage { pub fn new() -> Self { let mut new = Self::default(); let mut buf = [0u8;4]; thread_rng().fill_bytes(&mut buf); new.id = u32::from_be_bytes(buf); new.timestamp = Date::now().floor() as u64; new } pub fn try_decrypt_cipher(&self, cipher: Vec) -> Result> { if self.ciphertext.is_some() || self.shared_secret.is_none() { return Err(Error::msg( "Can't try decrypt this message, there's already a ciphertext or no shared secret" )); } let mut shared_secret = [0u8; 32]; shared_secret .copy_from_slice(&Vec::from_hex(self.shared_secret.as_ref().unwrap())?); let aes_decrypt = Aes256Decryption::new(Purpose::Arbitrary, cipher, shared_secret)?; aes_decrypt.decrypt_with_key() } pub fn try_decrypt_with_shared_secret(&self, shared_secret: [u8; 32]) -> Result> { if self.ciphertext.is_none() || self.shared_secret.is_some() { return Err(Error::msg( "Can't try decrypt this message, ciphertext is none or shared_secret already found" )); } let cipher_bin = Vec::from_hex(self.ciphertext.as_ref().unwrap())?; let aes_decrypt = Aes256Decryption::new(Purpose::Arbitrary, cipher_bin, shared_secret)?; aes_decrypt.decrypt_with_key() } }