diff --git a/Cargo.toml b/Cargo.toml index b5a24101..a6173bda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,10 @@ bitvec = { version = "0.22.3" } fixed-hash = { version = "0.7.0", default-features = false } hex = "0.4.3" inet2_addr = { version = "0.5.0", default-features = false, features = ["tor", "strict_encoding"] } -lightning_encoding = "=0.5.13" +lightning_encoding = "=0.5.0-beta.3" serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } -strict_encoding = "1.7.9" -strict_encoding_derive = "1.7.6" +strict_encoding = "1.7.4" +strict_encoding_derive = "1.7.4" thiserror = "1.0.24" tiny-keccak = { version = "2", features = ["keccak"] } @@ -47,23 +47,23 @@ tiny-keccak = { version = "2", features = ["keccak"] } bincode = { version = "1.3.3", optional = true } curve25519-dalek = "3.0.2" -ecdsa_fun = { version = "0.7", default-features = false, features = ["all"], optional = true } +ecdsa_fun = { version = "0.6", default-features = false, features = ["all"], optional = true } rand = { version = "0.8.4", optional = true } rand_alt = { package = "rand", version = "0.7.3", features = ["std"] } rand_chacha = { version = "0.3.1", optional = true } -secp256kfun = { version = "0.7", default-features = false, features = ["std", "serde", "libsecp_compat"], optional = true } +secp256kfun = { version = "0.6", default-features = false, features = ["std", "serde", "libsecp_compat"], optional = true } sha2 = { version = "0.9", optional = true } sha3 = "0.10.1" # blockchain specific -bitcoin = "0.28.0-rc.1" +bitcoin = "0.27" monero = { version = "0.16" } [dev-dependencies] -bitcoincore-rpc = { git = "https://github.com/romanz/rust-bitcoincore-rpc", rev = "cf505ae02ecd19eaf20465ec09910e724a4ede7e" } +bitcoincore-rpc = "0.14" lazy_static = "1.4" rand_core = { version = "^0.6.3", features = ["getrandom"] } -secp256k1 = { version = "0.21", features = ["rand-std"] } +secp256k1 = { version = "0.20", features = ["rand-std"] } serde_yaml = "0.8" [package.metadata.docs.rs] diff --git a/src/bitcoin/fee.rs b/src/bitcoin/fee.rs index 62687a38..48ec11d8 100644 --- a/src/bitcoin/fee.rs +++ b/src/bitcoin/fee.rs @@ -126,7 +126,7 @@ impl Fee for Bitcoin { strategy: &FeeStrategy, politic: FeePriority, ) -> Result { - if tx.unsigned_tx.output.len() != 1 { + if tx.global.unsigned_tx.output.len() != 1 { return Err(FeeStrategyError::new( transaction::Error::MultiUTXOUnsuported, )); @@ -137,7 +137,7 @@ impl Fee for Bitcoin { // FIXME This does not account for witnesses // currently the fees are wrong // Get the transaction weight - let weight = tx.unsigned_tx.get_weight() as u64; + let weight = tx.global.unsigned_tx.get_weight() as u64; // Compute the fee amount to set in total let fee_amount = match strategy { @@ -150,7 +150,7 @@ impl Fee for Bitcoin { .ok_or(FeeStrategyError::AmountOfFeeTooHigh)?; // Apply the fee on the first output - tx.unsigned_tx.output[0].value = input_sum + tx.global.unsigned_tx.output[0].value = input_sum .checked_sub(fee_amount) .ok_or(FeeStrategyError::NotEnoughAssets)? .as_sat(); @@ -164,18 +164,18 @@ impl Fee for Bitcoin { tx: &PartiallySignedTransaction, strategy: &FeeStrategy, ) -> Result { - if tx.unsigned_tx.output.len() != 1 { + if tx.global.unsigned_tx.output.len() != 1 { return Err(FeeStrategyError::new( transaction::Error::MultiUTXOUnsuported, )); } let input_sum = get_available_input_sat(tx)?.as_sat(); - let output_sum = tx.unsigned_tx.output[0].value; + let output_sum = tx.global.unsigned_tx.output[0].value; let fee = input_sum .checked_sub(output_sum) .ok_or(FeeStrategyError::AmountOfFeeTooHigh)?; - let weight = tx.unsigned_tx.get_weight() as u64; + let weight = tx.global.unsigned_tx.get_weight() as u64; let effective_sat_per_vbyte = SatPerVByte::from_sat( weight @@ -196,7 +196,6 @@ mod tests { for s in [ "0.0001 BTC/vByte", "100 satoshi/vByte", - "100 satoshis/vByte", "10 satoshi/vByte", "1 satoshi/vByte", ] @@ -206,7 +205,7 @@ mod tests { assert!(parse.is_ok()); } // MUST fail - for s in ["1 satoshi", "100 vByte"].iter() { + for s in ["100 satoshis/vByte", "1 satoshi", "100 vByte"].iter() { let parse = SatPerVByte::from_str(s); assert!(parse.is_err()); } diff --git a/src/bitcoin/segwitv0.rs b/src/bitcoin/segwitv0.rs index f6155fcb..e4e92fbe 100644 --- a/src/bitcoin/segwitv0.rs +++ b/src/bitcoin/segwitv0.rs @@ -19,10 +19,13 @@ use crate::script::{DataLock, DataPunishableLock, DoubleKeys, ScriptPath}; use bitcoin::blockdata::opcodes; use bitcoin::blockdata::script::{Builder, Instruction, Script}; -use bitcoin::blockdata::transaction::EcdsaSigHashType; +use bitcoin::blockdata::transaction::SigHashType; use bitcoin::hashes::sha256d::Hash as Sha256dHash; -use bitcoin::secp256k1::{ecdsa::Signature, Message, PublicKey, Secp256k1, SecretKey, Signing}; -use bitcoin::util::sighash::SigHashCache; +use bitcoin::secp256k1::{ + key::{PublicKey, SecretKey}, + Message, Secp256k1, Signature, Signing, +}; +use bitcoin::util::bip143::SigHashCache; use ecdsa_fun::adaptor::EncryptedSignature; @@ -81,8 +84,8 @@ impl From for Btc { } pub struct CoopLock { - a: PublicKey, - b: PublicKey, + a: bitcoin::util::key::PublicKey, + b: bitcoin::util::key::PublicKey, } impl CoopLock { @@ -92,9 +95,9 @@ impl CoopLock { .. } = data; Builder::new() - .push_key(&bitcoin::util::key::PublicKey::new(*alice)) + .push_key(&bitcoin::util::ecdsa::PublicKey::new(*alice)) .push_opcode(opcodes::all::OP_CHECKSIGVERIFY) - .push_key(&bitcoin::util::key::PublicKey::new(*bob)) + .push_key(&bitcoin::util::ecdsa::PublicKey::new(*bob)) .push_opcode(opcodes::all::OP_CHECKSIG) .into_script() } @@ -123,7 +126,7 @@ impl CoopLock { _ => Err(Error::MissingPublicKey), }, )?; - let a = PublicKey::from_slice(bytes).map_err(Error::new)?; + let a = bitcoin::util::key::PublicKey::from_slice(bytes).map_err(Error::new)?; // Checksig verify ints.next() .ok_or(Error::WrongTemplate("Missing opcode")) @@ -141,7 +144,7 @@ impl CoopLock { Err(e) => Err(Error::new(e)), _ => Err(Error::MissingPublicKey), })?; - let b = PublicKey::from_slice(bytes).map_err(Error::new)?; + let b = bitcoin::util::key::PublicKey::from_slice(bytes).map_err(Error::new)?; // Checksig ints.next() .ok_or(Error::WrongTemplate("Missing opcode")) @@ -162,7 +165,7 @@ impl CoopLock { Ok(Self { a, b }) } - pub fn get_pubkey(&self, swap_role: SwapRole) -> &PublicKey { + pub fn get_pubkey(&self, swap_role: SwapRole) -> &bitcoin::util::key::PublicKey { match swap_role { SwapRole::Alice => &self.a, SwapRole::Bob => &self.b, @@ -171,9 +174,9 @@ impl CoopLock { } pub struct PunishLock { - alice: PublicKey, - bob: PublicKey, - punish: PublicKey, + alice: bitcoin::util::key::PublicKey, + bob: bitcoin::util::key::PublicKey, + punish: bitcoin::util::key::PublicKey, } impl PunishLock { @@ -185,15 +188,15 @@ impl PunishLock { } = data; Builder::new() .push_opcode(opcodes::all::OP_IF) - .push_key(&bitcoin::util::key::PublicKey::new(*alice)) + .push_key(&bitcoin::util::ecdsa::PublicKey::new(*alice)) .push_opcode(opcodes::all::OP_CHECKSIGVERIFY) - .push_key(&bitcoin::util::key::PublicKey::new(*bob)) + .push_key(&bitcoin::util::ecdsa::PublicKey::new(*bob)) .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::all::OP_ELSE) .push_int(timelock.as_u32().into()) .push_opcode(opcodes::all::OP_CSV) .push_opcode(opcodes::all::OP_DROP) - .push_key(&bitcoin::util::key::PublicKey::new(*failure)) + .push_key(&bitcoin::util::ecdsa::PublicKey::new(*failure)) .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::all::OP_ENDIF) .into_script() @@ -231,7 +234,7 @@ impl PunishLock { _ => Err(Error::MissingPublicKey), }, )?; - let alice = PublicKey::from_slice(bytes).map_err(Error::new)?; + let alice = bitcoin::util::key::PublicKey::from_slice(bytes).map_err(Error::new)?; // Checksig verify ints.next() .ok_or(Error::WrongTemplate("Missing opcode")) @@ -249,7 +252,7 @@ impl PunishLock { Err(e) => Err(Error::new(e)), _ => Err(Error::MissingPublicKey), })?; - let bob = PublicKey::from_slice(bytes).map_err(Error::new)?; + let bob = bitcoin::util::key::PublicKey::from_slice(bytes).map_err(Error::new)?; // Checksig ints.next() .ok_or(Error::WrongTemplate("Missing opcode")) @@ -293,7 +296,7 @@ impl PunishLock { Err(e) => Err(Error::new(e)), _ => Err(Error::MissingPublicKey), })?; - let punish = PublicKey::from_slice(bytes).map_err(Error::new)?; + let punish = bitcoin::util::key::PublicKey::from_slice(bytes).map_err(Error::new)?; // Checksig ints.next() .ok_or(Error::WrongTemplate("Missing opcode")) @@ -322,7 +325,11 @@ impl PunishLock { Ok(Self { alice, bob, punish }) } - pub fn get_pubkey(&self, swap_role: SwapRole, script_path: ScriptPath) -> Option<&PublicKey> { + pub fn get_pubkey( + &self, + swap_role: SwapRole, + script_path: ScriptPath, + ) -> Option<&bitcoin::util::key::PublicKey> { match script_path { ScriptPath::Success => match swap_role { SwapRole::Alice => Some(&self.alice), @@ -445,11 +452,10 @@ pub fn signature_hash( txin: TxInRef, script: &Script, value: u64, - sighash_type: EcdsaSigHashType, + sighash_type: SigHashType, ) -> Sha256dHash { SigHashCache::new(txin.transaction) - .segwit_signature_hash(txin.index, script, value, sighash_type) - .expect("encoding works") + .signature_hash(txin.index, script, value, sighash_type) .as_hash() } @@ -461,7 +467,7 @@ pub fn sign_input( txin: TxInRef, script: &Script, value: u64, - sighash_type: EcdsaSigHashType, + sighash_type: SigHashType, secret_key: &bitcoin::secp256k1::SecretKey, ) -> Result where @@ -471,7 +477,7 @@ where let sighash = signature_hash(txin, script, value, sighash_type); // Makes signature. let msg = Message::from_slice(&sighash[..])?; - let mut sig = context.sign_ecdsa(&msg, secret_key); + let mut sig = context.sign(&msg, secret_key); sig.normalize_s(); Ok(sig) } @@ -487,7 +493,7 @@ pub fn sign_hash( let context = Secp256k1::new(); // Makes signature. let msg = Message::from_slice(&sighash[..])?; - let mut sig = context.sign_ecdsa(&msg, secret_key); + let mut sig = context.sign(&msg, secret_key); sig.normalize_s(); Ok(sig) } diff --git a/src/bitcoin/segwitv0/buy.rs b/src/bitcoin/segwitv0/buy.rs index d12f4f3e..6ebf474d 100644 --- a/src/bitcoin/segwitv0/buy.rs +++ b/src/bitcoin/segwitv0/buy.rs @@ -1,9 +1,7 @@ use std::marker::PhantomData; -use bitcoin::blockdata::transaction::{TxIn, TxOut}; -use bitcoin::blockdata::witness::Witness; -use bitcoin::secp256k1::ecdsa::Signature; -use bitcoin::util::ecdsa::EcdsaSig; +use bitcoin::blockdata::transaction::{SigHashType, TxIn, TxOut}; +use bitcoin::secp256k1::Signature; use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::Address; @@ -39,11 +37,7 @@ impl SubTransaction for Buy { .ok_or(FError::MissingSignature)? .clone(); - psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![ - bob_sig.to_vec(), - alice_sig.to_vec(), - script.into_bytes(), - ])); + psbt.inputs[0].final_script_witness = Some(vec![bob_sig, alice_sig, script.into_bytes()]); Ok(()) } @@ -64,7 +58,7 @@ impl Buyable, MetadataOutput> for Tx { previous_output: output_metadata.out_point, script_sig: bitcoin::Script::default(), sequence: 0, - witness: Witness::new(), + witness: vec![], }], output: vec![TxOut { value: output_metadata.tx_out.value, @@ -78,6 +72,7 @@ impl Buyable, MetadataOutput> for Tx { // Set the input witness data and sighash type psbt.inputs[0].witness_utxo = Some(output_metadata.tx_out); psbt.inputs[0].witness_script = output_metadata.script_pubkey; + psbt.inputs[0].sighash_type = Some(SigHashType::All); Ok(Tx { psbt, @@ -86,25 +81,25 @@ impl Buyable, MetadataOutput> for Tx { } fn verify_template(&self, destination_target: Address) -> Result<(), FError> { - (self.psbt.unsigned_tx.version == 2) + (self.psbt.global.unsigned_tx.version == 2) .then(|| 0) .ok_or(FError::WrongTemplate("Tx version is not 2"))?; - (self.psbt.unsigned_tx.lock_time == 0) + (self.psbt.global.unsigned_tx.lock_time == 0) .then(|| 0) .ok_or(FError::WrongTemplate("LockTime is not set to 0"))?; - (self.psbt.unsigned_tx.input.len() == 1) + (self.psbt.global.unsigned_tx.input.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of inputs is not 1"))?; - (self.psbt.unsigned_tx.output.len() == 1) + (self.psbt.global.unsigned_tx.output.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of outputs is not 1"))?; - let txin = &self.psbt.unsigned_tx.input[0]; + let txin = &self.psbt.global.unsigned_tx.input[0]; (txin.sequence == 0) .then(|| 0) .ok_or(FError::WrongTemplate("Sequence is not set to 0"))?; - let txout = &self.psbt.unsigned_tx.output[0]; + let txout = &self.psbt.global.unsigned_tx.output[0]; let script_pubkey = destination_target.script_pubkey(); (txout.script_pubkey == script_pubkey) .then(|| 0) @@ -115,9 +110,9 @@ impl Buyable, MetadataOutput> for Tx { fn extract_witness(tx: bitcoin::Transaction) -> Signature { let TxIn { witness, .. } = &tx.input[0]; - let witness_bytes = witness.to_vec(); - let ecdsa_sig = EcdsaSig::from_slice(witness_bytes[0].as_ref()) - .expect("Validated transaction on-chain, signature and witness position is correct."); - ecdsa_sig.sig + let bytes: &[u8] = witness[0].as_ref(); + // Remove SIGHASH type at the end of the signature + Signature::from_der(&bytes[..bytes.len() - 1]) + .expect("Validated transaction on-chain, signature and witness position is correct.") } } diff --git a/src/bitcoin/segwitv0/cancel.rs b/src/bitcoin/segwitv0/cancel.rs index ee63a9e3..d8b43c49 100644 --- a/src/bitcoin/segwitv0/cancel.rs +++ b/src/bitcoin/segwitv0/cancel.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData; -use bitcoin::blockdata::transaction::{TxIn, TxOut}; -use bitcoin::blockdata::witness::Witness; +use bitcoin::blockdata::transaction::{SigHashType, TxIn, TxOut}; use bitcoin::util::psbt::PartiallySignedTransaction; use crate::role::SwapRole; @@ -36,11 +35,7 @@ impl SubTransaction for Cancel { .ok_or(FError::MissingSignature)? .clone(); - psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![ - bob_sig.to_vec(), - alice_sig.to_vec(), - script.into_bytes(), - ])); + psbt.inputs[0].final_script_witness = Some(vec![bob_sig, alice_sig, script.into_bytes()]); Ok(()) } @@ -62,7 +57,7 @@ impl Cancelable, MetadataOutput> for Tx { previous_output: output_metadata.out_point, script_sig: bitcoin::Script::default(), sequence: lock.timelock.as_u32(), - witness: Witness::new(), + witness: vec![], }], output: vec![TxOut { value: output_metadata.tx_out.value, @@ -76,6 +71,7 @@ impl Cancelable, MetadataOutput> for Tx { // Set the input witness data and sighash type psbt.inputs[0].witness_utxo = Some(output_metadata.tx_out); psbt.inputs[0].witness_script = output_metadata.script_pubkey; + psbt.inputs[0].sighash_type = Some(SigHashType::All); // Set the script witness of the output psbt.outputs[0].witness_script = Some(script); @@ -91,27 +87,27 @@ impl Cancelable, MetadataOutput> for Tx { lock: script::DataLock>, punish_lock: script::DataPunishableLock>, ) -> Result<(), FError> { - (self.psbt.unsigned_tx.version == 2) + (self.psbt.global.unsigned_tx.version == 2) .then(|| 0) .ok_or(FError::WrongTemplate("Tx version is not 2"))?; - (self.psbt.unsigned_tx.lock_time == 0) + (self.psbt.global.unsigned_tx.lock_time == 0) .then(|| 0) .ok_or(FError::WrongTemplate("LockTime is not set to 0"))?; - (self.psbt.unsigned_tx.input.len() == 1) + (self.psbt.global.unsigned_tx.input.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of inputs is not 1"))?; - (self.psbt.unsigned_tx.output.len() == 1) + (self.psbt.global.unsigned_tx.output.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of outputs is not 1"))?; - let txin = &self.psbt.unsigned_tx.input[0]; + let txin = &self.psbt.global.unsigned_tx.input[0]; (txin.sequence == lock.timelock.as_u32()) .then(|| 0) .ok_or(FError::WrongTemplate( "Sequence is not set correctly for timelock", ))?; - let txout = &self.psbt.unsigned_tx.output[0]; + let txout = &self.psbt.global.unsigned_tx.output[0]; let script_pubkey = PunishLock::v0_p2wsh(punish_lock); (txout.script_pubkey == script_pubkey) .then(|| 0) diff --git a/src/bitcoin/segwitv0/funding.rs b/src/bitcoin/segwitv0/funding.rs index 51b28c66..60e2593f 100644 --- a/src/bitcoin/segwitv0/funding.rs +++ b/src/bitcoin/segwitv0/funding.rs @@ -2,7 +2,7 @@ use bitcoin::blockdata::transaction::{OutPoint, Transaction}; use bitcoin::network::constants::Network as BtcNetwork; -use bitcoin::secp256k1::PublicKey; +use bitcoin::secp256k1::key::PublicKey; use bitcoin::Address; use crate::blockchain::Network; @@ -25,7 +25,7 @@ impl Linkable for Funding { fn get_consumable_output(&self) -> Result { // Create a **COMPRESSED** ECDSA public key. let pubkey = match self.pubkey { - Some(pubkey) => bitcoin::util::key::PublicKey::new(pubkey), + Some(pubkey) => bitcoin::util::ecdsa::PublicKey::new(pubkey), None => return Err(FError::MissingPublicKey), }; @@ -79,7 +79,7 @@ impl Fundable, MetadataOutput> for Funding { fn get_address(&self) -> Result { let pubkey = match self.pubkey { - Some(pubkey) => Ok(bitcoin::util::key::PublicKey::new(pubkey)), + Some(pubkey) => Ok(bitcoin::util::ecdsa::PublicKey::new(pubkey)), None => Err(FError::MissingPublicKey), }?; diff --git a/src/bitcoin/segwitv0/lock.rs b/src/bitcoin/segwitv0/lock.rs index f95fa7ca..10d84f42 100644 --- a/src/bitcoin/segwitv0/lock.rs +++ b/src/bitcoin/segwitv0/lock.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData; -use bitcoin::blockdata::transaction::{TxIn, TxOut}; -use bitcoin::blockdata::witness::Witness; +use bitcoin::blockdata::transaction::{SigHashType, TxIn, TxOut}; use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::Amount; @@ -23,10 +22,7 @@ impl SubTransaction for Lock { .iter() .next() .ok_or(FError::MissingSignature)?; - psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![ - full_sig.to_vec(), - pubkey.serialize().to_vec(), - ])); + psbt.inputs[0].final_script_witness = Some(vec![full_sig.clone(), pubkey.to_bytes()]); Ok(()) } } @@ -51,7 +47,7 @@ impl Lockable, MetadataOutput> for Tx { previous_output: output_metadata.out_point, script_sig: bitcoin::Script::default(), sequence: CSVTimelock::disable(), - witness: Witness::new(), + witness: vec![], }], output: vec![TxOut { value: target_amount.as_sat(), @@ -65,6 +61,7 @@ impl Lockable, MetadataOutput> for Tx { // Set the input witness data and sighash type psbt.inputs[0].witness_utxo = Some(output_metadata.tx_out); psbt.inputs[0].witness_script = output_metadata.script_pubkey; + psbt.inputs[0].sighash_type = Some(SigHashType::All); // Set the script witness of the output psbt.outputs[0].witness_script = Some(script); @@ -76,25 +73,25 @@ impl Lockable, MetadataOutput> for Tx { } fn verify_template(&self, lock: script::DataLock>) -> Result<(), FError> { - (self.psbt.unsigned_tx.version == 2) + (self.psbt.global.unsigned_tx.version == 2) .then(|| 0) .ok_or(FError::WrongTemplate("Tx version is not 2"))?; - (self.psbt.unsigned_tx.lock_time == 0) + (self.psbt.global.unsigned_tx.lock_time == 0) .then(|| 0) .ok_or(FError::WrongTemplate("LockTime is not set to 0"))?; - (self.psbt.unsigned_tx.input.len() == 1) + (self.psbt.global.unsigned_tx.input.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of inputs is not 1"))?; - (self.psbt.unsigned_tx.output.len() == 1) + (self.psbt.global.unsigned_tx.output.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of outputs is not 1"))?; - let txin = &self.psbt.unsigned_tx.input[0]; + let txin = &self.psbt.global.unsigned_tx.input[0]; (txin.sequence == CSVTimelock::disable()) .then(|| 0) .ok_or(FError::WrongTemplate("Sequence timelock is not disabled"))?; - let txout = &self.psbt.unsigned_tx.output[0]; + let txout = &self.psbt.global.unsigned_tx.output[0]; let script_pubkey = CoopLock::v0_p2wsh(lock); (txout.script_pubkey == script_pubkey) .then(|| 0) diff --git a/src/bitcoin/segwitv0/punish.rs b/src/bitcoin/segwitv0/punish.rs index 40609ec2..dd2df646 100644 --- a/src/bitcoin/segwitv0/punish.rs +++ b/src/bitcoin/segwitv0/punish.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData; -use bitcoin::blockdata::transaction::{TxIn, TxOut}; -use bitcoin::blockdata::witness::Witness; +use bitcoin::blockdata::transaction::{SigHashType, TxIn, TxOut}; use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::Address; @@ -36,11 +35,11 @@ impl SubTransaction for Punish { .ok_or(Error::MissingSignature)? .clone(); - psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![ - punish_sig.to_vec(), + psbt.inputs[0].final_script_witness = Some(vec![ + punish_sig, vec![], // OP_FALSE script.into_bytes(), - ])); + ]); Ok(()) } } @@ -60,7 +59,7 @@ impl Punishable, MetadataOutput> for Tx { previous_output: output_metadata.out_point, script_sig: bitcoin::Script::default(), sequence: punish_lock.timelock.as_u32(), - witness: Witness::new(), + witness: vec![], }], output: vec![TxOut { value: output_metadata.tx_out.value, @@ -74,6 +73,7 @@ impl Punishable, MetadataOutput> for Tx { // Set the input witness data and sighash type psbt.inputs[0].witness_utxo = Some(output_metadata.tx_out); psbt.inputs[0].witness_script = output_metadata.script_pubkey; + psbt.inputs[0].sighash_type = Some(SigHashType::All); Ok(Tx { psbt, diff --git a/src/bitcoin/segwitv0/refund.rs b/src/bitcoin/segwitv0/refund.rs index e927cd5a..c3621f07 100644 --- a/src/bitcoin/segwitv0/refund.rs +++ b/src/bitcoin/segwitv0/refund.rs @@ -1,9 +1,7 @@ use std::marker::PhantomData; -use bitcoin::blockdata::transaction::{TxIn, TxOut}; -use bitcoin::blockdata::witness::Witness; -use bitcoin::secp256k1::ecdsa::Signature; -use bitcoin::util::ecdsa::EcdsaSig; +use bitcoin::blockdata::transaction::{SigHashType, TxIn, TxOut}; +use bitcoin::secp256k1::Signature; use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::Address; @@ -47,12 +45,12 @@ impl SubTransaction for Refund { .ok_or(FError::MissingSignature)? .clone(); - psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![ - bob_sig.to_vec(), - alice_sig.to_vec(), + psbt.inputs[0].final_script_witness = Some(vec![ + bob_sig, + alice_sig, vec![1], // OP_TRUE script.into_bytes(), // cancel script - ])); + ]); Ok(()) } @@ -72,7 +70,7 @@ impl Refundable, MetadataOutput> for Tx { previous_output: output_metadata.out_point, script_sig: bitcoin::Script::default(), sequence: 0, - witness: Witness::new(), + witness: vec![], }], output: vec![TxOut { value: output_metadata.tx_out.value, @@ -86,6 +84,7 @@ impl Refundable, MetadataOutput> for Tx { // Set the input witness data and sighash type psbt.inputs[0].witness_utxo = Some(output_metadata.tx_out); psbt.inputs[0].witness_script = output_metadata.script_pubkey; + psbt.inputs[0].sighash_type = Some(SigHashType::All); Ok(Tx { psbt, @@ -94,25 +93,25 @@ impl Refundable, MetadataOutput> for Tx { } fn verify_template(&self, refund_target: Address) -> Result<(), FError> { - (self.psbt.unsigned_tx.version == 2) + (self.psbt.global.unsigned_tx.version == 2) .then(|| 0) .ok_or(FError::WrongTemplate("Tx version is not 2"))?; - (self.psbt.unsigned_tx.lock_time == 0) + (self.psbt.global.unsigned_tx.lock_time == 0) .then(|| 0) .ok_or(FError::WrongTemplate("LockTime is not set to 0"))?; - (self.psbt.unsigned_tx.input.len() == 1) + (self.psbt.global.unsigned_tx.input.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of inputs is not 1"))?; - (self.psbt.unsigned_tx.output.len() == 1) + (self.psbt.global.unsigned_tx.output.len() == 1) .then(|| 0) .ok_or(FError::WrongTemplate("Number of outputs is not 1"))?; - let txin = &self.psbt.unsigned_tx.input[0]; + let txin = &self.psbt.global.unsigned_tx.input[0]; (txin.sequence == 0) .then(|| 0) .ok_or(FError::WrongTemplate("Sequence is not set to 0"))?; - let txout = &self.psbt.unsigned_tx.output[0]; + let txout = &self.psbt.global.unsigned_tx.output[0]; let script_pubkey = refund_target.script_pubkey(); (txout.script_pubkey == script_pubkey) .then(|| 0) @@ -123,9 +122,9 @@ impl Refundable, MetadataOutput> for Tx { fn extract_witness(tx: bitcoin::Transaction) -> Signature { let TxIn { witness, .. } = &tx.input[0]; - let witness_bytes = witness.to_vec(); - let ecdsa_sig = EcdsaSig::from_slice(witness_bytes[1].as_ref()) - .expect("Validated transaction on-chain, signature and witness position is correct."); - ecdsa_sig.sig + let bytes: &[u8] = witness[1].as_ref(); + // Remove SIGHASH type at the end of the signature + Signature::from_der(&bytes[..bytes.len() - 1]) + .expect("Validated transaction on-chain, signature and witness position is correct.") } } diff --git a/src/bitcoin/taproot/mod.rs b/src/bitcoin/taproot/mod.rs index f9520007..5c92d6be 100644 --- a/src/bitcoin/taproot/mod.rs +++ b/src/bitcoin/taproot/mod.rs @@ -11,7 +11,10 @@ use crate::crypto::{Keys, SharedKeyId, SharedSecretKeys, Signatures}; //use crate::role::Arbitrating; use bitcoin::hashes::sha256d::Hash as Sha256dHash; -use bitcoin::secp256k1::{constants::SECRET_KEY_SIZE, schnorr::Signature, KeyPair, XOnlyPublicKey}; +use bitcoin::secp256k1::{ + constants::SECRET_KEY_SIZE, + schnorrsig::{KeyPair, PublicKey, Signature}, +}; /// Inner type for the Taproot strategy with on-chain scripts. #[derive(Clone, Debug, Copy, Eq, PartialEq)] @@ -57,7 +60,7 @@ impl TryFrom for Bitcoin { impl Keys for Bitcoin { type SecretKey = KeyPair; - type PublicKey = XOnlyPublicKey; + type PublicKey = PublicKey; fn extra_keys() -> Vec { // No extra key @@ -65,7 +68,7 @@ impl Keys for Bitcoin { } } -impl CanonicalBytes for XOnlyPublicKey { +impl CanonicalBytes for PublicKey { fn as_canonical_bytes(&self) -> Vec { self.serialize().as_ref().into() } @@ -74,7 +77,7 @@ impl CanonicalBytes for XOnlyPublicKey { where Self: Sized, { - XOnlyPublicKey::from_slice(bytes).map_err(consensus::Error::new) + PublicKey::from_slice(bytes).map_err(consensus::Error::new) } } diff --git a/src/bitcoin/transaction.rs b/src/bitcoin/transaction.rs index 25315b41..3bc05eed 100644 --- a/src/bitcoin/transaction.rs +++ b/src/bitcoin/transaction.rs @@ -4,15 +4,14 @@ use std::fmt::Debug; use std::marker::PhantomData; use bitcoin::blockdata::script::Script; -use bitcoin::blockdata::transaction::{EcdsaSigHashType, OutPoint, TxIn, TxOut}; +use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut}; use bitcoin::util::address; -use bitcoin::util::ecdsa::EcdsaSig; use bitcoin::util::psbt::{self, PartiallySignedTransaction}; #[cfg(feature = "experimental")] use bitcoin::{ hashes::sha256d::Hash, - secp256k1::{ecdsa::Signature, PublicKey}, + secp256k1::{key::PublicKey, Signature}, Amount, }; @@ -111,14 +110,14 @@ where fn based_on(&self) -> MetadataOutput { MetadataOutput { - out_point: self.psbt.unsigned_tx.input[0].previous_output, + out_point: self.psbt.global.unsigned_tx.input[0].previous_output, tx_out: self.psbt.inputs[0].witness_utxo.clone().unwrap(), // FIXME script_pubkey: self.psbt.inputs[0].witness_script.clone(), } } fn output_amount(&self) -> Amount { - Amount::from_sat(self.psbt.unsigned_tx.output[0].value) + Amount::from_sat(self.psbt.global.unsigned_tx.output[0].value) } } @@ -146,10 +145,10 @@ where T: SubTransaction, { fn get_consumable_output(&self) -> Result { - match self.psbt.unsigned_tx.output.len() { + match self.psbt.global.unsigned_tx.output.len() { 1 => (), 2 => { - if !self.psbt.unsigned_tx.is_coin_base() { + if !self.psbt.global.unsigned_tx.is_coin_base() { return Err(FError::new(Error::MultiUTXOUnsuported)); } } @@ -157,8 +156,8 @@ where } Ok(MetadataOutput { - out_point: OutPoint::new(self.psbt.unsigned_tx.txid(), 0), - tx_out: self.psbt.unsigned_tx.output[0].clone(), + out_point: OutPoint::new(self.psbt.global.unsigned_tx.txid(), 0), + tx_out: self.psbt.global.unsigned_tx.output[0].clone(), script_pubkey: self.psbt.outputs[0].witness_script.clone(), }) } @@ -174,7 +173,7 @@ where /// This function is used for generating the witness message for all transactions but not /// funding. So implying only 1 input is valid as all templates only have 1 input. fn generate_witness_message(&self, _path: ScriptPath) -> Result { - let unsigned_tx = self.psbt.unsigned_tx.clone(); + let unsigned_tx = self.psbt.global.unsigned_tx.clone(); let txin = TxInRef::new(&unsigned_tx, 0); let witness_utxo = self.psbt.inputs[0] @@ -188,12 +187,21 @@ where .ok_or(FError::MissingWitness)?; let value = witness_utxo.value; - Ok(signature_hash(txin, &script, value, EcdsaSigHashType::All)) + let sighash_type = self.psbt.inputs[0] + .sighash_type + .ok_or_else(|| FError::new(Error::MissingSigHashType))?; + + Ok(signature_hash(txin, &script, value, sighash_type)) } fn add_witness(&mut self, pubkey: PublicKey, sig: Signature) -> Result<(), FError> { - let sig_all = EcdsaSig::sighash_all(sig); - self.psbt.inputs[0].partial_sigs.insert(pubkey, sig_all); + let sighash_type = self.psbt.inputs[0] + .sighash_type + .ok_or_else(|| FError::new(Error::MissingSigHashType))?; + let mut full_sig = sig.serialize_der().to_vec(); + full_sig.extend_from_slice(&[sighash_type.as_u32() as u8]); + let pubkey = bitcoin::util::ecdsa::PublicKey::new(pubkey); + self.psbt.inputs[0].partial_sigs.insert(pubkey, full_sig); Ok(()) } } diff --git a/src/crypto/slip10.rs b/src/crypto/slip10.rs index 0a815668..ec4f6a9c 100644 --- a/src/crypto/slip10.rs +++ b/src/crypto/slip10.rs @@ -401,7 +401,7 @@ mod tests { assert_eq!(asserts[0], res.parent_fingerprint.to_string()); assert_eq!(asserts[1], res.chain_code.to_string()); - assert_eq!(asserts[2], res.secret_key.display_secret().to_string()); + assert_eq!(asserts[2], res.secret_key.to_string()); assert_eq!(asserts[3], res.public_key(&ctx).to_string()); } diff --git a/src/negotiation.rs b/src/negotiation.rs index f4576124..1d656691 100644 --- a/src/negotiation.rs +++ b/src/negotiation.rs @@ -125,7 +125,7 @@ impl<'de> Deserialize<'de> for OfferId { /// An offer is created by a [`TradeRole::Maker`] before the start of his daemon, it references all /// the data needed to parametrize a trade and be validated from a [`TradeRole::Taker`] -/// perspective. The daemon start when the maker is ready to finalyze his offer, transforming the +/// perspective. The daemon start when the maker is ready to finalize his offer, transforming the /// offer into a [`PublicOffer`] which contains the data needed to a taker to connect to the /// maker's daemon. #[derive(Debug, Clone, Eq)] @@ -675,9 +675,9 @@ mod tests { lazy_static::lazy_static! { pub static ref NODE_ID: PublicKey = { let sk = - bitcoin::util::key::PrivateKey::from_wif("L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D") + bitcoin::PrivateKey::from_wif("L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D") .unwrap() - .inner; + .key; secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &sk) }; diff --git a/src/swap/btcxmr.rs b/src/swap/btcxmr.rs index cb102ffb..37c4d8b8 100644 --- a/src/swap/btcxmr.rs +++ b/src/swap/btcxmr.rs @@ -27,11 +27,12 @@ use rand_chacha::ChaCha20Rng; use sha2::Sha256; #[cfg(feature = "experimental")] -use bitcoin::{ - hashes::sha256d::Hash as Sha256dHash, secp256k1::ecdsa::Signature, secp256k1::Message, -}; +use bitcoin::{hashes::sha256d::Hash as Sha256dHash, secp256k1::Message, secp256k1::Signature}; -use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use bitcoin::secp256k1::{ + key::{PublicKey, SecretKey}, + Secp256k1, +}; use std::collections::HashMap; use std::str::FromStr; @@ -297,8 +298,7 @@ impl Sign for ) -> Result<(), crypto::Error> { let secp = Secp256k1::new(); let message = Message::from_slice(&msg).expect("Hash is always ok"); - secp.verify_ecdsa(&message, sig, key) - .map_err(crypto::Error::new) + secp.verify(&message, sig, key).map_err(crypto::Error::new) } fn encrypt_sign( diff --git a/tests/negotiation.rs b/tests/negotiation.rs index fec1afa9..41ac6562 100644 --- a/tests/negotiation.rs +++ b/tests/negotiation.rs @@ -90,11 +90,9 @@ fn serialize_public_offer() { let port = FromStr::from_str("9735").unwrap(); let secp = secp256k1::Secp256k1::new(); - let sk = bitcoin::util::key::PrivateKey::from_wif( - "L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D", - ) - .unwrap() - .inner; + let sk = bitcoin::PrivateKey::from_wif("L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D") + .unwrap() + .key; let node_id = secp256k1::PublicKey::from_secret_key(&secp, &sk); let peer_address = InetSocketAddr::new(ip, port); let public_offer = offer.to_public_v1(node_id, peer_address); diff --git a/tests/protocol.rs b/tests/protocol.rs index e5dca3ea..35a35964 100644 --- a/tests/protocol.rs +++ b/tests/protocol.rs @@ -16,7 +16,6 @@ use farcaster_core::swap::SwapId; use farcaster_core::transaction::*; use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut}; -use bitcoin::blockdata::witness::Witness; use bitcoin::secp256k1::{PublicKey, Secp256k1}; use bitcoin::Address; @@ -119,7 +118,7 @@ fn execute_offline_protocol() { previous_output: OutPoint::null(), script_sig: bitcoin::blockdata::script::Script::default(), sequence: (1 << 31) as u32, // activate disable flag on CSV - witness: Witness::new(), + witness: vec![], }], output: vec![TxOut { value: 123456789, diff --git a/tests/rpc/mod.rs b/tests/rpc/mod.rs index de70769a..778ec5dd 100644 --- a/tests/rpc/mod.rs +++ b/tests/rpc/mod.rs @@ -61,7 +61,7 @@ macro_rules! new_address { let pair = s.generate_keypair(&mut thread_rng()); let public_key = key::PublicKey { compressed: true, - inner: pair.1, + key: pair.1, }; // Generate pay-to-pubkey-hash address