add notify address for message

This commit is contained in:
Sosthene00 2024-04-17 09:15:44 +02:00
parent a6f4a5122c
commit 8f6918748d
3 changed files with 165 additions and 20 deletions

View File

@ -355,24 +355,69 @@ pub fn parse_network_msg(raw: String) -> ApiResult<parseNetworkMsgReturn> {
}
#[wasm_bindgen]
pub fn parse_4nk_msg(raw: String) -> Option<String>{
if let Ok(msg) = AnkNetworkMsg::new(&raw) {
match msg.topic {
AnkTopic::Faucet => {
match Txid::from_str(msg.content) {
Ok(txid) => {
// return the txid for verification
Some(txid.to_string())
},
Err(e) => {
log::error!("Invalid txid with a \"faucet\" message: {}", e.to_string());
None
}
}
}
}
pub fn get_outpoints_for_user(pre_id: String) -> ApiResult<outputs_list> {
let connected_users = lock_connected_users()?;
let user = connected_users.get(&pre_id).ok_or(ApiError {
message: "Can't find user".to_owned(),
})?;
Ok(outputs_list(user.get_all_outputs()))
}
#[wasm_bindgen]
pub fn is_tx_owned_by_user(pre_id: String, tx: String) -> ApiResult<bool> {
let transaction = deserialize::<Transaction>(&Vec::from_hex(&tx)?)?;
let txid = transaction.txid();
let connected_users = lock_connected_users()?;
let user = connected_users.get(&pre_id).ok_or(ApiError {
message: "Can't find user".to_owned(),
})?;
if let Some(_) = user
.recover
.get_outputs()
.to_outpoints_list()
.iter()
.find(|(outpoint, output)| outpoint.txid == txid)
{
Ok(true)
} else {
log::debug!("Can't parse message as a valid 4nk message: {}", raw);
None
Ok(false)
}
}
#[derive(Tsify, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct createNotificationTransactionReturn {
pub transaction: String,
pub spaddress2secret: HashMap<String, String>,
}
#[wasm_bindgen]
pub fn create_notification_transaction(
user_pre_id: String,
recipient: String,
message: String,
) -> ApiResult<createNotificationTransactionReturn> {
let sp_address: SilentPaymentAddress = recipient.try_into()?;
let (transaction, notification_information) =
create_transaction_for_address(user_pre_id, sp_address, message)?;
// The secret is an ecc point and *must* be hashed to produce a proper ecdh secret
// For now we propose to implement a tagged hash for it
// It could be interesting to add some piece of data to allow for the derivation of multiple secrets
let spaddress2secret = notification_information
.into_iter()
.map(|(address, shared_pubkey)| {
let shared_secret = AnkSharedSecret::new_from_public_key(shared_pubkey);
(address.into(), shared_secret.to_string())
})
.collect();
Ok(createNotificationTransactionReturn {
transaction: serialize(&transaction).to_lower_hex_string(),
spaddress2secret,
})
}

View File

@ -1,8 +1,14 @@
use std::collections::HashMap;
use anyhow::Result;
use anyhow::{Error, Result};
use rand::Rng;
use sp_backend::bitcoin::policy::DUST_RELAY_TX_FEE;
use sp_backend::bitcoin::secp256k1::ecdh::SharedSecret;
use sp_backend::bitcoin::{block, Amount, OutPoint};
use sp_backend::silentpayments::sending::SilentPaymentAddress;
use sp_backend::silentpayments::utils::receiving::calculate_shared_secret;
use sp_backend::spclient::{OutputList, OwnedOutput, Recipient, SpClient};
use sp_backend::{
bitcoin::{
secp256k1::{PublicKey, Scalar, XOnlyPublicKey},
@ -15,9 +21,79 @@ use crate::user::{lock_connected_users, CONNECTED_USERS};
type FoundOutputs = HashMap<Option<Label>, HashMap<XOnlyPublicKey, Scalar>>;
pub fn check_transaction(tx: Transaction, tweak_data: PublicKey) -> Result<FoundOutputs> {
type NotificationInformation = (Transaction, Vec<(SilentPaymentAddress, PublicKey)>);
pub fn create_transaction_for_address(
send_as: String,
sp_address: SilentPaymentAddress,
message: String,
) -> Result<NotificationInformation> {
let connected_users = lock_connected_users()?;
let sender = connected_users
.get(&send_as)
.ok_or(Error::msg("Unknown sender"))?;
let sp_wallet = if sp_address.is_testnet() {
&sender.recover
} else {
if let Some(main) = &sender.main {
main
} else {
return Err(Error::msg("Can't spend on mainnet"));
}
};
let available_outpoints = sender.recover.get_outputs().to_spendable_list();
// Here we need to add more heuristics about which outpoint we spend
// For now let's keep it simple
let mut inputs: HashMap<OutPoint, OwnedOutput> = HashMap::new();
let total_available =
available_outpoints
.into_iter()
.try_fold(Amount::from_sat(0), |acc, (outpoint, output)| {
let new_total = acc + output.amount;
inputs.insert(outpoint, output);
if new_total > Amount::from_sat(1000) {
Err(new_total)
} else {
Ok(new_total)
}
});
match total_available {
Err(total) => log::debug!("Spending {} outputs totaling {} sats", inputs.len(), total),
Ok(_) => return Err(Error::msg("Not enought fund available")),
}
let recipient = Recipient {
address: sp_address.into(),
amount: Amount::from_sat(1000),
nb_outputs: 1,
};
let mut new_psbt = sp_wallet
.get_client()
.create_new_psbt(inputs, vec![recipient], None)?;
log::debug!("Created psbt: {}", new_psbt);
SpClient::set_fees(&mut new_psbt, Amount::from_sat(1000), sp_address.into())?;
let shared_secrets: Vec<(SilentPaymentAddress, PublicKey)> =
sp_wallet.get_client().fill_sp_outputs(&mut new_psbt)?;
log::debug!("Definitive psbt: {}", new_psbt);
let mut aux_rand = [0u8; 32];
rand::thread_rng().fill(&mut aux_rand);
let mut signed = sp_wallet.get_client().sign_psbt(new_psbt, &aux_rand)?;
log::debug!("signed psbt: {}", signed);
SpClient::finalize_psbt(&mut signed)?;
let final_tx = signed.extract_tx()?;
Ok((final_tx, shared_secrets))
}
pub fn check_transaction(
tx: &Transaction,
blockheight: u32,

View File

@ -740,6 +740,30 @@ class Services {
return null;
}
}
public async notify_address_for_message(sp_address: string, message: string): Promise<createNotificationTransactionReturn | null> {
const services = await Services.getInstance();
let user: User;
try {
let possibleUser = await services.getUserInfo();
if (!possibleUser) {
console.error("No user loaded, please first create a new user or login");
return null;
} else {
user = possibleUser;
}
} catch (error) {
throw error;
}
try {
let notificationInfo: createNotificationTransactionReturn = services.sdkClient.create_notification_transaction(user, sp_address, message);
return notificationInfo;
} catch {
console.error("Failed to create notification transaction for user", user);
return null
}
}
}
export default Services;