Refactor the whole api for the new 2fa model + various improvements
This commit is contained in:
parent
d737242bb5
commit
30759027b0
@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "sdk_client"
|
||||
crate-type = ["cdylib"]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
@ -17,7 +17,7 @@ wasm-logger = "0.2.0"
|
||||
rand = "0.8.5"
|
||||
log = "0.4.6"
|
||||
tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" }
|
||||
#sdk_common = { path = "../../../sdk_common" }
|
||||
# sdk_common = { path = "../sdk_common" }
|
||||
sdk_common = { git = "https://git.4nkweb.com/4nk/sdk_common.git", branch = "dev" }
|
||||
shamir = { git = "https://github.com/Sosthene00/shamir", branch = "master" }
|
||||
img-parts = "0.3.0"
|
||||
|
441
src/api.rs
441
src/api.rs
@ -35,7 +35,7 @@ use sdk_common::sp_client::silentpayments::{
|
||||
use serde_json::{Error as SerdeJsonError, Value};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tsify::Tsify;
|
||||
use tsify::{JsValueSerdeExt, Tsify};
|
||||
use wasm_bindgen::convert::FromWasmAbi;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@ -43,16 +43,13 @@ use sdk_common::network::{
|
||||
self, AnkFlag, AnkNetworkMsg, CachedMessage, CachedMessageStatus, CipherMessage, FaucetMessage,
|
||||
NewTxMessage,
|
||||
};
|
||||
use sdk_common::silentpayments::{
|
||||
create_transaction, create_transaction_for_address_with_shared_secret,
|
||||
create_transaction_spend_outpoint, map_outputs_to_sp_address,
|
||||
};
|
||||
use sdk_common::silentpayments::{create_transaction, map_outputs_to_sp_address};
|
||||
|
||||
use crate::wallet::generate_sp_wallet;
|
||||
use sdk_common::sp_client::spclient::{
|
||||
derive_keys_from_seed, OutputList, OutputSpendStatus, OwnedOutput, Recipient, SpClient,
|
||||
};
|
||||
use sdk_common::sp_client::spclient::{SpWallet, SpendKey};
|
||||
use crate::wallet::generate_sp_wallet;
|
||||
|
||||
use crate::user::{
|
||||
lock_local_device, lock_spending_client, Device, RevokeOutput, LOCAL_DEVICE, SPENDING_CLIENT,
|
||||
@ -61,13 +58,13 @@ use crate::{images, lock_messages, CACHEDMESSAGES};
|
||||
|
||||
use crate::process::Process;
|
||||
|
||||
type ApiResult<T: FromWasmAbi> = Result<T, ApiError>;
|
||||
pub type ApiResult<T: FromWasmAbi> = Result<T, ApiError>;
|
||||
|
||||
const IS_TESTNET: bool = true;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ApiError {
|
||||
message: String,
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ApiError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl From<AnyhowError> for ApiError {
|
||||
@ -188,7 +185,55 @@ pub fn get_address() -> ApiResult<String> {
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_new_wallet(birthday: u32, network_str: String) -> ApiResult<String> {
|
||||
pub fn restore_device_from_sp_wallet(sp_wallet: String) -> ApiResult<String> {
|
||||
let wallet: SpWallet = serde_json::from_str(&sp_wallet)?;
|
||||
|
||||
let mut device = Device::new(
|
||||
wallet.get_client().get_scan_key(),
|
||||
wallet.get_client().get_spend_key().into(),
|
||||
wallet.get_client().get_network(),
|
||||
);
|
||||
|
||||
let birthday = wallet.get_outputs().get_birthday();
|
||||
let outputs = wallet.get_outputs().to_outpoints_list();
|
||||
device
|
||||
.get_watch_only_mut()
|
||||
.get_mut_outputs()
|
||||
.set_birthday(birthday);
|
||||
device
|
||||
.get_watch_only_mut()
|
||||
.get_mut_outputs()
|
||||
.extend_from(outputs);
|
||||
|
||||
let json_return = serde_json::to_string(&device)?;
|
||||
|
||||
// Set the LOCAL_DEVICE const with the new value
|
||||
let mut local_device = lock_local_device()?;
|
||||
|
||||
if *local_device.get_watch_only().get_client() == SpClient::default() {
|
||||
*local_device = device;
|
||||
} else {
|
||||
return Err(ApiError {
|
||||
message: "device is already initialized".to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
// Set the LOGGED_WALLET with the new wallet to keep it in memory while we wait for the linking
|
||||
let mut spending_client = lock_spending_client()?;
|
||||
|
||||
if *spending_client == SpClient::default() {
|
||||
*spending_client = wallet.get_client().clone();
|
||||
} else {
|
||||
return Err(ApiError {
|
||||
message: "device is already initialized".to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(json_return)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_new_device(birthday: u32, network_str: String) -> ApiResult<String> {
|
||||
let network = Network::from_core_arg(&network_str)?;
|
||||
let wallet = generate_sp_wallet(None, Network::Regtest)?;
|
||||
|
||||
@ -207,20 +252,32 @@ pub fn create_new_wallet(birthday: u32, network_str: String) -> ApiResult<String
|
||||
let our_address = device.get_watch_only().get_client().get_receiving_address();
|
||||
|
||||
// Set the LOCAL_DEVICE const with the new value
|
||||
LOCAL_DEVICE
|
||||
.set(Mutex::new(device))
|
||||
.expect("We shouldn't already have initialized LOCAL_DEVICE now");
|
||||
let mut local_device = lock_local_device()?;
|
||||
|
||||
if *local_device.get_watch_only().get_client() == SpClient::default() {
|
||||
*local_device = device;
|
||||
} else {
|
||||
return Err(ApiError {
|
||||
message: "device is already initialized".to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
// Set the LOGGED_WALLET with the new wallet to keep it in memory while we wait for the linking
|
||||
SPENDING_CLIENT
|
||||
.set(Mutex::new(wallet.get_client().clone()))
|
||||
.expect("We shouldn't already have initialized SPENDING_CLIENT now");
|
||||
let mut spending_client = lock_spending_client()?;
|
||||
|
||||
if *spending_client == SpClient::default() {
|
||||
*spending_client = wallet.get_client().clone();
|
||||
} else {
|
||||
return Err(ApiError {
|
||||
message: "device is already initialized".to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(our_address)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn update_linked_address(
|
||||
pub fn pair_device(
|
||||
spend_sk_cipher: Vec<u8>,
|
||||
linked_with: String,
|
||||
revokation_output: String,
|
||||
@ -327,6 +384,104 @@ pub fn login_user(fee_rate: u32) -> ApiResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn logout() -> ApiResult<()> {
|
||||
let mut spending_client = lock_spending_client()?;
|
||||
|
||||
*spending_client = SpClient::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn dump_watch_only_wallet() -> ApiResult<String> {
|
||||
let device = lock_local_device()?;
|
||||
|
||||
Ok(serde_json::to_string(device.get_watch_only()).unwrap())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn reset_message_cache() -> ApiResult<()> {
|
||||
let mut cached_msg = lock_messages()?;
|
||||
|
||||
*cached_msg = vec![];
|
||||
|
||||
debug_assert!(cached_msg.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_message_cache(msg_cache: Vec<String>) -> ApiResult<()> {
|
||||
let mut cached_msg = lock_messages()?;
|
||||
|
||||
if !cached_msg.is_empty() {
|
||||
return Err(ApiError {
|
||||
message: "Message cache not empty".to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
let new_cache: Result<Vec<CachedMessage>, serde_json::Error> =
|
||||
msg_cache.iter().map(|m| serde_json::from_str(m)).collect();
|
||||
|
||||
*cached_msg = new_cache?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn dump_message_cache() -> ApiResult<Vec<String>> {
|
||||
let cached_msg = lock_messages()?;
|
||||
|
||||
let res: Vec<String> = cached_msg
|
||||
.iter()
|
||||
.map(|m| serde_json::to_string(m).unwrap())
|
||||
.collect();
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn reset_device() -> ApiResult<()> {
|
||||
let mut spending_client = lock_spending_client()?;
|
||||
|
||||
*spending_client = SpClient::default();
|
||||
|
||||
let mut device = lock_local_device()?;
|
||||
|
||||
*device = Device::default();
|
||||
|
||||
reset_message_cache()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_remote_key(
|
||||
remote_key: String,
|
||||
aes_key: Option<String>,
|
||||
nonce: Option<String>,
|
||||
) -> ApiResult<String> {
|
||||
let mut device = lock_local_device()?;
|
||||
let encrypted_key: Vec<u8>;
|
||||
if let (Some(key_hex), Some(nonce_hex)) = (aes_key, nonce) {
|
||||
// check that the hex we got for key and nonce is valid
|
||||
let mut key = [0u8; 32];
|
||||
let mut nonce = [0u8; 12];
|
||||
key.copy_from_slice(&Vec::from_hex(&key_hex)?);
|
||||
nonce.copy_from_slice(&Vec::from_hex(&nonce_hex)?);
|
||||
encrypted_key = device.encrypt_for_remote_device(
|
||||
SecretKey::from_str(&remote_key)?,
|
||||
Some(key),
|
||||
Some(nonce),
|
||||
)?;
|
||||
} else {
|
||||
encrypted_key =
|
||||
device.encrypt_for_remote_device(SecretKey::from_str(&remote_key)?, None, None)?;
|
||||
}
|
||||
Ok(encrypted_key.to_lower_hex_string())
|
||||
}
|
||||
|
||||
fn handle_recover_transaction(
|
||||
updated: HashMap<OutPoint, OwnedOutput>,
|
||||
tx: &Transaction,
|
||||
@ -357,48 +512,37 @@ fn handle_recover_transaction(
|
||||
// empty utxo_destroyed means we received this transaction
|
||||
if utxo_destroyed.is_empty() {
|
||||
// We first check for faucet transactions
|
||||
if let Some(pos) = messages.iter().position(|m| {
|
||||
if let Some(message) = messages.iter_mut().find(|m| {
|
||||
if m.status == CachedMessageStatus::FaucetWaiting {
|
||||
m.commitment.as_ref() == Some(&commitment_str)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
let message = messages.get_mut(pos).unwrap();
|
||||
match message.status {
|
||||
CachedMessageStatus::FaucetWaiting => {
|
||||
message.status = CachedMessageStatus::FaucetComplete;
|
||||
message.commited_in = utxo_created
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|(outpoint, _)| *outpoint);
|
||||
return Ok(message.clone());
|
||||
}
|
||||
// Actually this is unreachable
|
||||
CachedMessageStatus::FaucetComplete => return Ok(message.clone()),
|
||||
_ => (),
|
||||
}
|
||||
message.status = CachedMessageStatus::Closed;
|
||||
message.commited_in = utxo_created
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|(outpoint, _)| *outpoint);
|
||||
return Ok(message.clone());
|
||||
}
|
||||
|
||||
// we inspect inputs looking for links with previous tx
|
||||
for input in tx.input.iter() {
|
||||
if let Some(pos) = messages.iter().position(|m| {
|
||||
debug!("{:?}", Some(input.previous_output));
|
||||
m.confirmed_by == Some(input.previous_output)
|
||||
}) {
|
||||
let message = messages.get_mut(pos).unwrap();
|
||||
// If we are receiver, that's pretty much it, just set status to complete
|
||||
message.status = CachedMessageStatus::Complete;
|
||||
if let Some(message) = messages
|
||||
.iter_mut()
|
||||
.find(|m| m.confirmed_by == Some(input.previous_output))
|
||||
{
|
||||
// If we are receiver, that's pretty much it
|
||||
message.status = CachedMessageStatus::Trusted;
|
||||
return Ok(message.clone());
|
||||
} else if let Some(pos) = messages
|
||||
.iter()
|
||||
.position(|m| m.commited_in == Some(input.previous_output))
|
||||
} else if let Some(message) = messages
|
||||
.iter_mut()
|
||||
.find(|m| m.commited_in == Some(input.previous_output))
|
||||
{
|
||||
// sender needs to spent it back again to receiver
|
||||
let (outpoint, output) = utxo_created.into_iter().next().unwrap();
|
||||
|
||||
let message = messages.get_mut(pos).unwrap();
|
||||
|
||||
message.confirmed_by = Some(outpoint.clone());
|
||||
message.status = CachedMessageStatus::MustSpendConfirmation;
|
||||
|
||||
@ -420,7 +564,7 @@ fn handle_recover_transaction(
|
||||
);
|
||||
|
||||
let mut plaintext: Vec<u8> = vec![];
|
||||
if let Some(cipher_pos) = messages.iter().position(|m| {
|
||||
if let Some(message) = messages.iter_mut().find(|m| {
|
||||
if m.status != CachedMessageStatus::CipherWaitingTx {
|
||||
return false;
|
||||
}
|
||||
@ -432,15 +576,14 @@ fn handle_recover_transaction(
|
||||
return false;
|
||||
}
|
||||
}) {
|
||||
let message = messages.get_mut(cipher_pos).unwrap();
|
||||
|
||||
let (outpoint, output) = utxo_created.into_iter().next().unwrap();
|
||||
|
||||
let cipher_msg: CipherMessage = serde_json::from_slice(&plaintext)?;
|
||||
message.commited_in = Some(outpoint.clone());
|
||||
message.shared_secret = Some(shared_secret.to_byte_array().to_lower_hex_string());
|
||||
message.commitment = Some(commitment_str);
|
||||
message.plaintext = Some(cipher_msg.message);
|
||||
message.plaintext.push(cipher_msg.message);
|
||||
message.ciphertext = None;
|
||||
message.sender = Some(cipher_msg.sender);
|
||||
message.recipient = Some(sp_wallet.get_client().get_receiving_address());
|
||||
message.status = CachedMessageStatus::ReceivedMustConfirm;
|
||||
@ -466,7 +609,6 @@ fn handle_recover_transaction(
|
||||
// We only need to return the message
|
||||
// eiter this is notification, a challenge, or response to a challenge
|
||||
// if notification, commitment is the same than in the message
|
||||
// if challenge or response, commitment is H(commitment | b_scan), b_scan being different depending on who we are
|
||||
if let Some(message) = messages.iter().find(|m| {
|
||||
if commitment.is_empty() || m.commitment.is_none() {
|
||||
return false;
|
||||
@ -480,23 +622,29 @@ fn handle_recover_transaction(
|
||||
.unwrap()
|
||||
== commitment
|
||||
}
|
||||
CachedMessageStatus::MustSpendConfirmation
|
||||
| CachedMessageStatus::ReceivedMustConfirm => {
|
||||
// we compute the potential commitment
|
||||
let m_commitment = m
|
||||
.commitment
|
||||
.as_ref()
|
||||
.map(|c| Vec::from_hex(&c).unwrap())
|
||||
.unwrap();
|
||||
let mut buf = [0u8; 64];
|
||||
buf[..32].copy_from_slice(&m_commitment);
|
||||
buf[32..]
|
||||
.copy_from_slice(&sp_wallet.get_client().get_scan_key().secret_bytes());
|
||||
|
||||
let mut engine = sha256::HashEngine::default();
|
||||
engine.write_all(&buf).unwrap();
|
||||
let hash = sha256::Hash::from_engine(engine);
|
||||
hash.to_byte_array().to_vec() == commitment
|
||||
CachedMessageStatus::ReceivedMustConfirm => {
|
||||
// we look for a message that has commited_in as input of the transaction
|
||||
if let Some(_) = tx
|
||||
.input
|
||||
.iter()
|
||||
.find(|i| Some(i.previous_output) == m.commited_in)
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
CachedMessageStatus::MustSpendConfirmation | CachedMessageStatus::Trusted => {
|
||||
// we look for a message that has confirm_by as input
|
||||
if let Some(_) = tx
|
||||
.input
|
||||
.iter()
|
||||
.find(|i| Some(i.previous_output) == m.confirmed_by)
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
@ -504,7 +652,7 @@ fn handle_recover_transaction(
|
||||
return Ok(message.clone());
|
||||
} else {
|
||||
return Err(anyhow::Error::msg(
|
||||
"We spent a transaction for a commitment we don't know",
|
||||
"Failed to find the message for one of our transactions",
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -573,21 +721,35 @@ pub fn parse_network_msg(raw: String, fee_rate: u32) -> ApiResult<CachedMessage>
|
||||
// let's try to decrypt with keys we found in transactions but haven't used yet
|
||||
let mut messages = lock_messages()?;
|
||||
let cipher = Vec::from_hex(&ank_msg.content.trim_matches('\"'))?;
|
||||
let cipher_pos = messages.iter().position(|m| {
|
||||
debug!("Trying message: {:?}", m);
|
||||
if m.status != CachedMessageStatus::TxWaitingCipher {
|
||||
return false;
|
||||
if let Some(message) = messages.iter_mut().find(|m| {
|
||||
// debug!("Trying message: {:?}", m);
|
||||
match m.status {
|
||||
CachedMessageStatus::TxWaitingCipher | CachedMessageStatus::Trusted => {
|
||||
m.try_decrypt_cipher(cipher.clone()).is_ok()
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
m.try_decrypt_cipher(cipher.clone()).is_ok()
|
||||
});
|
||||
if cipher_pos.is_some() {
|
||||
let mut message = messages.get_mut(cipher_pos.unwrap()).unwrap();
|
||||
}) {
|
||||
let plain = message.try_decrypt_cipher(cipher).unwrap();
|
||||
let cipher_msg: CipherMessage = serde_json::from_slice(&plain)?;
|
||||
message.plaintext = Some(cipher_msg.message);
|
||||
message.sender = Some(cipher_msg.sender);
|
||||
message.ciphertext = Some(ank_msg.content);
|
||||
message.status = CachedMessageStatus::ReceivedMustConfirm;
|
||||
if message.status == CachedMessageStatus::TxWaitingCipher {
|
||||
// does the retrieved message match with the commited hash?
|
||||
let hash = create_commitment(serde_json::to_string(&cipher_msg)?);
|
||||
if Some(hash) != message.commitment {
|
||||
return Err(ApiError {
|
||||
message: "Message doesn't match commitment".to_owned(),
|
||||
});
|
||||
}
|
||||
message.plaintext.push(cipher_msg.message);
|
||||
message.sender = Some(cipher_msg.sender);
|
||||
message.ciphertext = None;
|
||||
message.status = CachedMessageStatus::ReceivedMustConfirm;
|
||||
} else {
|
||||
// We're receiving a message for some action already engaged
|
||||
// We could check the sender, but do we really care since we alredy trust it?
|
||||
// Let's update the message by pushing what we just found out
|
||||
message.plaintext.push(cipher_msg.message);
|
||||
}
|
||||
return Ok(message.clone());
|
||||
} else {
|
||||
// let's keep it in case we receive the transaction later
|
||||
@ -608,20 +770,20 @@ pub fn parse_network_msg(raw: String, fee_rate: u32) -> ApiResult<CachedMessage>
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn get_outpoints_for_user() -> ApiResult<outputs_list> {
|
||||
pub fn get_outputs() -> ApiResult<JsValue> {
|
||||
let device = lock_local_device()?;
|
||||
let outputs = device.get_watch_only().get_outputs().clone();
|
||||
Ok(outputs_list(outputs))
|
||||
Ok(JsValue::from_serde(&outputs.to_outpoints_list())?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn get_available_amount_for_user() -> ApiResult<u64> {
|
||||
pub fn get_available_amount() -> ApiResult<u64> {
|
||||
let device = lock_local_device()?;
|
||||
|
||||
Ok(device.get_watch_only().get_outputs().get_balance().to_sat())
|
||||
}
|
||||
|
||||
#[derive(Tsify, Serialize, Deserialize, Default)]
|
||||
#[derive(Debug, Tsify, Serialize, Deserialize, Default)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct createTransactionReturn {
|
||||
@ -661,7 +823,7 @@ pub fn answer_confirmation_transaction(
|
||||
|
||||
let spending_client = lock_spending_client()?.clone();
|
||||
|
||||
let sp_wallet = SpWallet::new(spending_client, Some(current_outputs))?;
|
||||
let sp_wallet = SpWallet::new(spending_client, Some(current_outputs), vec![])?;
|
||||
|
||||
let recipient = Recipient {
|
||||
address: sp_address.into(),
|
||||
@ -669,21 +831,20 @@ pub fn answer_confirmation_transaction(
|
||||
nb_outputs: 1,
|
||||
};
|
||||
|
||||
let confirmed_by = message.confirmed_by.clone().unwrap();
|
||||
let commited_in = message.commited_in.clone().unwrap();
|
||||
let confirmed_by = message.confirmed_by.as_ref().unwrap();
|
||||
|
||||
let signed_psbt = create_transaction_spend_outpoint(
|
||||
&confirmed_by,
|
||||
let signed_psbt = create_transaction(
|
||||
&vec![confirmed_by],
|
||||
&sp_wallet,
|
||||
recipient,
|
||||
&commited_in.txid,
|
||||
None,
|
||||
Amount::from_sat(fee_rate.into()),
|
||||
message.recipient.clone(),
|
||||
)?;
|
||||
|
||||
let final_tx = signed_psbt.extract_tx()?;
|
||||
|
||||
message.status = CachedMessageStatus::Complete;
|
||||
message.status = CachedMessageStatus::Trusted;
|
||||
|
||||
Ok(createTransactionReturn {
|
||||
txid: final_tx.txid().to_string(),
|
||||
@ -701,7 +862,7 @@ pub fn create_confirmation_transaction(
|
||||
let mut messages = lock_messages()?;
|
||||
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() {
|
||||
if m.sender.is_none() || m.commited_in.is_none() || m.plaintext.is_empty() {
|
||||
return Err(ApiError {
|
||||
message: "Invalid network message".to_owned(),
|
||||
});
|
||||
@ -716,22 +877,11 @@ pub fn create_confirmation_transaction(
|
||||
|
||||
let sp_address: SilentPaymentAddress = message.sender.as_ref().unwrap().as_str().try_into()?;
|
||||
|
||||
let mut local_device = lock_local_device()?;
|
||||
|
||||
// Are we waiting for pairing?
|
||||
let remote_key_cipher: Option<Vec<u8>>;
|
||||
if !local_device.is_linked() {
|
||||
let remote_spend_sk = SecretKey::from_str(message.plaintext.as_deref().unwrap())?;
|
||||
remote_key_cipher = Some(local_device.encrypt_for_remote_device(remote_spend_sk)?);
|
||||
} else {
|
||||
remote_key_cipher = None;
|
||||
}
|
||||
|
||||
let current_outputs = local_device.get_watch_only().get_outputs().clone();
|
||||
let current_outputs = lock_local_device()?.get_watch_only().get_outputs().clone();
|
||||
|
||||
let spending_client = lock_spending_client()?.clone();
|
||||
|
||||
let sp_wallet = SpWallet::new(spending_client, Some(current_outputs))?;
|
||||
let sp_wallet = SpWallet::new(spending_client, Some(current_outputs), vec![])?;
|
||||
|
||||
let recipient = Recipient {
|
||||
address: sp_address.into(),
|
||||
@ -739,15 +889,15 @@ pub fn create_confirmation_transaction(
|
||||
nb_outputs: 1,
|
||||
};
|
||||
|
||||
let commited_in = message.commited_in.clone().unwrap();
|
||||
let commited_in = message.commited_in.as_ref().unwrap();
|
||||
|
||||
let signed_psbt = create_transaction_spend_outpoint(
|
||||
&commited_in,
|
||||
let signed_psbt = create_transaction(
|
||||
&vec![commited_in],
|
||||
&sp_wallet,
|
||||
recipient,
|
||||
&commited_in.txid,
|
||||
remote_key_cipher,
|
||||
None,
|
||||
Amount::from_sat(fee_rate.into()),
|
||||
message.sender.clone(),
|
||||
)?;
|
||||
|
||||
// what's the vout of the output sent to sender?
|
||||
@ -778,18 +928,37 @@ pub fn create_pairing_transaction(
|
||||
) -> ApiResult<createTransactionReturn> {
|
||||
let message: CipherMessage;
|
||||
{
|
||||
let mut spending_wallet = lock_spending_client()?;
|
||||
let mut spending_client = lock_spending_client()?;
|
||||
|
||||
let our_address = spending_wallet.get_receiving_address();
|
||||
let spend_sk: SecretKey = spending_wallet.get_spend_key().try_into()?;
|
||||
let our_address = spending_client.get_receiving_address();
|
||||
let spend_sk: SecretKey = spending_client.get_spend_key().try_into()?;
|
||||
|
||||
message = CipherMessage::new(our_address, format!("{}", spend_sk.display_secret()));
|
||||
|
||||
// we forget our own wallet
|
||||
*spending_wallet = SpClient::default();
|
||||
}
|
||||
|
||||
create_notification_transaction(address, message, fee_rate)
|
||||
let mut res = create_notification_transaction(address, message, fee_rate);
|
||||
|
||||
// we slightly alter the message
|
||||
if let Ok(ref mut new_msg) = res {
|
||||
new_msg.new_network_msg.plaintext = vec![]; // we don't want to let that information available
|
||||
new_msg.new_network_msg.status = CachedMessageStatus::Trusted;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Update the msg in cache
|
||||
let mut messages = lock_messages()?;
|
||||
let mut updated = res.as_ref().unwrap().new_network_msg.clone();
|
||||
let pos = messages.iter().position(|m| m.id == updated.id).unwrap();
|
||||
// clone the cached message and erase the ciphertext
|
||||
updated.ciphertext = None;
|
||||
messages[pos] = updated;
|
||||
|
||||
// we forget our own wallet
|
||||
let mut spending_client = lock_spending_client()?;
|
||||
*spending_client = SpClient::default();
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -815,7 +984,7 @@ pub fn create_login_transaction(fee_rate: u32) -> ApiResult<createTransactionRet
|
||||
#[wasm_bindgen]
|
||||
pub fn create_notification_transaction(
|
||||
address: String,
|
||||
message: CipherMessage,
|
||||
cipher_message: CipherMessage,
|
||||
fee_rate: u32,
|
||||
) -> ApiResult<createTransactionReturn> {
|
||||
let sp_address: SilentPaymentAddress = address.as_str().try_into()?;
|
||||
@ -826,7 +995,7 @@ pub fn create_notification_transaction(
|
||||
|
||||
let spending_client = lock_spending_client()?.clone();
|
||||
|
||||
let sp_wallet = SpWallet::new(spending_client, Some(current_outputs))?;
|
||||
let sp_wallet = SpWallet::new(spending_client, Some(current_outputs), vec![])?;
|
||||
|
||||
let recipient = Recipient {
|
||||
address: sp_address.into(),
|
||||
@ -834,18 +1003,20 @@ pub fn create_notification_transaction(
|
||||
nb_outputs: 1,
|
||||
};
|
||||
|
||||
let commitment = create_commitment(serde_json::to_string(&message)?);
|
||||
let commitment = create_commitment(serde_json::to_string(&cipher_message)?);
|
||||
|
||||
let signed_psbt = create_transaction_for_address_with_shared_secret(
|
||||
recipient,
|
||||
let signed_psbt = create_transaction(
|
||||
&vec![],
|
||||
&sp_wallet,
|
||||
Some(&commitment),
|
||||
recipient,
|
||||
Some(Vec::from_hex(&commitment)?),
|
||||
Amount::from_sat(fee_rate.into()),
|
||||
None,
|
||||
)?;
|
||||
|
||||
let psbt = Psbt::from_str(&signed_psbt)?;
|
||||
|
||||
let partial_secret = sp_wallet.get_client().get_partial_secret_from_psbt(&psbt)?;
|
||||
let partial_secret = sp_wallet
|
||||
.get_client()
|
||||
.get_partial_secret_from_psbt(&signed_psbt)?;
|
||||
|
||||
let shared_point = sp_utils::sending::calculate_ecdh_shared_secret(
|
||||
&sp_address.get_scan_key(),
|
||||
@ -854,26 +1025,26 @@ pub fn create_notification_transaction(
|
||||
|
||||
let shared_secret = AnkSharedSecret::new(shared_point);
|
||||
|
||||
debug!(
|
||||
"Created transaction with secret {}",
|
||||
shared_secret.to_byte_array().to_lower_hex_string()
|
||||
);
|
||||
// debug!(
|
||||
// "Created transaction with secret {}",
|
||||
// shared_secret.to_byte_array().to_lower_hex_string()
|
||||
// );
|
||||
|
||||
let cipher = encrypt_with_key(
|
||||
serde_json::to_string(&message)?,
|
||||
serde_json::to_string(&cipher_message)?,
|
||||
shared_secret.to_byte_array().to_lower_hex_string(),
|
||||
)?;
|
||||
|
||||
// update our cache
|
||||
let sp_address2vouts = map_outputs_to_sp_address(&signed_psbt)?;
|
||||
let sp_address2vouts = map_outputs_to_sp_address(&signed_psbt.to_string())?;
|
||||
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
|
||||
let final_tx = psbt.extract_tx()?;
|
||||
let final_tx = signed_psbt.extract_tx()?;
|
||||
let mut new_msg = CachedMessage::new();
|
||||
new_msg.plaintext = Some(message.message);
|
||||
new_msg.plaintext.push(cipher_message.message);
|
||||
new_msg.ciphertext = Some(cipher);
|
||||
new_msg.commitment = Some(commitment);
|
||||
new_msg.commited_in = Some(OutPoint {
|
||||
@ -882,7 +1053,7 @@ pub fn create_notification_transaction(
|
||||
});
|
||||
new_msg.shared_secret = Some(shared_secret.to_byte_array().to_lower_hex_string());
|
||||
new_msg.recipient = Some(address);
|
||||
new_msg.sender = Some(sp_wallet.get_client().get_receiving_address());
|
||||
new_msg.sender = Some(cipher_message.sender);
|
||||
new_msg.status = CachedMessageStatus::SentWaitingConfirmation;
|
||||
lock_messages()?.push(new_msg.clone());
|
||||
|
||||
|
38
src/user.rs
38
src/user.rs
@ -28,8 +28,6 @@ use sdk_common::crypto::{
|
||||
AeadCore, Aes256Decryption, Aes256Encryption, Aes256Gcm, HalfKey, KeyInit, Purpose,
|
||||
};
|
||||
|
||||
|
||||
|
||||
pub static LOCAL_DEVICE: OnceLock<Mutex<Device>> = OnceLock::new();
|
||||
|
||||
pub fn lock_local_device() -> Result<MutexGuard<'static, Device>> {
|
||||
@ -82,7 +80,7 @@ impl Device {
|
||||
.expect("watch_only_client creation failed");
|
||||
|
||||
let mut watch_only_wallet =
|
||||
SpWallet::new(watch_only_client, None).expect("watch_only_wallet creation failed");
|
||||
SpWallet::new(watch_only_client, None, vec![]).expect("watch_only_wallet creation failed");
|
||||
|
||||
let spend_sk_cipher = vec![];
|
||||
let remote_device_key = [0; 32];
|
||||
@ -116,12 +114,34 @@ impl Device {
|
||||
self.spend_sk_cipher.clone()
|
||||
}
|
||||
|
||||
pub fn encrypt_for_remote_device(&mut self, remote_spend_sk: SecretKey) -> Result<Vec<u8>> {
|
||||
// encrypt with the remote_device_key
|
||||
let encryption = Aes256Encryption::new(
|
||||
Purpose::ThirtyTwoBytes,
|
||||
remote_spend_sk.secret_bytes().to_vec(),
|
||||
)?;
|
||||
pub fn encrypt_for_remote_device(
|
||||
&mut self,
|
||||
remote_spend_sk: SecretKey,
|
||||
aes_key: Option<[u8; 32]>,
|
||||
nonce: Option<[u8; 12]>,
|
||||
) -> Result<Vec<u8>> {
|
||||
let encryption: Aes256Encryption;
|
||||
match (aes_key, nonce) {
|
||||
(Some(key), Some(nonce)) => {
|
||||
encryption = Aes256Encryption::import_key(
|
||||
Purpose::ThirtyTwoBytes,
|
||||
remote_spend_sk.secret_bytes().to_vec(),
|
||||
key,
|
||||
nonce,
|
||||
)?;
|
||||
}
|
||||
(None, None) => {
|
||||
encryption = Aes256Encryption::new(
|
||||
Purpose::ThirtyTwoBytes,
|
||||
remote_spend_sk.secret_bytes().to_vec(),
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::msg(
|
||||
"aes_key and nonce must either both be set or empty",
|
||||
))
|
||||
}
|
||||
}
|
||||
let remote_spend_sk_cipher = encryption.encrypt_with_aes_key();
|
||||
self.remote_device_key = encryption.export_key();
|
||||
remote_spend_sk_cipher
|
||||
|
@ -22,7 +22,7 @@ pub fn generate_sp_wallet(label: Option<String>, network: Network) -> anyhow::Re
|
||||
our_address.to_string()
|
||||
);
|
||||
|
||||
let res = SpWallet::new(sp_client, None)?;
|
||||
let res = SpWallet::new(sp_client, None, vec![])?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
479
tests/browser.rs
Normal file
479
tests/browser.rs
Normal file
@ -0,0 +1,479 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use log::debug;
|
||||
use sdk_client::api::{
|
||||
answer_confirmation_transaction, create_confirmation_transaction,
|
||||
create_notification_transaction, encrypt_remote_key, get_outputs, pair_device,
|
||||
parse_network_msg, reset_device, restore_device_from_sp_wallet, set_message_cache, setup,
|
||||
};
|
||||
use sdk_common::network::{
|
||||
AnkNetworkMsg, CachedMessage, CipherMessage, NewTxMessage, TrustedChannel,
|
||||
};
|
||||
use sdk_common::sp_client::bitcoin::consensus::deserialize;
|
||||
use sdk_common::sp_client::bitcoin::hex::FromHex;
|
||||
use sdk_common::sp_client::bitcoin::{OutPoint, ScriptBuf, Transaction};
|
||||
use sdk_common::sp_client::silentpayments::utils::receiving::{
|
||||
calculate_tweak_data, get_pubkey_from_input,
|
||||
};
|
||||
use sdk_common::sp_client::spclient::{OwnedOutput, SpWallet};
|
||||
use serde_json;
|
||||
|
||||
use tsify::JsValueSerdeExt;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
// We're using alice and bob for clarity, but it's important to remember that here Alice and Bob are the same person
|
||||
const ALICE_START_WALLET: &str = "{\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"}}}}";
|
||||
const ALICE_CHALLENGE_WALLET: &str = "{\"client\":{\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]},\"network\":\"regtest\"},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":0,\"outputs\":{\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:1\":{\"blockheight\":0,\"tweak\":\"28994b2f2ee8e5f35d6d2dcdee1580d0455fe3dc37f81e0a36864473ee86f5c4\",\"amount\":3937246,\"script\":\"51207d06144e982b6fd38a85d6152f1f95746b059553258a31e04df97fe6b5f19ea1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"},\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9\"}}}}}";
|
||||
const ALICE_ANSWER_WALLET: &str = "{\"client\":{\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]},\"network\":\"regtest\"},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":0,\"outputs\":{\"57ca073676fa6130397ce1e4738278952483cee943daa1664b9a1807d4700066:0\":{\"blockheight\":0,\"tweak\":\"b99660cd873026aebeb378ae2ff32aa1c79a5946c462a36a01247f8afcfc5dba\",\"amount\":1046,\"script\":\"51209b80b6e8b3e93437c82a26c977860fb4d2bef9f27f6332f811a41187592502ed\",\"label\":null,\"spend_status\":\"Unspent\"},\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:1\":{\"blockheight\":0,\"tweak\":\"28994b2f2ee8e5f35d6d2dcdee1580d0455fe3dc37f81e0a36864473ee86f5c4\",\"amount\":3937246,\"script\":\"51207d06144e982b6fd38a85d6152f1f95746b059553258a31e04df97fe6b5f19ea1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"},\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9\"}}}}}";
|
||||
const BOB_START_WALLET: &str = "{\"client\":{\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]},\"network\":\"regtest\"},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
|
||||
const BOB_CHALLENGE_WALLET: &str = "{\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":0,\"outputs\":{\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"},\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
|
||||
const BOB_ANSWER_WALLET: &str = "{\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":0,\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":0,\"outputs\":{\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":{\"Spent\":\"57ca073676fa6130397ce1e4738278952483cee943daa1664b9a1807d4700066\"}},\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
|
||||
const ALICE_SPK: &str = "51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1";
|
||||
const REVOKATION_OUTPUT: &str =
|
||||
"3dd51588af6cc2e4ff8e405fd2620f19c8f4e09e05692ad57a9208a061687295:3";
|
||||
const ALICE_CHALLENGE_CACHE: &str = "{\"id\":1283337801,\"status\":\"SentWaitingConfirmation\",\"ciphertext\":\"d53ec574b09068cc661a76e6934cb9131df1b8d84b0ec791cb2c2789c42aae53bf2e7d514a00b2c27a78a786fee97f55dd022a99551770c4f05edfc4296993b2f82104933f61cd0d7c2e02a21188c58b492ac0a190e711ddc697505e106df087f8f9d3591aa30798608c2a0eb1e7c124d54e88dd9271884a3fe7f24f628a150d0dc90ded4753f9aa4e2730c7a339bfd7ef0faa02ee6d4cd3b1de36f8796485bd6bd8eee0e78c1e6d6c822dd9532df4c963835ea412bb36827cd89d3466\",\"plaintext\":[\"TEST\"],\"commited_in\":\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:0\",\"tie_by\":null,\"commitment\":\"d12f3c5b37240bc3abf2976f41fdf9a594f0680aafd2781ac448f80440fbeb99\",\"sender\":\"sprt1qqf50y6deqe7dqg3vj562xf3lmktqe3sct78ha6e9jh54sa3q2m5esq7x2tz0xrpm0ekclyyaszc2sh3ksm5gkumxpwegpa80lv5wgsuefqaufx8q\",\"recipient\":\"sprt1qqf2kqh8n7lkupngfk2fxtmvyaq8sy3v2ramyeryweadqmsz7l6tg2qc9n4dl42ff8klz28nznr7mfzh6263xv555stwzextl2v4lvhg3aqq7ru8u\",\"shared_secret\":\"5a28562161f939bf77983df807dd914e73f02ea67a21ed976d214452887ae43e\",\"key\":null,\"confirmed_by\":null,\"timestamp\":1720966687785,\"error\":null}";
|
||||
const ALICE_ANSWER_CACHE: &str = "{\"id\":1283337801,\"status\":\"MustSpendConfirmation\",\"ciphertext\":\"d53ec574b09068cc661a76e6934cb9131df1b8d84b0ec791cb2c2789c42aae53bf2e7d514a00b2c27a78a786fee97f55dd022a99551770c4f05edfc4296993b2f82104933f61cd0d7c2e02a21188c58b492ac0a190e711ddc697505e106df087f8f9d3591aa30798608c2a0eb1e7c124d54e88dd9271884a3fe7f24f628a150d0dc90ded4753f9aa4e2730c7a339bfd7ef0faa02ee6d4cd3b1de36f8796485bd6bd8eee0e78c1e6d6c822dd9532df4c963835ea412bb36827cd89d3466\",\"plaintext\":[\"TEST\"],\"commited_in\":\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:0\",\"tie_by\":null,\"commitment\":\"d12f3c5b37240bc3abf2976f41fdf9a594f0680aafd2781ac448f80440fbeb99\",\"sender\":\"sprt1qqf50y6deqe7dqg3vj562xf3lmktqe3sct78ha6e9jh54sa3q2m5esq7x2tz0xrpm0ekclyyaszc2sh3ksm5gkumxpwegpa80lv5wgsuefqaufx8q\",\"recipient\":\"sprt1qqf2kqh8n7lkupngfk2fxtmvyaq8sy3v2ramyeryweadqmsz7l6tg2qc9n4dl42ff8klz28nznr7mfzh6263xv555stwzextl2v4lvhg3aqq7ru8u\",\"shared_secret\":\"5a28562161f939bf77983df807dd914e73f02ea67a21ed976d214452887ae43e\",\"key\":null,\"confirmed_by\":\"57ca073676fa6130397ce1e4738278952483cee943daa1664b9a1807d4700066:0\",\"timestamp\":1720966687785,\"error\":null}";
|
||||
const BOB_CHALLENGE_CACHE: &str = "{\"id\":2639151119,\"status\":\"ReceivedMustConfirm\",\"ciphertext\":null,\"plaintext\":[\"TEST\"],\"commited_in\":\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:0\",\"tie_by\":null,\"commitment\":\"d12f3c5b37240bc3abf2976f41fdf9a594f0680aafd2781ac448f80440fbeb99\",\"sender\":\"sprt1qqf50y6deqe7dqg3vj562xf3lmktqe3sct78ha6e9jh54sa3q2m5esq7x2tz0xrpm0ekclyyaszc2sh3ksm5gkumxpwegpa80lv5wgsuefqaufx8q\",\"recipient\":\"sprt1qqf2kqh8n7lkupngfk2fxtmvyaq8sy3v2ramyeryweadqmsz7l6tg2qc9n4dl42ff8klz28nznr7mfzh6263xv555stwzextl2v4lvhg3aqq7ru8u\",\"shared_secret\":\"5a28562161f939bf77983df807dd914e73f02ea67a21ed976d214452887ae43e\",\"key\":null,\"confirmed_by\":null,\"timestamp\":1721315780531,\"error\":null}";
|
||||
const BOB_ANSWER_CACHE: &str = "{\"id\":2639151119,\"status\":\"ReceivedMustConfirm\",\"ciphertext\":null,\"plaintext\":[\"TEST\"],\"commited_in\":\"d5edbf1f60ae4fd7d698626b44ed1e85edddf65b45f62e5f82928a9e12759db9:0\",\"tie_by\":null,\"commitment\":\"d12f3c5b37240bc3abf2976f41fdf9a594f0680aafd2781ac448f80440fbeb99\",\"sender\":\"sprt1qqf50y6deqe7dqg3vj562xf3lmktqe3sct78ha6e9jh54sa3q2m5esq7x2tz0xrpm0ekclyyaszc2sh3ksm5gkumxpwegpa80lv5wgsuefqaufx8q\",\"recipient\":\"sprt1qqf2kqh8n7lkupngfk2fxtmvyaq8sy3v2ramyeryweadqmsz7l6tg2qc9n4dl42ff8klz28nznr7mfzh6263xv555stwzextl2v4lvhg3aqq7ru8u\",\"shared_secret\":\"5a28562161f939bf77983df807dd914e73f02ea67a21ed976d214452887ae43e\",\"key\":null,\"confirmed_by\":\"57ca073676fa6130397ce1e4738278952483cee943daa1664b9a1807d4700066:0\",\"timestamp\":1721315780531,\"error\":null}";
|
||||
|
||||
fn helper_switch_device(wallet: String) {
|
||||
reset_device().unwrap();
|
||||
// debug!("{}", wallet);
|
||||
restore_device_from_sp_wallet(wallet.clone()).unwrap();
|
||||
// debug!("Restored device with wallet {}", wallet);
|
||||
}
|
||||
|
||||
fn helper_get_alice_address() -> String {
|
||||
let wallet: SpWallet = serde_json::from_str(ALICE_START_WALLET).unwrap();
|
||||
wallet.get_client().get_receiving_address()
|
||||
}
|
||||
|
||||
fn helper_get_bob_address() -> String {
|
||||
let wallet: SpWallet = serde_json::from_str(BOB_START_WALLET).unwrap();
|
||||
wallet.get_client().get_receiving_address()
|
||||
}
|
||||
|
||||
fn helper_get_tweak_data(transaction: &str, spk: ScriptBuf) -> String {
|
||||
let tx = deserialize::<Transaction>(&Vec::from_hex(transaction).unwrap()).unwrap();
|
||||
let prevout = tx.input.get(0).unwrap().to_owned();
|
||||
let outpoint_data = (
|
||||
prevout.previous_output.txid.to_string(),
|
||||
prevout.previous_output.vout,
|
||||
);
|
||||
let input_pubkey =
|
||||
get_pubkey_from_input(&vec![], &prevout.witness.to_vec(), spk.as_bytes()).unwrap();
|
||||
let tweak_data =
|
||||
calculate_tweak_data(&vec![&input_pubkey.unwrap()], &vec![outpoint_data]).unwrap();
|
||||
tweak_data.to_string()
|
||||
}
|
||||
|
||||
fn helper_parse_transaction(transaction: &str, tweak_data: String) -> CachedMessage {
|
||||
let new_tx_msg =
|
||||
serde_json::to_string(&NewTxMessage::new(transaction.to_owned(), Some(tweak_data)))
|
||||
.unwrap();
|
||||
let network_msg = serde_json::to_string(&AnkNetworkMsg::new(
|
||||
sdk_common::network::AnkFlag::NewTx,
|
||||
&new_tx_msg,
|
||||
))
|
||||
.unwrap();
|
||||
let result = parse_network_msg(network_msg, 1);
|
||||
match result {
|
||||
Ok(m) => m,
|
||||
Err(e) => panic!("Unexpected error: {}", e.message),
|
||||
}
|
||||
}
|
||||
|
||||
fn helper_parse_ank_msg(ank_network_msg: String) -> CachedMessage {
|
||||
let result = parse_network_msg(ank_network_msg, 1);
|
||||
match result {
|
||||
Ok(r) => return r,
|
||||
Err(e) => panic!("Unexpected error: {}", e.message),
|
||||
};
|
||||
}
|
||||
|
||||
fn helper_link_alice() {
|
||||
const ALICE_SPEND_KEY: &str =
|
||||
"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b";
|
||||
let encrypted_alice_key = encrypt_remote_key(
|
||||
ALICE_SPEND_KEY.to_owned(),
|
||||
Some("073ec0cd90cd77f5486cbfb586ed639c4d435d39bb038dc905d7a65762d29ff9".to_owned()),
|
||||
Some("46b4e8e9cfb1b6fd139d480f".to_owned()),
|
||||
)
|
||||
.unwrap();
|
||||
pair_device(
|
||||
Vec::from_hex(&encrypted_alice_key).unwrap(),
|
||||
helper_get_alice_address(),
|
||||
REVOKATION_OUTPUT.to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn helper_link_bob() {
|
||||
const BOB_ENCRYPTED_KEY: &str = "62f10cffb1ed127ef88826c0e65d21dc337c763db53a3d814e1bf21460b53d84d612995ab3b272ac395221bf49cb54df6250502fc8941ba06d85bd58";
|
||||
pair_device(
|
||||
Vec::from_hex(BOB_ENCRYPTED_KEY).unwrap(),
|
||||
helper_get_alice_address(),
|
||||
REVOKATION_OUTPUT.to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// #[wasm_bindgen_test]
|
||||
// fn test_pairing_transaction() {
|
||||
// setup();
|
||||
|
||||
// helper_switch_bob();
|
||||
|
||||
// // bob sends pairing transaction to alice
|
||||
// let result = create_pairing_transaction(helper_get_alice_address(), 1);
|
||||
// let bob_pairing_transaction = match result {
|
||||
// Ok(t) => t,
|
||||
// Err(e) => {
|
||||
// panic!("create_pairing_transaction failed with {}", e.message);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // get the tweak data to add to the transaction
|
||||
// let bob_pairing_tweak_data = helper_get_tweak_data(
|
||||
// &bob_pairing_transaction.transaction,
|
||||
// ScriptBuf::from_hex(BOB_SPK).unwrap(),
|
||||
// );
|
||||
|
||||
// // dump bob message cache
|
||||
// let bob_msg_cache = dump_message_cache().unwrap();
|
||||
// reset_message_cache().unwrap();
|
||||
|
||||
// // switch to alice
|
||||
// helper_switch_alice();
|
||||
|
||||
// // alice receives and parses that transaction
|
||||
// helper_parse_transaction(&bob_pairing_transaction.transaction, bob_pairing_tweak_data);
|
||||
|
||||
// // alice receives and parses the message
|
||||
// let bob_sent_ank_msg = AnkNetworkMsg::new(
|
||||
// sdk_common::network::AnkFlag::Cipher,
|
||||
// &bob_pairing_transaction.new_network_msg.ciphertext.unwrap(),
|
||||
// );
|
||||
// let mut pairing_msg = helper_parse_ank_msg(bob_sent_ank_msg.to_string());
|
||||
|
||||
// // Alice takes the key out of the plaintext cached message and returns it encrypted to Bob
|
||||
// let bob_key = pairing_msg.plaintext.pop().unwrap();
|
||||
// let encrypted_bob_key = encrypt_remote_key(
|
||||
// bob_key,
|
||||
// Some("d8ac223089c8f5e575a06bc0e7071a0dc1124688722ecb34ccf9b3c95e5baaa8".to_owned()),
|
||||
// Some("62f10cffb1ed127ef88826c0".to_owned()),
|
||||
// )
|
||||
// .unwrap();
|
||||
// let cipher_msg = CipherMessage::new(helper_get_alice_address(), encrypted_bob_key.clone());
|
||||
// let encrypted_payload =
|
||||
// encrypt_with_key(cipher_msg.to_string(), pairing_msg.shared_secret.unwrap()).unwrap();
|
||||
// let alice_response_msg =
|
||||
// AnkNetworkMsg::new(sdk_common::network::AnkFlag::Cipher, &encrypted_payload).to_string();
|
||||
|
||||
// // same switch back to Bob
|
||||
// // let alice_msg_cache = dump_message_cache().unwrap();
|
||||
// reset_message_cache().unwrap();
|
||||
|
||||
// helper_switch_bob();
|
||||
|
||||
// set_message_cache(bob_msg_cache).unwrap();
|
||||
|
||||
// let bob_received_msg = helper_parse_ank_msg(alice_response_msg);
|
||||
// assert!(*bob_received_msg.plaintext.get(0).unwrap() == encrypted_bob_key);
|
||||
|
||||
// // take the revokation output
|
||||
// let keypair = Keypair::from_seckey_str(
|
||||
// &Secp256k1::signing_only(),
|
||||
// &bob_received_msg.shared_secret.clone().unwrap(),
|
||||
// )
|
||||
// .unwrap();
|
||||
// let spk = ScriptBuf::new_p2tr_tweaked(keypair.x_only_public_key().0.dangerous_assume_tweaked());
|
||||
// let pairing_transaction =
|
||||
// deserialize::<Transaction>(&Vec::from_hex(&bob_pairing_transaction.transaction).unwrap())
|
||||
// .unwrap();
|
||||
|
||||
// let vout = pairing_transaction
|
||||
// .output
|
||||
// .iter()
|
||||
// .position(|o| o.script_pubkey == spk)
|
||||
// .unwrap();
|
||||
|
||||
// let revokation_outpoint =
|
||||
// OutPoint::new(pairing_transaction.txid(), vout.try_into().unwrap()).to_string();
|
||||
|
||||
// pair_device(
|
||||
// Vec::from_hex(bob_received_msg.plaintext.get(0).unwrap()).unwrap(),
|
||||
// bob_received_msg.recipient.clone().unwrap(),
|
||||
// revokation_outpoint.clone(),
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// // Now we can set a trusted channel, which is basically the pairing
|
||||
// let mut trusted_channel = TrustedChannel::new(bob_received_msg.recipient.unwrap()).unwrap();
|
||||
// trusted_channel
|
||||
// .set_revokation(revokation_outpoint, bob_received_msg.shared_secret.unwrap())
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_alice_notifies_bob() {
|
||||
setup();
|
||||
debug!("==============================================\nStarting test_alice_notifies_bob\n==============================================");
|
||||
|
||||
// Alice notifies Bob
|
||||
helper_switch_device(ALICE_START_WALLET.to_owned());
|
||||
helper_link_alice();
|
||||
|
||||
let cipher_msg = CipherMessage::new(helper_get_alice_address(), "TEST".to_owned());
|
||||
|
||||
debug!("Alice notifies Bob");
|
||||
let notification_tx =
|
||||
create_notification_transaction(helper_get_bob_address(), cipher_msg, 1).unwrap();
|
||||
|
||||
// debug!("new_network_msg: {:?}", notification_tx.new_network_msg);
|
||||
// debug!("notification_tx: {}", serde_json::to_string(¬ification_tx).unwrap());
|
||||
|
||||
let notification_tweak_data = helper_get_tweak_data(
|
||||
¬ification_tx.transaction,
|
||||
ScriptBuf::from_hex(ALICE_SPK).unwrap(),
|
||||
);
|
||||
|
||||
let ank_msg = AnkNetworkMsg::new(
|
||||
sdk_common::network::AnkFlag::Cipher,
|
||||
¬ification_tx.new_network_msg.ciphertext.unwrap(),
|
||||
);
|
||||
|
||||
debug!("Alice parse her own notification transaction");
|
||||
// Alice parse her own transaction to update her utxos
|
||||
helper_parse_transaction(
|
||||
¬ification_tx.transaction,
|
||||
notification_tweak_data.clone(),
|
||||
);
|
||||
|
||||
// debug!("Alice parsed transaction {}", notification_tx.txid);
|
||||
// debug!("alice_wallet_dump: {:?}", dump_watch_only_wallet().unwrap());
|
||||
|
||||
reset_device().unwrap();
|
||||
helper_switch_device(BOB_START_WALLET.to_owned());
|
||||
helper_link_bob();
|
||||
|
||||
debug!(
|
||||
"Bob parses notification transaction {}",
|
||||
notification_tx.txid
|
||||
);
|
||||
// bob parse the transaction and the message
|
||||
helper_parse_transaction(¬ification_tx.transaction, notification_tweak_data);
|
||||
helper_parse_ank_msg(ank_msg.to_string());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_bob_challenges_alice() {
|
||||
setup();
|
||||
debug!("==============================================\nStarting test_bob_challenges_alice\n==============================================");
|
||||
helper_switch_device(BOB_CHALLENGE_WALLET.to_owned());
|
||||
helper_link_bob();
|
||||
|
||||
set_message_cache(vec![BOB_CHALLENGE_CACHE.to_owned()]).unwrap();
|
||||
let notification_msg: CachedMessage = serde_json::from_str(BOB_CHALLENGE_CACHE).unwrap();
|
||||
|
||||
let commited_in = notification_msg.commited_in.unwrap();
|
||||
|
||||
let get_outputs_result = get_outputs().unwrap();
|
||||
|
||||
let bob_outputs: HashMap<OutPoint, OwnedOutput> = get_outputs_result.into_serde().unwrap();
|
||||
|
||||
let bob_spk = &bob_outputs.get(&commited_in).unwrap().script;
|
||||
|
||||
debug!("Bob sends a challenge transaction back to Alice");
|
||||
let confirmation_tx = create_confirmation_transaction(notification_msg.id, 1).unwrap();
|
||||
|
||||
let confirmation_tweak_data = helper_get_tweak_data(
|
||||
&confirmation_tx.transaction,
|
||||
ScriptBuf::from_hex(&bob_spk).unwrap(),
|
||||
);
|
||||
|
||||
debug!("Bob parsing its own confirmation tx");
|
||||
helper_parse_transaction(
|
||||
&confirmation_tx.transaction,
|
||||
confirmation_tweak_data.clone(),
|
||||
);
|
||||
|
||||
reset_device().unwrap();
|
||||
helper_switch_device(ALICE_CHALLENGE_WALLET.to_owned());
|
||||
helper_link_alice();
|
||||
|
||||
set_message_cache(vec![ALICE_CHALLENGE_CACHE.to_owned()]).unwrap();
|
||||
|
||||
debug!("Alice parsing confirmation tx");
|
||||
helper_parse_transaction(&confirmation_tx.transaction, confirmation_tweak_data);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_alice_answers_bob() {
|
||||
setup();
|
||||
debug!("==============================================\nStarting test_alice_answers_bob\n==============================================");
|
||||
helper_switch_device(ALICE_ANSWER_WALLET.to_owned());
|
||||
helper_link_alice();
|
||||
|
||||
set_message_cache(vec![ALICE_ANSWER_CACHE.to_owned()]).unwrap();
|
||||
let challenge_msg: CachedMessage = serde_json::from_str(ALICE_ANSWER_CACHE).unwrap();
|
||||
|
||||
debug!("Alice answers bob's challenge");
|
||||
let answer_tx = answer_confirmation_transaction(challenge_msg.id, 1).unwrap();
|
||||
|
||||
// debug!("answer_tx: {:?}", answer_tx.new_network_msg);
|
||||
let confirmed_by = challenge_msg.confirmed_by.unwrap();
|
||||
debug!("confirmed_by: {}", confirmed_by);
|
||||
|
||||
let get_outputs_result = get_outputs().unwrap();
|
||||
|
||||
let alice_outputs: HashMap<OutPoint, OwnedOutput> = get_outputs_result.into_serde().unwrap();
|
||||
|
||||
let alice_spk = &alice_outputs.get(&confirmed_by).unwrap().script;
|
||||
|
||||
let answer_tweak_data = helper_get_tweak_data(
|
||||
&answer_tx.transaction,
|
||||
ScriptBuf::from_hex(&alice_spk).unwrap(),
|
||||
);
|
||||
|
||||
debug!("Alice parsing its own answer tx");
|
||||
debug!(
|
||||
"message: {}",
|
||||
serde_json::to_string(&answer_tx.new_network_msg).unwrap()
|
||||
);
|
||||
helper_parse_transaction(&answer_tx.transaction, answer_tweak_data.clone());
|
||||
|
||||
reset_device().unwrap();
|
||||
helper_switch_device(BOB_ANSWER_WALLET.to_owned());
|
||||
helper_link_bob();
|
||||
|
||||
set_message_cache(vec![BOB_ANSWER_CACHE.to_owned()]).unwrap();
|
||||
|
||||
debug!("Bob parses answer transaction {}", answer_tx.txid);
|
||||
let answer_msg = helper_parse_transaction(&answer_tx.transaction, answer_tweak_data);
|
||||
debug!(
|
||||
"bob's message: {}",
|
||||
serde_json::to_string(&answer_msg).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
// #[wasm_bindgen_test]
|
||||
// fn test_linking_devices() {
|
||||
// setup();
|
||||
// helper_switch_bob();
|
||||
|
||||
// // bob sends pairing transaction to alice
|
||||
// let result = create_pairing_transaction(helper_get_alice_address(), 1);
|
||||
// let bob_pairing_transaction = match result {
|
||||
// Ok(t) => t,
|
||||
// Err(e) => {
|
||||
// panic!("create_pairing_transaction failed with {}", e.message);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // get the tweak data to add to the transaction
|
||||
// let bob_pairing_tweak_data = helper_get_tweak_data(
|
||||
// &bob_pairing_transaction.transaction,
|
||||
// ScriptBuf::from_hex(BOB_SPK).unwrap(),
|
||||
// );
|
||||
|
||||
// // alice receives and parses that transaction
|
||||
// helper_switch_alice();
|
||||
|
||||
// helper_parse_transaction(&bob_pairing_transaction.transaction, bob_pairing_tweak_data);
|
||||
|
||||
// // alice receives and parses the message
|
||||
// let pairing_msg =
|
||||
// helper_parse_cipher_msg(bob_pairing_transaction.new_network_msg.ciphertext.unwrap());
|
||||
|
||||
// // and sends the encrypted key back in a confirm transaction
|
||||
// let result = create_confirmation_transaction(pairing_msg.id, 1);
|
||||
// let alice_confirm_transaction = match result {
|
||||
// Ok(r) => r,
|
||||
// Err(e) => {
|
||||
// panic!("create_pairing_transaction failed with {}", e.message);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // get the tweak data to add to the transaction
|
||||
// let alice_confirm_tweak_data = helper_get_tweak_data(
|
||||
// &alice_confirm_transaction.transaction,
|
||||
// helper_get_spk(
|
||||
// bob_pairing_transaction.transaction,
|
||||
// alice_confirm_transaction
|
||||
// .new_network_msg
|
||||
// .commited_in
|
||||
// .unwrap(),
|
||||
// ),
|
||||
// );
|
||||
|
||||
// // alice parses her own transaction to update her utxoset
|
||||
// helper_parse_transaction(
|
||||
// &alice_confirm_transaction.transaction,
|
||||
// alice_confirm_tweak_data.clone(),
|
||||
// );
|
||||
|
||||
// // alice sends her own pairing transaction
|
||||
// let result = create_pairing_transaction(helper_get_bob_address(), 1);
|
||||
// let alice_pairing_transaction = match result {
|
||||
// Ok(t) => t,
|
||||
// Err(e) => {
|
||||
// panic!("create_pairing_transaction failed with {}", e.message);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // get the tweak data to add to the transaction
|
||||
// let alice_pairing_tweak_data = helper_get_tweak_data(
|
||||
// &alice_pairing_transaction.transaction,
|
||||
// ScriptBuf::from_hex(ALICE_SPK).unwrap(),
|
||||
// );
|
||||
|
||||
// helper_switch_bob();
|
||||
|
||||
// // bob receives the spend_key from alice and does the same thing
|
||||
// helper_parse_transaction(
|
||||
// &alice_pairing_transaction.transaction,
|
||||
// alice_pairing_tweak_data,
|
||||
// );
|
||||
|
||||
// let pairing_msg = helper_parse_cipher_msg(
|
||||
// alice_pairing_transaction
|
||||
// .new_network_msg
|
||||
// .ciphertext
|
||||
// .unwrap(),
|
||||
// );
|
||||
|
||||
// // let result = create_confirmation_transaction(pairing_msg.id, 1);
|
||||
// // let bob_confirm_transaction = match result {
|
||||
// // Ok(r) => r,
|
||||
// // Err(e) => {
|
||||
// // panic!("create_pairing_transaction failed with {}", e.message);
|
||||
// // }
|
||||
// // };
|
||||
|
||||
// // bob also received the confirmation transaction from alice, containing his own encrypted key
|
||||
// log::debug!("Parsing alice confirmation transaction");
|
||||
// helper_parse_transaction(
|
||||
// &alice_confirm_transaction.transaction,
|
||||
// alice_confirm_tweak_data,
|
||||
// );
|
||||
|
||||
// log::debug!("Parsing the message that goes with it");
|
||||
// let confirmation_msg = helper_parse_cipher_msg(
|
||||
// alice_confirm_transaction
|
||||
// .new_network_msg
|
||||
// .ciphertext
|
||||
// .unwrap(),
|
||||
// );
|
||||
// log::debug!("{:?}", confirmation_msg);
|
||||
|
||||
// // bob can now update its device with the linking information
|
||||
// pair_device(
|
||||
// confirmation_msg.plaintext.get(0).unwrap().to_owned().into(),
|
||||
// confirmation_msg.recipient.unwrap(),
|
||||
// OutPoint::null().to_string(),
|
||||
// )
|
||||
// .unwrap();
|
||||
// }
|
Loading…
x
Reference in New Issue
Block a user