Refactor silentpayments
This commit is contained in:
parent
0e88a729c3
commit
1f8da7ea61
@ -1,39 +1,16 @@
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{Error, Result};
|
||||
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sp_client::bitcoin::secp256k1::ecdh::shared_secret_point;
|
||||
use sp_client::bitcoin::{secp256k1::PublicKey, Transaction};
|
||||
use sp_client::bitcoin::{Amount, OutPoint, Txid};
|
||||
use rand::{thread_rng, Rng, RngCore};
|
||||
use sp_client::bitcoin::psbt::raw;
|
||||
use sp_client::bitcoin::{Psbt, Transaction};
|
||||
use sp_client::bitcoin::{Amount, OutPoint};
|
||||
use sp_client::bitcoin::consensus::{deserialize, serialize};
|
||||
use sp_client::silentpayments::sending::SilentPaymentAddress;
|
||||
use sp_client::spclient::{OwnedOutput, Recipient, SpClient, SpWallet};
|
||||
use tsify::Tsify;
|
||||
|
||||
use crate::crypto::AnkSharedSecret;
|
||||
|
||||
// #[derive(Debug, Serialize, Deserialize, Tsify)]
|
||||
// #[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
// pub struct ScannedTransaction(HashMap<Txid, Vec<AnkSharedSecret>>);
|
||||
|
||||
// impl ScannedTransaction {
|
||||
// pub fn new() -> Self {
|
||||
// Self(HashMap::new())
|
||||
// }
|
||||
|
||||
// pub fn get(&self) -> &HashMap<Txid, Vec<AnkSharedSecret>> {
|
||||
// &self.0
|
||||
// }
|
||||
|
||||
// pub fn get_mut(&mut self) -> &mut HashMap<Txid, Vec<AnkSharedSecret>> {
|
||||
// &mut self.0
|
||||
// }
|
||||
|
||||
// pub fn to_inner(&self) -> HashMap<Txid, Vec<AnkSharedSecret>> {
|
||||
// self.0.clone()
|
||||
// }
|
||||
// }
|
||||
use sp_client::constants;
|
||||
|
||||
pub fn create_transaction(sp_address: SilentPaymentAddress, sp_wallet: &SpWallet, fee_rate: Amount) -> Result<Transaction> {
|
||||
let available_outpoints = sp_wallet.get_outputs().to_spendable_list();
|
||||
@ -77,7 +54,7 @@ pub fn create_transaction(sp_address: SilentPaymentAddress, sp_wallet: &SpWallet
|
||||
|
||||
// This wouldn't work with many recipients in the same transaction
|
||||
// each address (or more precisely each scan public key) would have its own point
|
||||
let shared_point = shared_secret_point(&sp_address.get_scan_key(), &partial_secret);
|
||||
// let shared_point = shared_secret_point(&sp_address.get_scan_key(), &partial_secret);
|
||||
|
||||
sp_wallet
|
||||
.get_client()
|
||||
@ -92,12 +69,66 @@ pub fn create_transaction(sp_address: SilentPaymentAddress, sp_wallet: &SpWallet
|
||||
Ok(final_tx)
|
||||
}
|
||||
|
||||
pub fn create_transaction_for_address_with_shared_secret(
|
||||
sp_address: SilentPaymentAddress,
|
||||
pub fn create_transaction_spend_outpoint(
|
||||
outpoint: &OutPoint,
|
||||
sp_wallet: &SpWallet,
|
||||
message: Option<String>,
|
||||
recipient: Recipient,
|
||||
fee_rate: Amount
|
||||
) -> Result<Psbt> {
|
||||
let available_outpoints = sp_wallet.get_outputs().to_spendable_list();
|
||||
|
||||
let mut inputs: HashMap<OutPoint, OwnedOutput> = HashMap::new();
|
||||
let mut total_available = Amount::from_sat(0);
|
||||
let (must_outpoint, must_output) = available_outpoints.get_key_value(outpoint).ok_or_else(|| Error::msg("Mandatory outpoint unknown"))?;
|
||||
total_available += must_output.amount;
|
||||
inputs.insert(*must_outpoint, must_output.clone());
|
||||
|
||||
for (outpoint, output) in available_outpoints {
|
||||
if total_available > Amount::from_sat(1000) {
|
||||
break;
|
||||
}
|
||||
total_available += output.amount;
|
||||
inputs.insert(outpoint, output);
|
||||
}
|
||||
|
||||
if total_available < Amount::from_sat(1000) {
|
||||
return Err(Error::msg("Not enough available funds"));
|
||||
}
|
||||
|
||||
// create a dummy commitment
|
||||
let mut buf = [0u8;64];
|
||||
thread_rng().fill_bytes(&mut buf);
|
||||
|
||||
let mut new_psbt = sp_wallet.get_client().create_new_psbt(
|
||||
inputs,
|
||||
vec![recipient],
|
||||
Some(&buf),
|
||||
)?;
|
||||
|
||||
let change_addr = sp_wallet.get_client().sp_receiver.get_change_address();
|
||||
SpClient::set_fees(&mut new_psbt, fee_rate, change_addr)?;
|
||||
|
||||
let partial_secret = sp_wallet
|
||||
.get_client()
|
||||
.get_partial_secret_from_psbt(&new_psbt)?;
|
||||
|
||||
sp_wallet
|
||||
.get_client()
|
||||
.fill_sp_outputs(&mut new_psbt, partial_secret)?;
|
||||
let mut aux_rand = [0u8; 32];
|
||||
thread_rng().fill(&mut aux_rand);
|
||||
let mut signed = sp_wallet.get_client().sign_psbt(new_psbt, &aux_rand)?;
|
||||
SpClient::finalize_psbt(&mut signed)?;
|
||||
|
||||
Ok(signed)
|
||||
}
|
||||
|
||||
pub fn create_transaction_for_address_with_shared_secret(
|
||||
recipient: Recipient,
|
||||
sp_wallet: &SpWallet,
|
||||
message: Option<&str>,
|
||||
fee_rate: Amount,
|
||||
) -> Result<(Transaction, AnkSharedSecret)> {
|
||||
) -> Result<String> {
|
||||
let available_outpoints = sp_wallet.get_outputs().to_spendable_list();
|
||||
|
||||
// Here we need to add more heuristics about which outpoint we spend
|
||||
@ -118,16 +149,10 @@ pub fn create_transaction_for_address_with_shared_secret(
|
||||
return Err(Error::msg("Not enough available funds"));
|
||||
}
|
||||
|
||||
let recipient = Recipient {
|
||||
address: sp_address.into(),
|
||||
amount: Amount::from_sat(1000),
|
||||
nb_outputs: 1,
|
||||
};
|
||||
|
||||
let mut new_psbt = sp_wallet.get_client().create_new_psbt(
|
||||
inputs,
|
||||
vec![recipient],
|
||||
message.as_ref().map(|m| m.as_bytes()),
|
||||
message.map(|m| m.as_bytes()),
|
||||
)?;
|
||||
|
||||
let change_addr = sp_wallet.get_client().sp_receiver.get_change_address();
|
||||
@ -137,10 +162,6 @@ pub fn create_transaction_for_address_with_shared_secret(
|
||||
.get_client()
|
||||
.get_partial_secret_from_psbt(&new_psbt)?;
|
||||
|
||||
// This wouldn't work with many recipients in the same transaction
|
||||
// each address (or more precisely each scan public key) would have its own point
|
||||
let shared_point = shared_secret_point(&sp_address.get_scan_key(), &partial_secret);
|
||||
|
||||
sp_wallet
|
||||
.get_client()
|
||||
.fill_sp_outputs(&mut new_psbt, partial_secret)?;
|
||||
@ -149,30 +170,30 @@ pub fn create_transaction_for_address_with_shared_secret(
|
||||
let mut signed = sp_wallet.get_client().sign_psbt(new_psbt, &aux_rand)?;
|
||||
SpClient::finalize_psbt(&mut signed)?;
|
||||
|
||||
let final_tx = signed.extract_tx()?;
|
||||
|
||||
Ok((final_tx, AnkSharedSecret::new(shared_point, true)))
|
||||
Ok(signed.to_string())
|
||||
}
|
||||
|
||||
pub fn get_shared_point_from_transaction(tx: &Transaction, sp_wallet: &SpWallet, tweak_data: PublicKey) -> Result<[u8;64]> {
|
||||
let b_scan = sp_wallet.get_client().get_scan_key();
|
||||
let test = sp_client::bitcoin::secp256k1::ecdh::shared_secret_point(&tweak_data, &b_scan);
|
||||
let ecdh_shared= sp_client::silentpayments::utils::receiving::calculate_shared_secret(tweak_data, b_scan)?;
|
||||
let uncompressed = ecdh_shared.serialize_uncompressed();
|
||||
assert!(test == uncompressed[1..]);
|
||||
Ok(test)
|
||||
}
|
||||
pub fn map_outputs_to_sp_address(psbt_str: &str) -> Result<HashMap<String, Vec<usize>>> {
|
||||
let psbt = Psbt::from_str(&psbt_str)?;
|
||||
|
||||
pub fn check_transaction(
|
||||
tx: &Transaction,
|
||||
sp_wallet: &mut SpWallet,
|
||||
blockheight: u32,
|
||||
tweak_data: PublicKey,
|
||||
) -> Result<String> {
|
||||
let txid = tx.txid().to_string();
|
||||
if sp_wallet.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 {
|
||||
return Ok(txid);
|
||||
let mut res: HashMap<String, Vec<usize>> = HashMap::new();
|
||||
for (i, output) in psbt.outputs.iter().enumerate() {
|
||||
if let Some(value) = output.proprietary.get(&raw::ProprietaryKey {
|
||||
prefix: constants::PSBT_SP_PREFIX.as_bytes().to_vec(),
|
||||
subtype: constants::PSBT_SP_SUBTYPE,
|
||||
key: constants::PSBT_SP_ADDRESS_KEY.as_bytes().to_vec(),
|
||||
}) {
|
||||
let sp_address = SilentPaymentAddress::try_from(deserialize::<String>(value)?)?;
|
||||
if let Some(vouts) = res.get_mut::<String>(&sp_address.into()) {
|
||||
vouts.push(i);
|
||||
} else {
|
||||
res.insert(sp_address.into(), vec![i]);
|
||||
}
|
||||
} else {
|
||||
// Not a sp output
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Error::msg("No new outputs found"));
|
||||
Ok(res)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user