Skip to content

Commit

Permalink
actor: Remove old code and add unit tests that checks SinglePlusAnyon…
Browse files Browse the repository at this point in the history
…eCanPay scenarios.
  • Loading branch information
ceyhunsen committed Sep 16, 2024
1 parent 9399bbe commit e50c648
Showing 1 changed file with 154 additions and 53 deletions.
207 changes: 154 additions & 53 deletions core/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use bitcoin::sighash::SighashCache;
use bitcoin::taproot::LeafVersion;
use bitcoin::{
hashes::Hash,
secp256k1::{ecdsa, schnorr, Keypair, Message, SecretKey, XOnlyPublicKey},
secp256k1::{schnorr, Keypair, Message, SecretKey, XOnlyPublicKey},
Address, TapSighash, TapTweakHash,
};
use bitcoin::{TapLeafHash, TapNodeHash, TapSighashType, TxOut};

#[derive(Debug, Clone)]
pub struct Actor {
pub keypair: Keypair,
secret_key: SecretKey,
_secret_key: SecretKey,
pub xonly_public_key: XOnlyPublicKey,
pub public_key: secp256k1::PublicKey,
pub address: Address,
Expand All @@ -28,7 +28,7 @@ impl Actor {

Actor {
keypair,
secret_key: keypair.secret_key(),
_secret_key: keypair.secret_key(),
xonly_public_key: xonly,
public_key: keypair.public_key(),
address,
Expand Down Expand Up @@ -58,54 +58,6 @@ impl Actor {
)
}

#[tracing::instrument(skip(self), ret(level = tracing::Level::TRACE))]
pub fn sign_ecdsa(&self, data: [u8; 32]) -> ecdsa::Signature {
utils::SECP.sign_ecdsa(
&Message::from_digest_slice(&data).expect("should be hash"),
&self.secret_key,
)
}

#[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub fn sign_taproot_script_spend_tx(
&self,
tx: &mut bitcoin::Transaction,
prevouts: &[TxOut],
spend_script: &bitcoin::Script,
input_index: usize,
) -> Result<schnorr::Signature, BridgeError> {
let mut sighash_cache = SighashCache::new(tx);
let sig_hash = sighash_cache.taproot_script_spend_signature_hash(
input_index,
&bitcoin::sighash::Prevouts::All(prevouts),
TapLeafHash::from_script(spend_script, LeafVersion::TapScript),
bitcoin::sighash::TapSighashType::Default,
)?;

Ok(self.sign(sig_hash))
}

#[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub fn sighash_taproot_script_spend(
&self,
tx: &mut TxHandler,
txin_index: usize,
script_index: usize,
) -> Result<TapSighash, BridgeError> {
let mut sighash_cache: SighashCache<&mut bitcoin::Transaction> =
SighashCache::new(&mut tx.tx);
let sig_hash = sighash_cache.taproot_script_spend_signature_hash(
txin_index,
&bitcoin::sighash::Prevouts::All(&tx.prevouts),
TapLeafHash::from_script(
&tx.scripts[txin_index][script_index],
LeafVersion::TapScript,
),
bitcoin::sighash::TapSighashType::Default,
)?;
Ok(sig_hash)
}

#[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub fn sign_taproot_script_spend_tx_new(
&self,
Expand All @@ -115,9 +67,9 @@ impl Actor {
) -> Result<schnorr::Signature, BridgeError> {
// TODO: if sighash_cache exists in the TxHandler, use it
// else create a new one and save it to the TxHandler

let mut sighash_cache: SighashCache<&mut bitcoin::Transaction> =
SighashCache::new(&mut tx.tx);

let sig_hash = sighash_cache.taproot_script_spend_signature_hash(
txin_index,
&bitcoin::sighash::Prevouts::All(&tx.prevouts),
Expand All @@ -127,6 +79,7 @@ impl Actor {
),
bitcoin::sighash::TapSighashType::Default,
)?;

Ok(self.sign(sig_hash))
}

Expand All @@ -138,6 +91,7 @@ impl Actor {
sighash_type: Option<TapSighashType>,
) -> Result<schnorr::Signature, BridgeError> {
let mut sighash_cache = SighashCache::new(&mut tx_handler.tx);

let sig_hash = sighash_cache.taproot_key_spend_signature_hash(
input_index,
&match sighash_type {
Expand All @@ -149,6 +103,7 @@ impl Actor {
},
sighash_type.unwrap_or(TapSighashType::Default),
)?;

self.sign_with_tweak(sig_hash, None)
}

Expand All @@ -160,11 +115,13 @@ impl Actor {
input_index: usize,
) -> Result<schnorr::Signature, BridgeError> {
let mut sighash_cache = SighashCache::new(tx);

let sig_hash = sighash_cache.taproot_key_spend_signature_hash(
input_index,
&bitcoin::sighash::Prevouts::All(prevouts),
bitcoin::sighash::TapSighashType::Default,
)?;

self.sign_with_tweak(sig_hash, None)
}

Expand All @@ -177,6 +134,7 @@ impl Actor {
sighash_type: Option<TapSighashType>,
) -> Result<schnorr::Signature, BridgeError> {
let mut sighash_cache = SighashCache::new(tx);

let sig_hash = sighash_cache.taproot_key_spend_signature_hash(
input_index,
&match sighash_type {
Expand All @@ -187,6 +145,7 @@ impl Actor {
},
sighash_type.unwrap_or(TapSighashType::Default),
)?;

self.sign_with_tweak(sig_hash, None)
}

Expand All @@ -199,9 +158,9 @@ impl Actor {
) -> Result<schnorr::Signature, BridgeError> {
// TODO: if sighash_cache exists in the TxHandler, use it
// else create a new one and save it to the TxHandler

let mut sighash_cache: SighashCache<&mut bitcoin::Transaction> =
SighashCache::new(&mut tx_handler.tx);

let sig_hash = sighash_cache.taproot_script_spend_signature_hash(
txin_index,
&bitcoin::sighash::Prevouts::All(&tx_handler.prevouts),
Expand All @@ -223,6 +182,7 @@ impl Actor {
) -> Result<TapSighash, BridgeError> {
let mut sighash_cache: SighashCache<&mut bitcoin::Transaction> =
SighashCache::new(&mut tx_handler.tx);

let sig_hash = sighash_cache.taproot_script_spend_signature_hash(
txin_index,
&bitcoin::sighash::Prevouts::All(&tx_handler.prevouts),
Expand All @@ -232,6 +192,7 @@ impl Actor {
),
bitcoin::sighash::TapSighashType::Default,
)?;

Ok(sig_hash)
}

Expand All @@ -242,11 +203,151 @@ impl Actor {
) -> Result<TapSighash, BridgeError> {
let mut sighash_cache: SighashCache<&mut bitcoin::Transaction> =
SighashCache::new(&mut tx.tx);

let sig_hash = sighash_cache.taproot_key_spend_signature_hash(
txin_index,
&bitcoin::sighash::Prevouts::All(&tx.prevouts),
bitcoin::sighash::TapSighashType::Default,
)?;

Ok(sig_hash)
}
}

#[cfg(test)]
mod tests {
use super::Actor;
use crate::transaction_builder::TxHandler;
use bitcoin::{
absolute::Height, transaction::Version, Amount, Network, OutPoint, Transaction, TxIn, TxOut,
};
use secp256k1::{rand, Secp256k1, SecretKey};

/// Returns a valid [`TxHandler`].
fn create_valid_mock_tx_handler(actor: &Actor) -> TxHandler {
let mut tx_handler = create_invalid_mock_tx_handler(actor);

let prev_tx: Transaction = Transaction {
version: Version::TWO,
lock_time: bitcoin::absolute::LockTime::Blocks(Height::ZERO),
input: vec![],
output: tx_handler.prevouts.clone(),
};

tx_handler.tx = Transaction {
version: prev_tx.version,
lock_time: prev_tx.lock_time,
input: vec![TxIn {
previous_output: OutPoint {
txid: prev_tx.compute_txid(),
vout: 0,
},
script_sig: actor.address.script_pubkey(),
..Default::default()
}],
output: vec![TxOut {
value: Amount::from_sat(0x1F),
script_pubkey: actor.address.script_pubkey(),
}],
};

tx_handler
}
/// Returns an invalid [`TxHandler`]. Only the tx part is invalid.
fn create_invalid_mock_tx_handler(actor: &Actor) -> TxHandler {
let prevouts = vec![TxOut {
value: Amount::from_sat(0x45),
script_pubkey: actor.address.script_pubkey(),
}];

let tx = Transaction {
version: Version::TWO,
lock_time: bitcoin::absolute::LockTime::Blocks(Height::ZERO),
input: vec![],
output: vec![TxOut {
value: Amount::from_sat(0x1F),
script_pubkey: actor.address.script_pubkey(),
}],
};

let tx_handler = TxHandler {
tx,
prevouts,
scripts: vec![],
taproot_spend_infos: vec![],
};

tx_handler
}

#[test]
fn actor_new() {
let secp = Secp256k1::new();
let sk = SecretKey::new(&mut rand::thread_rng());
let network = Network::Regtest;

let actor = Actor::new(sk, network);

assert_eq!(sk, actor._secret_key);
assert_eq!(sk.public_key(&secp), actor.public_key);
assert_eq!(sk.x_only_public_key(&secp).0, actor.xonly_public_key);
}

#[test]
fn sign_taproot_pubkey_spend() {
let sk = SecretKey::new(&mut rand::thread_rng());
let network = Network::Regtest;
let actor = Actor::new(sk, network);

// Trying to sign with an invalid transaction will result with an error.
let mut tx_handler = create_invalid_mock_tx_handler(&actor);
assert!(actor
.sign_taproot_pubkey_spend(
&mut tx_handler,
0,
Some(bitcoin::TapSighashType::SinglePlusAnyoneCanPay),
)
.is_err());

// This transaction is matching with prevouts. Therefore signing will
// be successful.
tx_handler = create_valid_mock_tx_handler(&actor);
actor
.sign_taproot_pubkey_spend(
&mut tx_handler,
0,
Some(bitcoin::TapSighashType::SinglePlusAnyoneCanPay),
)
.unwrap();
}

#[test]
fn sign_taproot_pubkey_spend_tx_with_sighash() {
let sk = SecretKey::new(&mut rand::thread_rng());
let network = Network::Regtest;
let actor = Actor::new(sk, network);

// Trying to sign with an invalid transaction will result with an error.
let mut tx_handler = create_invalid_mock_tx_handler(&actor);
assert!(actor
.sign_taproot_pubkey_spend_tx_with_sighash(
&mut tx_handler.tx,
&tx_handler.prevouts,
0,
Some(bitcoin::TapSighashType::SinglePlusAnyoneCanPay),
)
.is_err());

// This transaction is matching with prevouts. Therefore signing will
// be successful.
tx_handler = create_valid_mock_tx_handler(&actor);
actor
.sign_taproot_pubkey_spend_tx_with_sighash(
&mut tx_handler.tx,
&tx_handler.prevouts,
0,
Some(bitcoin::TapSighashType::SinglePlusAnyoneCanPay),
)
.unwrap();
}
}

0 comments on commit e50c648

Please sign in to comment.