Update faucet

This commit is contained in:
Sosthene00 2024-04-17 08:58:28 +02:00
parent 0d42c289cb
commit 8fb517c107
4 changed files with 103 additions and 123 deletions

View File

@ -3,15 +3,18 @@ use std::collections::HashMap;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Mutex, OnceLock, PoisonError}; use std::sync::{Mutex, OnceLock, PoisonError};
use log::debug;
use rand::Rng; use rand::Rng;
use anyhow::Error as AnyhowError; use anyhow::Error as AnyhowError;
use sdk_common::crypto::AnkSharedSecret;
use serde_json::Error as SerdeJsonError; use serde_json::Error as SerdeJsonError;
use shamir::SecretData; use shamir::SecretData;
use sp_backend::bitcoin::consensus::deserialize; use sp_backend::bitcoin::consensus::{deserialize, serialize};
use sp_backend::bitcoin::hex::{FromHex, HexToBytesError}; use sp_backend::bitcoin::hex::{parse, DisplayHex, FromHex, HexToBytesError};
use sp_backend::bitcoin::secp256k1::ecdh::SharedSecret;
use sp_backend::bitcoin::secp256k1::{PublicKey, SecretKey}; use sp_backend::bitcoin::secp256k1::{PublicKey, SecretKey};
use sp_backend::bitcoin::{Transaction, Txid}; use sp_backend::bitcoin::{OutPoint, Transaction, Txid};
use sp_backend::silentpayments::Error as SpError; use sp_backend::silentpayments::Error as SpError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -20,12 +23,13 @@ use tsify::Tsify;
use wasm_bindgen::convert::FromWasmAbi; use wasm_bindgen::convert::FromWasmAbi;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use sp_backend::spclient::{derive_keys_from_seed, OutputList, SpClient}; use sdk_common::network::{AnkFlag, AnkNetworkMsg, NewTxMessage};
use sp_backend::spclient::{derive_keys_from_seed, OutputList, OwnedOutput, SpClient};
use sp_backend::spclient::{SpWallet, SpendKey}; use sp_backend::spclient::{SpWallet, SpendKey};
use crate::images; use crate::images;
use crate::network::{BitcoinNetworkMsg, BitcoinTopic, AnkNetworkMsg, AnkTopic}; use crate::silentpayments::{check_transaction, create_transaction_for_address};
use crate::silentpayments::check_transaction;
use crate::user::{lock_connected_users, User, UserWallets, CONNECTED_USERS}; use crate::user::{lock_connected_users, User, UserWallets, CONNECTED_USERS};
use crate::process::Process; use crate::process::Process;
@ -264,7 +268,7 @@ impl shamir_shares {
} }
#[derive(Debug, Tsify, Serialize, Deserialize)] #[derive(Debug, Tsify, Serialize, Deserialize)]
#[tsify(from_wasm_abi)] #[tsify(from_wasm_abi, into_wasm_abi)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub struct outputs_list(Vec<OutputList>); pub struct outputs_list(Vec<OutputList>);
@ -296,30 +300,58 @@ pub fn login_user(
#[wasm_bindgen] #[wasm_bindgen]
pub fn check_transaction_for_silent_payments( pub fn check_transaction_for_silent_payments(
tx_hex: String, tx_hex: String,
blockheight: u32,
tweak_data_hex: String, tweak_data_hex: String,
) -> ApiResult<()> { ) -> ApiResult<String> {
let tx = deserialize::<Transaction>(&Vec::from_hex(&tx_hex)?)?; let tx = deserialize::<Transaction>(&Vec::from_hex(&tx_hex)?)?;
let tweak_data = PublicKey::from_str(&tweak_data_hex)?; let tweak_data = PublicKey::from_str(&tweak_data_hex)?;
check_transaction(tx, tweak_data); let updated_user = check_transaction(&tx, blockheight, tweak_data)?;
Ok(()) Ok(updated_user)
}
#[derive(Tsify, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct parseNetworkMsgReturn {
topic: String,
message: String,
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn parse_bitcoin_network_msg(msg: Vec<u8>) -> ApiResult<()> { pub fn parse_network_msg(raw: String) -> ApiResult<parseNetworkMsgReturn> {
let parsed_msg = BitcoinNetworkMsg::new(&msg)?; if let Ok(ank_msg) = serde_json::from_str::<AnkNetworkMsg>(&raw) {
match ank_msg.flag {
match parsed_msg.topic { AnkFlag::NewTx => {
BitcoinTopic::RawTx => { let tx_message = serde_json::from_str::<NewTxMessage>(&ank_msg.content)?;
let tx = deserialize::<Transaction>(parsed_msg.data)?; let tx = deserialize::<Transaction>(&Vec::from_hex(&tx_message.transaction)?)?;
let tweak_data = PublicKey::from_slice(parsed_msg.addon)?; if tx_message.tweak_data.is_none() {
check_transaction(tx, tweak_data); return Err(ApiError {
message: "Missing tweak_data".to_owned(),
});
}
let partial_tweak = PublicKey::from_str(&tx_message.tweak_data.unwrap())?;
let txid = check_transaction(&tx, 0, partial_tweak)?;
return Ok(parseNetworkMsgReturn {
topic: AnkFlag::NewTx.as_str().to_owned(),
message: txid,
});
}
AnkFlag::Faucet => unimplemented!(),
AnkFlag::Error => {
return Ok(parseNetworkMsgReturn {
topic: AnkFlag::Error.as_str().to_owned(),
message: ank_msg.content.to_owned(),
})
}
_ => unimplemented!(),
} }
BitcoinTopic::RawBlock => (), } else {
Err(ApiError {
message: format!("Can't parse message as a valid 4nk message: {}", raw),
})
} }
Ok(())
} }
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -18,59 +18,33 @@ type FoundOutputs = HashMap<Option<Label>, HashMap<XOnlyPublicKey, Scalar>>;
pub fn check_transaction(tx: Transaction, tweak_data: PublicKey) -> Result<FoundOutputs> { pub fn check_transaction(tx: Transaction, tweak_data: PublicKey) -> Result<FoundOutputs> {
let connected_users = lock_connected_users()?; let connected_users = lock_connected_users()?;
let pubkeys_to_check: HashMap<XOnlyPublicKey, u32> = (0u32..) pub fn check_transaction(
.zip(tx.output) tx: &Transaction,
.filter_map(|(i, o)| { blockheight: u32,
if o.script_pubkey.is_p2tr() { tweak_data: PublicKey,
let xonly = XOnlyPublicKey::from_slice(&o.script_pubkey.as_bytes()[2..]) ) -> Result<String> {
.expect("Transaction is invalid"); let connected_users = lock_connected_users()?;
Some((xonly, i))
} else {
None
}
})
.collect();
let txid = tx.txid().to_string();
// Check the transaction for all connected users // Check the transaction for all connected users
for (pre_id, keys) in connected_users.clone() { for (pre_id, keys) in connected_users.clone() {
let recover = keys.recover; let mut recover = keys.recover;
let shared_secret = if recover.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 {
calculate_shared_secret(tweak_data, recover.get_client().get_scan_key())?; return Ok(txid);
let res = recover
.get_client()
.sp_receiver
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
if res.len() > 0 {
return Ok(res);
} }
if let Some(main) = keys.main { if let Some(mut main) = keys.main {
let shared_secret = if main.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 {
calculate_shared_secret(tweak_data, main.get_client().get_scan_key())?; return Ok(txid);
let res = main
.get_client()
.sp_receiver
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
if res.len() > 0 {
return Ok(res);
} }
} }
if let Some(revoke) = keys.revoke { if let Some(mut revoke) = keys.revoke {
let shared_secret = if revoke.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 {
calculate_shared_secret(tweak_data, revoke.get_client().get_scan_key())?; return Ok(txid);
let res = revoke
.get_client()
.sp_receiver
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
if res.len() > 0 {
return Ok(res);
} }
} }
} }
Ok(HashMap::new()) return Err(Error::msg("No new outputs found"));
} }

View File

@ -177,18 +177,6 @@ class Services {
return imageBytes; return imageBytes;
} }
public async parseBitcoinMessage(raw: Blob): Promise<string | null> {
try {
const buffer = await raw.arrayBuffer();
const uint8Array = new Uint8Array(buffer);
const msg: string = this.sdkClient.parse_bitcoin_network_msg(uint8Array);
return msg;
} catch (error) {
console.error("Error processing the blob:", error);
return null;
}
}
public async displayRevoke(): Promise<void> { public async displayRevoke(): Promise<void> {
const services = await Services.getInstance(); const services = await Services.getInstance();
await services.revokeInjectHtml(); await services.revokeInjectHtml();
@ -211,30 +199,15 @@ class Services {
services.attachSubmitListener("form4nk", services.updateAnId); services.attachSubmitListener("form4nk", services.updateAnId);
} }
public async parse4nkMessage(raw: string): Promise<string | null> { public async parseNetworkMessage(raw: string): Promise<parseNetworkMsgReturn | null> {
const msg: string = this.sdkClient.parse_4nk_msg(raw); const services = await Services.getInstance();
try {
const msg: parseNetworkMsgReturn = services.sdkClient.parse_network_msg(raw);
return msg; return msg;
} } catch (error) {
console.error(error);
public injectUpdateAnIdHtml(bodyToInject: string, styleToInject: string, scriptToInject: string) { return null;
console.log("JS html : "+bodyToInject);
const body = document.getElementsByTagName('body')[0];
if (!body) {
console.error("No body tag");
return;
} }
body.innerHTML = styleToInject + bodyToInject;
const script = document.createElement("script");
script.innerHTML = scriptToInject;
document.body.appendChild(script);
script.onload = () => {
console.log('Script loaded successfuly');
};
script.onerror = () => {
console.log('Error loading script');
};
} }
public async updateAnId(event: Event): Promise<void> { public async updateAnId(event: Event): Promise<void> {
@ -288,11 +261,13 @@ class Services {
return []; return [];
} }
} }
public async checkTransaction(tx: string, tweak_data: string, blkheight: number): Promise<string | null> {
public async checkTransaction(tx: string): Promise<string | null> {
const services = await Services.getInstance(); const services = await Services.getInstance();
try { try {
return services.sdkClient.check_network_transaction(tx); const updated_user: string = services.sdkClient.check_transaction_for_silent_payments(tx, blkheight, tweak_data);
await services.updateOwnedOutputsForUser(updated_user);
return updated_user;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null; return null;
@ -647,7 +622,11 @@ class Services {
return null; return null;
} }
try { try {
connection.sendMessage('faucet'+spaddress); const flag: AnkFlag = "Faucet";
const faucetMsg: FaucetMessage = {
'sp_address': spaddress
}
connection.sendMessage(flag, JSON.stringify(faucetMsg));
} catch (error) { } catch (error) {
console.error("Failed to obtain tokens with relay ", connection.getUrl()); console.error("Failed to obtain tokens with relay ", connection.getUrl());
return null; return null;

View File

@ -1,5 +1,5 @@
import Services from "./services"; import Services from "./services";
// import * as mempool from "./mempool"; import { AnkFlag, AnkNetworkMsg, parseNetworkMsgReturn } from "../dist/pkg/sdk_client";
class WebSocketClient { class WebSocketClient {
private ws: WebSocket; private ws: WebSocket;
@ -25,28 +25,18 @@ class WebSocketClient {
console.log(msgData); console.log(msgData);
(async () => { (async () => {
if (msgData instanceof Blob) { if (typeof(msgData) === 'string') {
// bitcoin network msg is just bytes
let res = await services.parseBitcoinMessage(msgData);
if (res) {
let ours = await services.checkTransaction(res);
if (ours) {
console.log("Found our utxo in "+res);
} else {
console.log("No utxo found in tx "+res);
}
} else {
console.error("Faile to parse a bitcoin network msg");
}
} else if (typeof(msgData) === 'string') {
// json strings are 4nk message
console.log("Received text message: "+msgData); console.log("Received text message: "+msgData);
let res = await services.parse4nkMessage(msgData); let res = await services.parseNetworkMessage(msgData);
if (res) { if (res) {
console.debug(res); if (res.topic === 'new_tx') {
// we received a tx
window.alert(`New tx\n${res.message}`);
await services.updateOwnedOutputsForUser(res.message);
}
} }
} else { } else {
console.error("Received an unknown message"); console.error("Received an invalid message");
} }
})(); })();
}); });
@ -63,9 +53,14 @@ class WebSocketClient {
} }
// Method to send messages // Method to send messages
public sendMessage(message: string): void { public sendMessage(flag: AnkFlag, message: string): void {
if (this.ws.readyState === WebSocket.OPEN) { if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(message); const networkMessage: AnkNetworkMsg = {
'flag': flag,
'content': message
}
// console.debug("Sending message:", JSON.stringify(networkMessage));
this.ws.send(JSON.stringify(networkMessage));
} else { } else {
console.error('WebSocket is not open. ReadyState:', this.ws.readyState); console.error('WebSocket is not open. ReadyState:', this.ws.readyState);
this.messageQueue.push(message); this.messageQueue.push(message);