Message processing heavy refactoring

This commit is contained in:
Sosthene 2024-05-24 22:40:40 +02:00
parent c3e435a228
commit bc6f95a98f
3 changed files with 191 additions and 141 deletions

View File

@ -25,7 +25,8 @@ use sp_client::bitcoin::key::Secp256k1;
use sp_client::bitcoin::secp256k1::ecdh::shared_secret_point; use sp_client::bitcoin::secp256k1::ecdh::shared_secret_point;
use sp_client::bitcoin::secp256k1::{PublicKey, SecretKey}; use sp_client::bitcoin::secp256k1::{PublicKey, SecretKey};
use sp_client::bitcoin::{Amount, Network, OutPoint, Psbt, Transaction, Txid}; use sp_client::bitcoin::{Amount, Network, OutPoint, Psbt, Transaction, Txid};
use sp_client::silentpayments::Error as SpError; use sp_client::silentpayments::utils as sp_utils;
use sp_client::silentpayments::{Error as SpError, Network as SpNetwork};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sp_client::silentpayments::sending::SilentPaymentAddress; use sp_client::silentpayments::sending::SilentPaymentAddress;
@ -34,11 +35,12 @@ use wasm_bindgen::convert::FromWasmAbi;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use sdk_common::network::{ use sdk_common::network::{
self, AnkFlag, AnkNetworkMsg, CachedMessage, CachedMessageStatus, FaucetMessage, NewTxMessage, UnknownMessage self, AnkFlag, AnkNetworkMsg, CachedMessage, CachedMessageStatus, FaucetMessage, NewTxMessage,
UnknownMessage,
}; };
use sdk_common::silentpayments::{ use sdk_common::silentpayments::{
create_transaction, create_transaction_for_address_with_shared_secret, create_transaction, create_transaction_for_address_with_shared_secret,
create_transaction_spend_outpoint, map_outputs_to_sp_address create_transaction_spend_outpoint, map_outputs_to_sp_address,
}; };
use sp_client::spclient::{ use sp_client::spclient::{
@ -402,23 +404,29 @@ fn handle_recover_transaction(
} else { } else {
false false
} }
}) }) {
{
let message = messages.get_mut(pos).unwrap(); let message = messages.get_mut(pos).unwrap();
match message.status { match message.status {
CachedMessageStatus::FaucetWaiting => { CachedMessageStatus::FaucetWaiting => {
message.status = CachedMessageStatus::FaucetComplete; message.status = CachedMessageStatus::FaucetComplete;
message.commited_in = utxo_created.into_iter().next().map(|(outpoint, _)| *outpoint); message.commited_in = utxo_created
.into_iter()
.next()
.map(|(outpoint, _)| *outpoint);
return Ok(message.clone()); return Ok(message.clone());
}, }
// Actually this is unreachable
CachedMessageStatus::FaucetComplete => return Ok(message.clone()), CachedMessageStatus::FaucetComplete => return Ok(message.clone()),
_ => () _ => (),
} }
} }
// we inspect inputs looking for links with previous tx // we inspect inputs looking for links with previous tx
for input in tx.input.iter() { for input in tx.input.iter() {
if let Some(pos) = messages.iter().position(|m| { if let Some(pos) = messages
.iter()
.position(|m| {
debug!("{:?}", Some(input.previous_output));
m.confirmed_by == Some(input.previous_output) m.confirmed_by == Some(input.previous_output)
}) })
{ {
@ -426,9 +434,9 @@ fn handle_recover_transaction(
// If we are receiver, that's pretty much it, just set status to complete // If we are receiver, that's pretty much it, just set status to complete
message.status = CachedMessageStatus::Complete; message.status = CachedMessageStatus::Complete;
return Ok(message.clone()); return Ok(message.clone());
} else if let Some(pos) = messages.iter().position(|m| { } else if let Some(pos) = messages
m.commited_in == Some(input.previous_output) .iter()
}) .position(|m| m.commited_in == Some(input.previous_output))
{ {
// sender needs to spent it back again to receiver // sender needs to spent it back again to receiver
let (outpoint, output) = utxo_created.into_iter().next().unwrap(); let (outpoint, output) = utxo_created.into_iter().next().unwrap();
@ -444,9 +452,16 @@ fn handle_recover_transaction(
} }
// if we've found nothing we are being notified // if we've found nothing we are being notified
let shared_point = let shared_point = sp_utils::receiving::calculate_shared_point(
shared_secret_point(&tweak_data, &sp_wallet.get_client().get_scan_key()); &tweak_data,
let shared_secret = AnkSharedSecret::new(shared_point); &sp_wallet.get_client().get_scan_key(),
);
let shared_secret = AnkSharedSecret::new(PublicKey::from_slice(&shared_point)?);
debug!(
"Shared secret: {}",
shared_secret.to_byte_array().to_lower_hex_string()
);
if let Some(cipher_pos) = messages.iter().position(|m| { if let Some(cipher_pos) = messages.iter().position(|m| {
if m.status != CachedMessageStatus::CipherWaitingTx { if m.status != CachedMessageStatus::CipherWaitingTx {
@ -454,15 +469,13 @@ fn handle_recover_transaction(
} }
m.try_decrypt_with_shared_secret(shared_secret.to_byte_array()) m.try_decrypt_with_shared_secret(shared_secret.to_byte_array())
.is_ok() .is_ok()
}) }) {
{
let message = messages.get_mut(cipher_pos).unwrap(); let message = messages.get_mut(cipher_pos).unwrap();
let (outpoint, output) = utxo_created.into_iter().next().unwrap(); let (outpoint, output) = utxo_created.into_iter().next().unwrap();
message.commited_in = Some(outpoint.clone()); message.commited_in = Some(outpoint.clone());
message.shared_secret = message.shared_secret = Some(shared_secret.to_byte_array().to_lower_hex_string());
Some(shared_secret.to_byte_array().to_lower_hex_string());
message.commitment = Some(commitment_str); message.commitment = Some(commitment_str);
let plaintext = message let plaintext = message
@ -474,16 +487,18 @@ fn handle_recover_transaction(
message.recipient = Some(sp_wallet.get_client().get_receiving_address()); message.recipient = Some(sp_wallet.get_client().get_receiving_address());
message.status = CachedMessageStatus::ReceivedMustConfirm; message.status = CachedMessageStatus::ReceivedMustConfirm;
return Ok(message.clone()) return Ok(message.clone());
} else { } else {
// store it and wait for the message // store it and wait for the message
let mut new_msg = CachedMessage::new(); let mut new_msg = CachedMessage::new();
let (outpoint, output) = utxo_created.into_iter().next().expect("utxo_created shouldn't be empty"); let (outpoint, output) = utxo_created
.into_iter()
.next()
.expect("utxo_created shouldn't be empty");
new_msg.commited_in = Some(outpoint.clone()); new_msg.commited_in = Some(outpoint.clone());
new_msg.commitment = Some(commitment.to_lower_hex_string()); new_msg.commitment = Some(commitment.to_lower_hex_string());
new_msg.recipient = Some(sp_wallet.get_client().get_receiving_address()); new_msg.recipient = Some(sp_wallet.get_client().get_receiving_address());
new_msg.shared_secret = new_msg.shared_secret = Some(shared_secret.to_byte_array().to_lower_hex_string());
Some(shared_secret.to_byte_array().to_lower_hex_string());
new_msg.status = CachedMessageStatus::TxWaitingCipher; new_msg.status = CachedMessageStatus::TxWaitingCipher;
messages.push(new_msg.clone()); messages.push(new_msg.clone());
return Ok(new_msg.clone()); return Ok(new_msg.clone());
@ -491,14 +506,15 @@ fn handle_recover_transaction(
} else { } else {
// We are sender of a transaction // We are sender of a transaction
// We only need to return the message // We only need to return the message
if let Some(message) = messages.iter() if let Some(message) = messages
.find(|m| { .iter()
m.commitment.as_ref() == Some(&commitment_str) .find(|m| m.commitment.as_ref() == Some(&commitment_str))
})
{ {
return Ok(message.clone()); return Ok(message.clone());
} else { } else {
return Err(anyhow::Error::msg("We spent a transaction for a commitment we don't know")); return Err(anyhow::Error::msg(
"We spent a transaction for a commitment we don't know",
));
} }
} }
} }
@ -577,7 +593,7 @@ pub fn parse_network_msg(raw: String, fee_rate: u32) -> ApiResult<CachedMessage>
AnkFlag::Faucet => unimplemented!(), AnkFlag::Faucet => unimplemented!(),
AnkFlag::Error => { AnkFlag::Error => {
let error_msg = CachedMessage::new_error(ank_msg.content); let error_msg = CachedMessage::new_error(ank_msg.content);
return Ok(error_msg) return Ok(error_msg);
} }
AnkFlag::Unknown => { AnkFlag::Unknown => {
// let's try to decrypt with keys we found in transactions but haven't used yet // let's try to decrypt with keys we found in transactions but haven't used yet
@ -597,16 +613,15 @@ pub fn parse_network_msg(raw: String, fee_rate: u32) -> ApiResult<CachedMessage>
message.plaintext = Some(unknown_msg.message); message.plaintext = Some(unknown_msg.message);
message.sender = Some(unknown_msg.sender); message.sender = Some(unknown_msg.sender);
message.ciphertext = Some(ank_msg.content); message.ciphertext = Some(ank_msg.content);
message.status = CachedMessageStatus::ReceivedMustConfirm;
return Ok(message.clone()); return Ok(message.clone());
} else { } else {
// let's keep it in case we receive the transaction later // let's keep it in case we receive the transaction later
let mut new_msg = CachedMessage::new(); let mut new_msg = CachedMessage::new();
new_msg.status = CachedMessageStatus::CipherWaitingTx; new_msg.status = CachedMessageStatus::CipherWaitingTx;
new_msg.ciphertext = Some(ank_msg.content); new_msg.ciphertext = Some(ank_msg.content);
messages.push(new_msg); messages.push(new_msg.clone());
return Err(ApiError { return Ok(new_msg);
message: "Can't decrypt message".to_owned(),
});
} }
} }
_ => unimplemented!(), _ => unimplemented!(),
@ -676,22 +691,33 @@ pub struct createTransactionReturn {
pub new_network_msg: CachedMessage, pub new_network_msg: CachedMessage,
} }
/// This is what we call to answer a confirmation as a sender /// This is what we call to answer a confirmation as a sender
#[wasm_bindgen] #[wasm_bindgen]
pub fn answer_confirmation_transaction( pub fn answer_confirmation_transaction(
message: CachedMessage, message_id: u32,
fee_rate: u32, fee_rate: u32,
) -> ApiResult<createTransactionReturn> { ) -> ApiResult<createTransactionReturn> {
if message.recipient.is_none() || message.confirmed_by.is_none() { let mut messages = lock_messages()?;
return Err(ApiError { message: "Invalid network message".to_owned() }); let message: &mut CachedMessage;
if let Some(m) = messages.iter_mut().find(|m| m.id == message_id) {
if m.sender.is_none() || m.commited_in.is_none() {
return Err(ApiError {
message: "Invalid network message".to_owned(),
});
}
message = m;
} else {
return Err(ApiError { message: format!("Can't find message for id {}", message_id) });
} }
let sp_address: SilentPaymentAddress = message.recipient.as_ref().unwrap().as_str().try_into()?; let sp_address: SilentPaymentAddress =
message.recipient.as_ref().unwrap().as_str().try_into()?;
let connected_user = lock_connected_user()?; let connected_user = lock_connected_user()?;
let sp_wallet: &SpWallet; let sp_wallet: &SpWallet;
if sp_address.is_testnet() { if sp_address.get_network() != SpNetwork::Mainnet {
sp_wallet = connected_user.try_get_recover()?; sp_wallet = connected_user.try_get_recover()?;
} else { } else {
sp_wallet = connected_user.try_get_main()?; sp_wallet = connected_user.try_get_main()?;
@ -703,38 +729,53 @@ pub fn answer_confirmation_transaction(
nb_outputs: 1, nb_outputs: 1,
}; };
let confirmed_by = message.confirmed_by.clone().unwrap();
let commited_in = message.commited_in.clone().unwrap();
let signed_psbt = create_transaction_spend_outpoint( let signed_psbt = create_transaction_spend_outpoint(
&message.confirmed_by.unwrap(), &confirmed_by,
sp_wallet, sp_wallet,
recipient, recipient,
Amount::from_sat(fee_rate.into()) &commited_in.txid,
Amount::from_sat(fee_rate.into()),
)?; )?;
let final_tx = signed_psbt.extract_tx()?; let final_tx = signed_psbt.extract_tx()?;
message.status = CachedMessageStatus::Complete;
Ok(createTransactionReturn { Ok(createTransactionReturn {
txid: final_tx.txid().to_string(), txid: final_tx.txid().to_string(),
transaction: serialize(&final_tx).to_lower_hex_string(), transaction: serialize(&final_tx).to_lower_hex_string(),
new_network_msg: message new_network_msg: message.clone(),
}) })
} }
/// This is what we call to confirm as a receiver /// This is what we call to confirm as a receiver
#[wasm_bindgen] #[wasm_bindgen]
pub fn create_confirmation_transaction( pub fn create_confirmation_transaction(
message: CachedMessage, message_id: u32,
fee_rate: u32, fee_rate: u32,
) -> ApiResult<createTransactionReturn> { ) -> ApiResult<createTransactionReturn> {
if message.sender.is_none() || message.confirmed_by.is_none() { let mut messages = lock_messages()?;
return Err(ApiError { message: "Invalid network message".to_owned() }); let message: &mut CachedMessage;
if let Some(m) = messages.iter_mut().find(|m| m.id == message_id) {
if m.sender.is_none() || m.commited_in.is_none() {
return Err(ApiError {
message: "Invalid network message".to_owned(),
});
}
message = m;
} else {
return Err(ApiError { message: format!("Can't find message for id {}", message_id) });
} }
let sp_address: SilentPaymentAddress = message.sender.as_ref().unwrap().as_str().try_into()?; let sp_address: SilentPaymentAddress = message.sender.as_ref().unwrap().as_str().try_into()?;
let connected_user = lock_connected_user()?; let connected_user = lock_connected_user()?;
let sp_wallet: &SpWallet; let sp_wallet: &SpWallet;
if sp_address.is_testnet() { if sp_address.get_network() != SpNetwork::Mainnet {
sp_wallet = connected_user.try_get_recover()?; sp_wallet = connected_user.try_get_recover()?;
} else { } else {
sp_wallet = connected_user.try_get_main()?; sp_wallet = connected_user.try_get_main()?;
@ -746,26 +787,38 @@ pub fn create_confirmation_transaction(
nb_outputs: 1, nb_outputs: 1,
}; };
let commited_in = message.commited_in.clone().unwrap();
let signed_psbt = create_transaction_spend_outpoint( let signed_psbt = create_transaction_spend_outpoint(
&message.confirmed_by.unwrap(), &commited_in,
sp_wallet, sp_wallet,
recipient, recipient,
Amount::from_sat(fee_rate.into()) &commited_in.txid,
Amount::from_sat(fee_rate.into()),
)?; )?;
// what's the vout of the output sent to sender?
let sp_address2vouts = map_outputs_to_sp_address(&signed_psbt.to_string())?;
let recipients_vouts = sp_address2vouts
.get::<String>(&sp_address.into())
.expect("recipients didn't change")
.as_slice();
let final_tx = signed_psbt.extract_tx()?; let final_tx = signed_psbt.extract_tx()?;
message.confirmed_by = Some(OutPoint { txid: final_tx.txid(), vout: recipients_vouts[0] as u32 });
Ok(createTransactionReturn { Ok(createTransactionReturn {
txid: final_tx.txid().to_string(), txid: final_tx.txid().to_string(),
transaction: serialize(&final_tx).to_lower_hex_string(), transaction: serialize(&final_tx).to_lower_hex_string(),
new_network_msg: message new_network_msg: message.clone(),
}) })
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn create_notification_transaction( pub fn create_notification_transaction(
address: String, address: String,
commitment: Option<String>, message: UnknownMessage,
fee_rate: u32, fee_rate: u32,
) -> ApiResult<createTransactionReturn> { ) -> ApiResult<createTransactionReturn> {
let sp_address: SilentPaymentAddress = address.as_str().try_into()?; let sp_address: SilentPaymentAddress = address.as_str().try_into()?;
@ -773,7 +826,7 @@ pub fn create_notification_transaction(
let connected_user = lock_connected_user()?; let connected_user = lock_connected_user()?;
let sp_wallet: &SpWallet; let sp_wallet: &SpWallet;
if sp_address.is_testnet() { if sp_address.get_network() != SpNetwork::Mainnet {
sp_wallet = connected_user.try_get_recover()?; sp_wallet = connected_user.try_get_recover()?;
} else { } else {
sp_wallet = connected_user.try_get_main()?; sp_wallet = connected_user.try_get_main()?;
@ -785,42 +838,47 @@ pub fn create_notification_transaction(
nb_outputs: 1, nb_outputs: 1,
}; };
let commitment = create_commitment(serde_json::to_string(&message)?);
let signed_psbt = create_transaction_for_address_with_shared_secret( let signed_psbt = create_transaction_for_address_with_shared_secret(
recipient, recipient,
sp_wallet, sp_wallet,
commitment.as_deref(), Some(&commitment),
Amount::from_sat(fee_rate.into()), Amount::from_sat(fee_rate.into()),
)?; )?;
let psbt = Psbt::from_str(&signed_psbt)?; let psbt = Psbt::from_str(&signed_psbt)?;
let partial_secret = sp_wallet let partial_secret = sp_wallet.get_client().get_partial_secret_from_psbt(&psbt)?;
.get_client()
.get_partial_secret_from_psbt(&psbt)?;
let shared_point = shared_secret_point( let shared_point =
&sp_wallet sp_utils::sending::calculate_shared_point(&sp_address.get_scan_key(), &partial_secret);
.get_client()
.get_scan_key()
.public_key(&Secp256k1::signing_only()),
&partial_secret,
);
let shared_secret = AnkSharedSecret::new(shared_point); let shared_secret = AnkSharedSecret::new(PublicKey::from_slice(&shared_point)?);
debug!( debug!(
"Created transaction with secret {}", "Created transaction with secret {}",
shared_secret.to_byte_array().to_lower_hex_string() shared_secret.to_byte_array().to_lower_hex_string()
); );
let cipher = encrypt_with_key(serde_json::to_string(&message)?, shared_secret.to_byte_array().to_lower_hex_string())?;
// update our cache // update our cache
let sp_address2vouts = map_outputs_to_sp_address(&signed_psbt)?; let sp_address2vouts = map_outputs_to_sp_address(&signed_psbt)?;
let recipients_vouts = sp_address2vouts.get::<String>(&address).expect("recipients didn't change").as_slice(); let recipients_vouts = sp_address2vouts
.get::<String>(&address)
.expect("recipients didn't change")
.as_slice();
// for now let's just take the smallest vout that belongs to the recipient // for now let's just take the smallest vout that belongs to the recipient
let final_tx = psbt.extract_tx()?; let final_tx = psbt.extract_tx()?;
let mut new_msg = CachedMessage::default(); let mut new_msg = CachedMessage::new();
new_msg.commitment = commitment; new_msg.plaintext = Some(message.message);
new_msg.commited_in = Some(OutPoint { txid: final_tx.txid(), vout: recipients_vouts[0] as u32 }); new_msg.ciphertext = Some(cipher);
new_msg.commitment = Some(commitment);
new_msg.commited_in = Some(OutPoint {
txid: final_tx.txid(),
vout: recipients_vouts[0] as u32,
});
new_msg.shared_secret = Some(shared_secret.to_byte_array().to_lower_hex_string()); new_msg.shared_secret = Some(shared_secret.to_byte_array().to_lower_hex_string());
new_msg.recipient = Some(address); new_msg.recipient = Some(address);
new_msg.sender = Some(sp_wallet.get_client().get_receiving_address()); new_msg.sender = Some(sp_wallet.get_client().get_receiving_address());
@ -908,11 +966,11 @@ pub fn create_faucet_msg() -> ApiResult<CachedMessage> {
let user = lock_connected_user()?; let user = lock_connected_user()?;
let sp_address = user.try_get_recover()?.get_client().get_receiving_address(); let sp_address = user.try_get_recover()?.get_client().get_receiving_address();
let mut commitment = [0u8;64]; let mut commitment = [0u8; 64];
thread_rng().fill_bytes(&mut commitment); thread_rng().fill_bytes(&mut commitment);
let mut cached_msg = CachedMessage::new(); let mut cached_msg = CachedMessage::new();
cached_msg.recipient = Some(sp_address); cached_msg.recipient = Some(sp_address);
cached_msg.commitment = Some(commitment.to_lower_hex_string()); cached_msg.commitment = Some(commitment.to_lower_hex_string());
cached_msg.status = CachedMessageStatus::FaucetWaiting; cached_msg.status = CachedMessageStatus::FaucetWaiting;
lock_messages()?.push(cached_msg.clone()); lock_messages()?.push(cached_msg.clone());

View File

@ -1,4 +1,4 @@
import { createUserReturn, User, Process, createTransactionReturn, parse_network_msg, outputs_list, FaucetMessage, AnkFlag, NewTxMessage, encryptWithNewKeyResult, AnkSharedSecret, CachedMessage } from '../dist/pkg/sdk_client'; import { createUserReturn, User, Process, createTransactionReturn, parse_network_msg, outputs_list, FaucetMessage, AnkFlag, NewTxMessage, encryptWithNewKeyResult, AnkSharedSecret, CachedMessage, UnknownMessage } from '../dist/pkg/sdk_client';
import IndexedDB from './database' import IndexedDB from './database'
import { WebSocketClient } from './websockets'; import { WebSocketClient } from './websockets';
@ -105,36 +105,24 @@ class Services {
const recipientSpAddress = spAddressElement.value; const recipientSpAddress = spAddressElement.value;
const message = messageElement.value; const message = messageElement.value;
const msg_payload = JSON.stringify({sender: this.sp_address, message: message}); const msg_payload: UnknownMessage = {sender: this.sp_address!, message: message};
let notificationInfo = await services.notify_address_for_message(recipientSpAddress, msg_payload); let notificationInfo = await services.notify_address_for_message(recipientSpAddress, msg_payload);
if (notificationInfo) { if (notificationInfo) {
let networkMsg = notificationInfo.new_network_msg; let networkMsg = notificationInfo.new_network_msg;
let shared_secret = ''; console.debug(networkMsg);
if (networkMsg.shared_secret) {
shared_secret = networkMsg.shared_secret;
} else {
throw 'no shared_secret';
}
console.info('Successfully sent notification transaction');
const connection = await services.pickWebsocketConnectionRandom(); const connection = await services.pickWebsocketConnectionRandom();
const flag: AnkFlag = "Unknown"; const flag: AnkFlag = "Unknown";
// encrypt the message(s)
// TODO we'd rather do that in the wasm as part of notify_address_for_message
try { try {
const cipher = await services.encryptData(msg_payload, shared_secret); // send message (transaction in envelope)
let updated_msg = notificationInfo.new_network_msg; await services.updateMessages(networkMsg);
updated_msg.plaintext = msg_payload; connection?.sendMessage(flag, networkMsg.ciphertext!);
updated_msg.ciphertext = cipher;
await services.updateMessages(updated_msg);
connection?.sendMessage(flag, cipher);
} catch (error) { } catch (error) {
throw error; throw error;
} }
// add peers list // add peers list
// add processes list // add processes list
// send message (transaction in envelope)
} }
} }
@ -820,6 +808,41 @@ class Services {
} }
} }
public async answer_confirmation_message(msg: CachedMessage): Promise<void> {
const services = await Services.getInstance();
const connection = await services.pickWebsocketConnectionRandom();
if (!connection) {
throw new Error("No connection to relay");
}
let user: User;
try {
let possibleUser = await services.getUserInfo();
if (!possibleUser) {
throw new Error("No user loaded, please first create a new user or login");
} else {
user = possibleUser;
}
} catch (error) {
throw error;
}
let notificationInfo: createTransactionReturn;
try {
const feeRate = 1;
notificationInfo = services.sdkClient.answer_confirmation_transaction(msg.id, feeRate);
} catch (error) {
throw new Error(`Failed to create confirmation transaction: ${error}`);
}
const flag: AnkFlag = "NewTx";
const newTxMsg: NewTxMessage = {
'transaction': notificationInfo.transaction,
'tweak_data': null
}
connection.sendMessage(flag, JSON.stringify(newTxMsg));
await services.updateMessages(notificationInfo.new_network_msg);
return;
}
public async confirm_sender_address(msg: CachedMessage): Promise<void> { public async confirm_sender_address(msg: CachedMessage): Promise<void> {
const services = await Services.getInstance(); const services = await Services.getInstance();
const connection = await services.pickWebsocketConnectionRandom(); const connection = await services.pickWebsocketConnectionRandom();
@ -841,7 +864,7 @@ class Services {
let notificationInfo: createTransactionReturn; let notificationInfo: createTransactionReturn;
try { try {
const feeRate = 1; const feeRate = 1;
notificationInfo = services.sdkClient.answer_confirmation_transaction(msg, feeRate); notificationInfo = services.sdkClient.create_confirmation_transaction(msg.id, feeRate);
} catch (error) { } catch (error) {
throw new Error(`Failed to create confirmation transaction: ${error}`); throw new Error(`Failed to create confirmation transaction: ${error}`);
} }
@ -851,10 +874,11 @@ class Services {
'tweak_data': null 'tweak_data': null
} }
connection.sendMessage(flag, JSON.stringify(newTxMsg)); connection.sendMessage(flag, JSON.stringify(newTxMsg));
await services.updateMessages(notificationInfo.new_network_msg);
return; return;
} }
public async notify_address_for_message(sp_address: string, message: string): Promise<createTransactionReturn> { public async notify_address_for_message(sp_address: string, message: UnknownMessage): Promise<createTransactionReturn> {
const services = await Services.getInstance(); const services = await Services.getInstance();
const connection = await services.pickWebsocketConnectionRandom(); const connection = await services.pickWebsocketConnectionRandom();
if (!connection) { if (!connection) {
@ -863,62 +887,19 @@ class Services {
try { try {
const feeRate = 1; const feeRate = 1;
const commitment = services.sdkClient.create_commitment(message); let notificationInfo: createTransactionReturn = services.sdkClient.create_notification_transaction(sp_address, message, feeRate);
let notificationInfo: createTransactionReturn = services.sdkClient.create_notification_transaction(sp_address, commitment, feeRate);
const flag: AnkFlag = "NewTx"; const flag: AnkFlag = "NewTx";
const newTxMsg: NewTxMessage = { const newTxMsg: NewTxMessage = {
'transaction': notificationInfo.transaction, 'transaction': notificationInfo.transaction,
'tweak_data': null 'tweak_data': null
} }
connection.sendMessage(flag, JSON.stringify(newTxMsg)); connection.sendMessage(flag, JSON.stringify(newTxMsg));
console.info('Successfully sent notification transaction');
return notificationInfo; return notificationInfo;
} catch (error) { } catch (error) {
throw 'Failed to create notification transaction:", error'; throw 'Failed to create notification transaction:", error';
} }
} }
// public async encryptData(data: string, sharedSecret: Record<string, AnkSharedSecret>): Promise<Map<string, string>> {
// const services = await Services.getInstance();
// let msg_cipher: encryptWithNewKeyResult;
// try {
// msg_cipher = services.sdkClient.encrypt_with_new_key(data);
// } catch (error) {
// throw error;
// }
// let res = new Map<string, string>();
// for (const [recipient, secret] of Object.entries(sharedSecret)) {
// try {
// const key = secret.secret;
// const encryptedKey: string = await services.sdkClient.encrypt_with_key(msg_cipher.key, key);
// res.set(recipient, encryptedKey);
// } catch (error) {
// throw new Error(`Failed to encrypt key for recipient ${recipient}: ${error}`);
// }
// }
// return res;
// }
public async encryptData(data: string, key: string): Promise<string> {
const services = await Services.getInstance();
try {
let res: string = services.sdkClient.encrypt_with_key(data, key);
return res;
} catch (error) {
throw error;
}
}
public async decryptData(cipher: string, key: string): Promise<string> {
const services = await Services.getInstance();
try {
let res = services.sdkClient.try_decrypt_with_key(cipher, key);
return res;
} catch (error) {
throw error;
}
}
} }
export default Services; export default Services;

View File

@ -34,7 +34,7 @@ class WebSocketClient {
if (res.status === 'FaucetComplete') { if (res.status === 'FaucetComplete') {
// we received a faucet tx, there's nothing else to do // we received a faucet tx, there's nothing else to do
window.alert(`New faucet output\n${res.commited_in}`); window.alert(`New faucet output\n${res.commited_in}`);
await services.removeMessage(res.id); await services.updateMessages(res);
await services.updateOwnedOutputsForUser(); await services.updateOwnedOutputsForUser();
} else if (res.status === 'TxWaitingCipher') { } else if (res.status === 'TxWaitingCipher') {
// we received a tx but we don't have the cipher // we received a tx but we don't have the cipher
@ -47,6 +47,7 @@ class WebSocketClient {
await services.updateMessages(res); await services.updateMessages(res);
} else if (res.status === 'SentWaitingConfirmation') { } else if (res.status === 'SentWaitingConfirmation') {
// We are sender and we're waiting for the challenge that will confirm recipient got the transaction and the message // We are sender and we're waiting for the challenge that will confirm recipient got the transaction and the message
await services.updateMessages(res);
await services.updateOwnedOutputsForUser(); await services.updateOwnedOutputsForUser();
} else if (res.status === 'MustSpendConfirmation') { } else if (res.status === 'MustSpendConfirmation') {
// we received a challenge for a notification we made // we received a challenge for a notification we made
@ -54,7 +55,17 @@ class WebSocketClient {
window.alert(`Spending ${res.confirmed_by} to prove our identity`); window.alert(`Spending ${res.confirmed_by} to prove our identity`);
console.debug(`sending confirm message to ${res.recipient}`); console.debug(`sending confirm message to ${res.recipient}`);
await services.updateMessages(res); await services.updateMessages(res);
await services.answer_confirmation_message(res);
} else if (res.status === 'ReceivedMustConfirm') {
// we found a notification and decrypted the cipher
window.alert(`Received message from ${res.sender}\n${res.plaintext}`);
// we must spend the commited_in output to sender
await services.updateMessages(res);
await services.confirm_sender_address(res); await services.confirm_sender_address(res);
} else if (res.status === 'Complete') {
window.alert(`Received confirmation that ${res.sender} is the author of message ${res.plaintext}`)
await services.updateMessages(res);
await services.updateOwnedOutputsForUser();
} else { } else {
console.debug('Received an unimplemented valid message'); console.debug('Received an unimplemented valid message');
} }
@ -88,7 +99,7 @@ class WebSocketClient {
// console.debug("Sending message:", JSON.stringify(networkMessage)); // console.debug("Sending message:", JSON.stringify(networkMessage));
this.ws.send(JSON.stringify(networkMessage)); this.ws.send(JSON.stringify(networkMessage));
} else { } else {
console.error('WebSocket is not open. ReadyState:', this.ws.readyState); console.warn('WebSocket is not open. ReadyState:', this.ws.readyState);
this.messageQueue.push(message); this.messageQueue.push(message);
} }
} }