use crate::transaction::Transaction;
use core::iter::once;
use itertools::Itertools;
use kaspa_consensus_core::{
hashing::{
sighash::{calc_schnorr_signature_hash, SigHashReusedValues},
sighash_type::SIG_HASH_ALL,
},
tx::PopulatedTransaction,
};
use std::collections::BTreeMap;
pub enum Signed<'a> {
Fully(&'a Transaction),
Partially(&'a Transaction),
}
impl<'a> Signed<'a> {
pub fn unwrap(self) -> &'a Transaction {
match self {
Signed::Fully(tx) => tx,
Signed::Partially(tx) => tx,
}
}
}
#[allow(clippy::result_large_err)]
pub fn sign_with_multiple_v3<'a>(tx: &'a Transaction, privkeys: &[[u8; 32]]) -> crate::result::Result<Signed<'a>> {
let mut map = BTreeMap::new();
for privkey in privkeys {
let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, privkey).unwrap();
let schnorr_public_key = schnorr_key.public_key().x_only_public_key().0;
let script_pub_key_script = once(0x20).chain(schnorr_public_key.serialize().into_iter()).chain(once(0xac)).collect_vec();
map.insert(script_pub_key_script, schnorr_key);
}
let mut reused_values = SigHashReusedValues::new();
let mut additional_signatures_required = false;
{
let input_len = tx.inner().inputs.len();
let (cctx, utxos) = tx.tx_and_utxos()?;
let populated_transaction = PopulatedTransaction::new(&cctx, utxos);
for i in 0..input_len {
let script_pub_key = match tx.inner().inputs[i].script_public_key() {
Some(script) => script,
None => {
return Err(crate::imports::Error::Custom("expected to be called only following full UTXO population".to_string()))
}
};
let script = script_pub_key.script();
if let Some(schnorr_key) = map.get(script) {
let sig_hash = calc_schnorr_signature_hash(&populated_transaction, i, SIG_HASH_ALL, &mut reused_values);
let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice()).unwrap();
let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref();
tx.set_signature_script(i, std::iter::once(65u8).chain(sig).chain([SIG_HASH_ALL.to_u8()]).collect())?;
} else {
additional_signatures_required = true;
}
}
}
if additional_signatures_required {
Ok(Signed::Partially(tx))
} else {
Ok(Signed::Fully(tx))
}
}