spend refactoring
This commit is contained in:
parent
ee5fcb4932
commit
94b96320d7
36
Cargo.lock
generated
36
Cargo.lock
generated
@ -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",
|
||||
|
231
src/main.rs
231
src/main.rs
@ -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());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user