Update faucet
This commit is contained in:
parent
0d42c289cb
commit
8fb517c107
@ -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]
|
||||||
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user