spend refactoring

This commit is contained in:
Sosthene00 2024-04-18 00:34:10 +02:00
parent ee5fcb4932
commit 94b96320d7
2 changed files with 88 additions and 179 deletions

36
Cargo.lock generated
View File

@ -939,9 +939,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.80"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
"unicode-ident",
]
@ -1097,7 +1097,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
"sp_backend",
"sp_client 0.1.0 (git+https://github.com/Sosthene00/sp-backend?branch=sp_client)",
"tsify",
"uuid",
"wasm-bindgen",
@ -1118,7 +1118,7 @@ dependencies = [
"serde",
"serde_json",
"serde_with",
"sp_backend",
"sp_client 0.1.0 (git+https://github.com/Sosthene00/sp-client?branch=sp_client)",
"tokio",
"tokio-stream",
"tokio-tungstenite",
@ -1168,18 +1168,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.197"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
@ -1299,9 +1299,21 @@ dependencies = [
]
[[package]]
name = "sp_backend"
name = "sp_client"
version = "0.1.0"
source = "git+https://github.com/Sosthene00/sp-backend?branch=sp_client#d09c92ac7718c34c9ce3ff55298140ebda0b2e1a"
source = "git+https://github.com/Sosthene00/sp-backend?branch=sp_client#4eaa51ed306fc939f487c0e14208bf36da8c8a26"
dependencies = [
"anyhow",
"bitcoin 0.31.2",
"serde",
"serde_json",
"silentpayments",
]
[[package]]
name = "sp_client"
version = "0.1.0"
source = "git+https://github.com/Sosthene00/sp-client?branch=sp_client#4eaa51ed306fc939f487c0e14208bf36da8c8a26"
dependencies = [
"anyhow",
"bitcoin 0.31.2",
@ -1330,9 +1342,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.59"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
"proc-macro2",
"quote",

View File

@ -1,5 +1,11 @@
use std::{
any::Any, collections::HashMap, env, fmt::Debug, net::SocketAddr, str::FromStr, sync::{Arc, Mutex, MutexGuard}
any::Any,
collections::HashMap,
env,
fmt::Debug,
net::SocketAddr,
str::FromStr,
sync::{Arc, Mutex, MutexGuard},
};
use bitcoincore_rpc::json::{self as bitcoin_json};
@ -20,8 +26,8 @@ use sp_client::bitcoin::{
use sp_client::{
bitcoin::secp256k1::{
rand::{thread_rng, Rng},
Error as Secp256k1Error, Keypair, Message as Secp256k1Message, PublicKey, Scalar,
Secp256k1, SecretKey, ThirtyTwoByteHash,
Keypair, Message as Secp256k1Message, PublicKey,
Secp256k1, ThirtyTwoByteHash,
},
spclient::SpWallet,
};
@ -86,6 +92,7 @@ impl SilentPaymentWallet {
enum BroadcastType {
Sender(SocketAddr),
#[allow(dead_code)]
ExcludeSender(SocketAddr),
#[allow(dead_code)]
ToAll,
@ -169,55 +176,6 @@ fn spend_from_core(
}
}
fn find_owned_outputs(
tx: &Transaction,
ours: HashMap<Option<Label>, HashMap<XOnlyPublicKey, Scalar>>,
) -> Result<HashMap<OutPoint, OwnedOutput>> {
let mut res: HashMap<OutPoint, OwnedOutput> = HashMap::new();
for (label, map) in ours {
res.extend(tx.output.iter().enumerate().filter_map(
|(i, o)| match XOnlyPublicKey::from_slice(&o.script_pubkey.as_bytes()[2..]) {
Ok(key) => {
if let Some(scalar) = map.get(&key) {
match SecretKey::from_slice(&scalar.to_be_bytes()) {
Ok(tweak) => {
let outpoint = OutPoint {
txid: tx.txid(),
vout: i as u32,
};
let label_str: Option<String>;
if let Some(l) = &label {
label_str =
Some(l.as_inner().to_be_bytes().to_lower_hex_string());
} else {
label_str = None;
}
return Some((
outpoint,
OwnedOutput {
blockheight: 0,
tweak: tweak.secret_bytes().to_lower_hex_string(),
amount: o.value,
script: o.script_pubkey.as_bytes().to_lower_hex_string(),
label: label_str,
spend_status: OutputSpendStatus::Unspent,
},
));
}
Err(_) => {
return None;
}
}
}
None
}
Err(_) => None,
},
));
}
Ok(res)
}
fn faucet_send(
sp_address: SilentPaymentAddress,
sp_wallet: Arc<SilentPaymentWallet>,
@ -225,7 +183,6 @@ fn faucet_send(
) -> Result<Transaction> {
let mut first_tx: Option<Transaction> = None;
let final_tx: Transaction;
let mut new_outpoints: HashMap<OutPoint, OwnedOutput>;
// do we have a sp output available ?
let available_outpoints = sp_wallet.get_wallet()?.get_outputs().to_spendable_list();
@ -270,7 +227,8 @@ fn faucet_send(
.create_new_psbt(inputs.clone(), vec![recipient], None)?;
log::debug!("Created psbt: {}", new_psbt);
SpClient::set_fees(&mut new_psbt, fee_estimate, sp_address.into())?;
wallet.get_client().fill_sp_outputs(&mut new_psbt)?;
let partial_secret = wallet.get_client().get_partial_secret_from_psbt(&new_psbt)?;
wallet.get_client().fill_sp_outputs(&mut new_psbt, partial_secret)?;
log::debug!("Definitive psbt: {}", new_psbt);
let mut aux_rand = [0u8; 32];
thread_rng().fill(&mut aux_rand);
@ -279,46 +237,6 @@ fn faucet_send(
SpClient::finalize_psbt(&mut signed)?;
final_tx = signed.extract_tx()?;
// take all we need to register the new sp output
let outpoints: Vec<(String, u32)> = final_tx
.input
.iter()
.map(|i| (i.previous_output.txid.to_string(), i.previous_output.vout))
.collect();
let our_sp_address: SilentPaymentAddress = wallet
.get_client()
.sp_receiver
.get_receiving_address()
.try_into()?;
let our_spend_pubkey = our_sp_address.get_spend_key();
let secp = Secp256k1::verification_only();
let input_pubkeys: Result<Vec<PublicKey>, Secp256k1Error> = inputs
.iter()
.map(|(_, o)| {
let tweak = SecretKey::from_str(&o.tweak)?;
our_spend_pubkey.mul_tweak(&secp, &tweak.into())
})
.collect();
let input_pubkeys = input_pubkeys?;
let input_pubkeys: Vec<&PublicKey> = input_pubkeys.iter().collect();
let partial_tweak = calculate_tweak_data(&input_pubkeys, &outpoints)?;
let ecdh_shared_secret =
calculate_shared_secret(partial_tweak, wallet.get_client().get_scan_key())?;
let outputs_to_check: Result<Vec<XOnlyPublicKey>, Secp256k1Error> = final_tx
.output
.iter()
.map(|o| XOnlyPublicKey::from_slice(&o.script_pubkey.as_bytes()[2..]))
.collect();
let ours = wallet
.get_client()
.sp_receiver
.scan_transaction(&ecdh_shared_secret, outputs_to_check?)?;
new_outpoints = find_owned_outputs(&final_tx, ours)?;
} else {
// let's try to spend directly from the mining address
let secp = Secp256k1::signing_only();
@ -329,7 +247,7 @@ fn faucet_send(
spend_from_core(keypair.x_only_public_key().0, shared_daemon.clone())?;
// check that the first output of the transaction pays to the key we just created
assert!(
debug_assert!(
core_tx.output[0].script_pubkey
== ScriptBuf::new_p2tr_tweaked(
keypair.x_only_public_key().0.dangerous_assume_tweaked()
@ -411,40 +329,12 @@ fn faucet_send(
faucet_tx.input[0].witness.push(final_sig.to_vec());
// take all we need to register the new sp output
let outpoints: Vec<(String, u32)> = vec![(core_tx.txid().to_string(), 0)];
first_tx = Some(core_tx);
let client = sp_wallet.get_wallet()?;
let input_pubkey = &keypair.public_key();
let input_pub_keys: Vec<&PublicKey> = vec![input_pubkey];
let partial_tweak = calculate_tweak_data(&input_pub_keys, &outpoints)?;
let ecdh_shared_secret =
calculate_shared_secret(partial_tweak, client.get_client().get_scan_key())?;
let p2tr_outs = vec![ext_output_key, change_output_key];
let ours = client
.get_client()
.sp_receiver
.scan_transaction(&ecdh_shared_secret, p2tr_outs)?;
final_tx = faucet_tx;
new_outpoints = find_owned_outputs(&final_tx, ours)?;
}
if let Ok(daemon) = shared_daemon.lock() {
// get current blockheight
let blkheight: u32 = daemon.get_current_height()?.try_into()?;
// update the new outpoints
for o in new_outpoints.iter_mut() {
o.1.blockheight = blkheight;
}
// broadcast one or two transactions
if first_tx.is_some() {
daemon.broadcast(&first_tx.unwrap())?;
@ -454,17 +344,6 @@ fn faucet_send(
return Err(Error::msg("Failed to lock daemon"));
}
// update our sp_client with the change output(s)
sp_wallet
.get_wallet()?
.get_mut_outputs()
.extend_from(new_outpoints);
debug!("{:?}", sp_wallet);
// save to disk
sp_wallet.save()?;
Ok(final_tx)
}
@ -476,9 +355,33 @@ fn handle_faucet_request(
if let Ok(sp_address) = SilentPaymentAddress::try_from(msg) {
debug!("Sending bootstrap coins to {}", sp_address);
// send bootstrap coins to this sp_address
let tx = faucet_send(sp_address, sp_wallet, shared_daemon.clone())?;
let partial_tweak =
compute_partial_tweak_to_transaction(tx.clone(), shared_daemon.clone())?;
let tx = faucet_send(sp_address, sp_wallet.clone(), shared_daemon.clone())?;
// get the tweak
let partial_tweak = compute_partial_tweak_to_transaction(&tx, shared_daemon.clone())?;
debug!("Got the tweak");
// get current blockheight
let blkheight: u32 = shared_daemon
.lock_anyhow()?
.get_current_height()?
.try_into()?;
// update our sp_client with the change output(s)
sp_wallet.get_wallet()?.update_wallet_with_transaction(
&tx,
blkheight,
partial_tweak,
)?;
debug!("{:?}", sp_wallet.get_wallet()?.get_outputs());
debug!("updated the wallet");
// save to disk
sp_wallet.save()?;
debug!("saved the wallet");
Ok(NewTxMessage::new(
serialize(&tx).to_lower_hex_string(),
Some(partial_tweak.to_string()),
@ -594,19 +497,18 @@ async fn handle_connection(
}
fn compute_partial_tweak_to_transaction(
tx: Transaction,
tx: &Transaction,
daemon: Arc<Mutex<Daemon>>,
) -> Result<PublicKey> {
let mut outpoints: Vec<(String, u32)> = Vec::with_capacity(tx.input.len());
let mut pubkeys: Vec<PublicKey> = Vec::with_capacity(tx.input.len());
for input in tx.input {
for input in tx.input.iter() {
outpoints.push((
input.previous_output.txid.to_string(),
input.previous_output.vout,
));
let prev_tx = daemon
.lock()
.map_err(|e| Error::msg(format!("Failed to lock the daemon: {}", e)))?
.lock_anyhow()?
.get_transaction(&input.previous_output.txid, None)
.map_err(|e| Error::msg(format!("Failed to find previous transaction: {}", e)))?;
@ -642,7 +544,7 @@ fn create_new_tx_message(transaction: Vec<u8>, daemon: Arc<Mutex<Daemon>>) -> Re
return Err(Error::msg("Can't process coinbase transaction"));
}
let partial_tweak = compute_partial_tweak_to_transaction(tx, daemon)?;
let partial_tweak = compute_partial_tweak_to_transaction(&tx, daemon)?;
Ok(NewTxMessage::new(
transaction.to_lower_hex_string(),
Some(partial_tweak.to_string()),
@ -666,35 +568,30 @@ async fn handle_zmq(peers: PeerMap, shared_daemon: SharedDaemon) {
};
debug!("Received a message");
let payload: String =
if let (Some(topic), Some(data)) = (core_msg.get(0), core_msg.get(1)) {
match std::str::from_utf8(&topic) {
Ok("rawtx") => {
match create_new_tx_message(data.to_vec(), shared_daemon.clone()) {
Ok(m) => serde_json::to_string(&m).expect("This shouldn't fail"),
Err(e) => {
error!("{}", e);
continue;
}
}
}
Ok("hashblock") => todo!(),
_ => {
error!("Unexpected message in zmq");
let payload: String = if let (Some(topic), Some(data)) = (core_msg.get(0), core_msg.get(1))
{
match std::str::from_utf8(&topic) {
Ok("rawtx") => match create_new_tx_message(data.to_vec(), shared_daemon.clone()) {
Ok(m) => serde_json::to_string(&m).expect("This shouldn't fail"),
Err(e) => {
error!("{}", e);
continue;
}
},
Ok("hashblock") => todo!(),
_ => {
error!("Unexpected message in zmq");
continue;
}
} else {
error!("Empty message");
continue;
};
}
} else {
error!("Empty message");
continue;
};
if let Err(e) = broadcast_message(
peers.clone(),
AnkFlag::NewTx,
payload,
BroadcastType::ToAll,
) {
if let Err(e) =
broadcast_message(peers.clone(), AnkFlag::NewTx, payload, BroadcastType::ToAll)
{
log::error!("{}", e.to_string());
}
}