Skip to content

Commit

Permalink
Have sign_transaction() use own enum for signing
Browse files Browse the repository at this point in the history
Previously the sign_transaction function would require a psbt json Value
previously obtained from the wallet_create_funded_psbt RPC call. That meant
the function couldnt be used without the psbt. A better way is for the
function to accept an iterator of enums which give the required information
for signing a transaction.
  • Loading branch information
chris-belcher committed Jan 16, 2022
1 parent 5ab0837 commit 99b2111
Showing 1 changed file with 71 additions and 49 deletions.
120 changes: 71 additions & 49 deletions src/wallet_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ pub enum CoreAddressLabelType {
}
const WATCH_ONLY_SWAPCOIN_LABEL: &str = "watchonly_swapcoin_label";

pub enum SignTransactionInputInfo {
SeedCoin { path: String, input_value: u64 },
SwapCoin { multisig_redeemscript: Script },
}

//swapcoins are UTXOs + metadata which are not from the deterministic wallet
//they are made in the process of a coinswap
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -1014,62 +1019,53 @@ impl Wallet {
(privkey.key, privkey.public_key(&secp))
}

fn sign_transaction(&self, spending_tx: &mut Transaction, decoded_psbt: &Value) {
pub fn sign_transaction(
&self,
spending_tx: &mut Transaction,
inputs_info: &mut dyn Iterator<Item = SignTransactionInputInfo>,
) {
let secp = Secp256k1::new();
let master_private_key = self
.master_key
.derive_priv(&secp, &DerivationPath::from_str(DERIVATION_PATH).unwrap())
.unwrap();
let tx_clone = spending_tx.clone();

for (ix, (mut input, input_info)) in spending_tx
.input
.iter_mut()
.zip(decoded_psbt["inputs"].as_array().unwrap())
.enumerate()
for (ix, (mut input, input_info)) in
spending_tx.input.iter_mut().zip(inputs_info).enumerate()
{
let bip32_info = &input_info["bip32_derivs"].as_array().unwrap();

if bip32_info.len() == 2 {
//signing multisig input
let redeemscript = Builder::from(
Vec::from_hex(&input_info["witness_script"]["hex"].as_str().unwrap()).unwrap(),
)
.into_script();

self.find_incoming_swapcoin(&redeemscript)
.unwrap()
.sign_transaction_input(ix, &tx_clone, &mut input, &redeemscript)
.unwrap();
} else {
//signing single sig input
let path = bip32_info[0]["path"].as_str().unwrap();

let privkey = master_private_key
.derive_priv(&secp, &DerivationPath::from_str(path).unwrap())
.unwrap()
.private_key;
let pubkey = privkey.public_key(&secp);
assert_eq!(pubkey.to_bytes().to_hex(), bip32_info[0]["pubkey"]);

let input_value =
convert_json_rpc_bitcoin_to_satoshis(&input_info["witness_utxo"]["amount"]);
let scriptcode = Script::new_p2pkh(&pubkey.pubkey_hash());
let sighash = SigHashCache::new(&tx_clone).signature_hash(
ix,
&scriptcode,
input_value,
SigHashType::All,
);
//TODO use low-R value signatures for privacy
//https://en.bitcoin.it/wiki/Privacy#Wallet_fingerprinting
let signature = secp.sign(
&secp256k1::Message::from_slice(&sighash[..]).unwrap(),
&privkey.key,
);
input.witness.push(signature.serialize_der().to_vec());
input.witness[0].push(SigHashType::All as u8);
input.witness.push(pubkey.to_bytes());
match input_info {
SignTransactionInputInfo::SwapCoin {
multisig_redeemscript,
} => {
self.find_incoming_swapcoin(&multisig_redeemscript)
.unwrap()
.sign_transaction_input(ix, &tx_clone, &mut input, &multisig_redeemscript)
.unwrap();
}
SignTransactionInputInfo::SeedCoin { path, input_value } => {
let privkey = master_private_key
.derive_priv(&secp, &DerivationPath::from_str(&path).unwrap())
.unwrap()
.private_key;
let pubkey = privkey.public_key(&secp);
let scriptcode = Script::new_p2pkh(&pubkey.pubkey_hash());
let sighash = SigHashCache::new(&tx_clone).signature_hash(
ix,
&scriptcode,
input_value,
SigHashType::All,
);
//TODO use low-R value signatures for privacy
//https://en.bitcoin.it/wiki/Privacy#Wallet_fingerprinting
let signature = secp.sign(
&secp256k1::Message::from_slice(&sighash[..]).unwrap(),
&privkey.key,
);
input.witness.push(signature.serialize_der().to_vec());
input.witness[0].push(SigHashType::All as u8);
input.witness.push(pubkey.to_bytes());
}
}
}
}
Expand Down Expand Up @@ -1223,7 +1219,33 @@ impl Wallet {
lock_time: 0,
version: 2,
};
self.sign_transaction(&mut spending_tx, &decoded_psbt);

let mut inputs_info = decoded_psbt["inputs"]
.as_array()
.unwrap()
.iter()
.map(|input_info| (input_info, input_info["bip32_derivs"].as_array().unwrap()))
.map(|(input_info, bip32_info)| {
if bip32_info.len() == 2 {
SignTransactionInputInfo::SwapCoin {
multisig_redeemscript: Builder::from(
Vec::from_hex(
&input_info["witness_script"]["hex"].as_str().unwrap(),
)
.unwrap(),
)
.into_script(),
}
} else {
SignTransactionInputInfo::SeedCoin {
path: bip32_info[0]["path"].as_str().unwrap().to_string(),
input_value: convert_json_rpc_bitcoin_to_satoshis(
&input_info["witness_utxo"]["amount"],
),
}
}
});
self.sign_transaction(&mut spending_tx, &mut inputs_info);

log::debug!(target: "wallet",
"txhex = {}",
Expand Down

0 comments on commit 99b2111

Please sign in to comment.