sdk_common/src/network.rs
2024-05-27 21:55:03 +02:00

208 lines
6.4 KiB
Rust

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<String> 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<AnkError>,
}
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<String>,
pub error: Option<AnkError>,
}
impl NewTxMessage {
pub fn new(transaction: String, tweak_data: Option<String>) -> 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<AnkError>,
}
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<String>, // When we receive message we can't decrypt we only have this and commited_in_tx
pub plaintext: Option<String>, // Never None when message sent
pub commited_in: Option<OutPoint>,
pub commitment: Option<String>, // content of the op_return
pub sender: Option<String>, // Never None when message sent
pub recipient: Option<String>, // Never None when message sent
pub shared_secret: Option<String>, // Never None when message sent
pub key: Option<String>, // Never None when message sent
pub confirmed_by: Option<OutPoint>, // If this None, Sender keeps sending
pub timestamp: u64,
pub error: Option<AnkError>,
}
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<u8>) -> Result<Vec<u8>> {
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<Vec<u8>> {
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()
}
}