diff --git a/src/dlcat.rs b/src/dlcat.rs index 515d263..8c49bfa 100644 --- a/src/dlcat.rs +++ b/src/dlcat.rs @@ -4,7 +4,7 @@ use bitcoin::opcodes::all::{OP_CHECKSIGVERIFY, OP_NOP4}; use bitcoin::script::PushBytesBuf; use bitcoin::{ taproot::{TaprootBuilder, TaprootSpendInfo}, - ScriptBuf, + ScriptBuf, TxOut, }; use dlc::get_adaptor_point_from_oracle_info; use dlc::secp256k1_zkp::{Message, Secp256k1, XOnlyPublicKey}; @@ -29,7 +29,11 @@ pub fn build_cat_taproot_leafs( let msg = Message::from_hashed_data::( payout.outcome.as_bytes(), ); - let cat_script = op_cat_dlc_payout(&script_pubkey, payout.offer_payout); + let output = TxOut { + script_pubkey: script_pubkey.clone(), + value: payout.offer_payout, + }; + let cat_script = op_cat_dlc_payout(&[output]); builder = builder.add_leaf(depth, cat_script).unwrap(); } } diff --git a/src/op_cat/mod.rs b/src/op_cat/mod.rs index 7301356..77d449a 100644 --- a/src/op_cat/mod.rs +++ b/src/op_cat/mod.rs @@ -3,12 +3,14 @@ use crate::build_cat_taproot_leafs; use crate::utils::create_nums_key; use bitcoin::absolute::LockTime; use bitcoin::consensus::Encodable; +use bitcoin::hashes::{sha256, Hash}; use bitcoin::key::Secp256k1; use bitcoin::opcodes::all::{ OP_CAT, OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_FROMALTSTACK, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK, }; use bitcoin::script::{Builder, PushBytesBuf}; +use bitcoin::secp256k1::ThirtyTwoByteHash; use bitcoin::sighash::TapSighashType; use bitcoin::taproot::{LeafVersion, TapLeafHash, TaprootBuilder}; use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut}; @@ -80,13 +82,11 @@ pub(crate) fn create_cat_spending_tx( }; let tx_commitment_spec = TxCommitmentSpec { - prev_sciptpubkeys: false, - prev_amounts: false, outputs: false, ..Default::default() }; - let enforce_payout_spk = op_cat_dlc_payout(&output.script_pubkey, output.value); + let enforce_payout_spk = op_cat_dlc_payout(&[output.clone()]); let leaf_hash = TapLeafHash::from_script(&enforce_payout_spk, LeafVersion::TapScript); let contract_components = @@ -113,22 +113,9 @@ pub(crate) fn create_cat_spending_tx( let computed_signature = compute_signature_from_components(&contract_components.signature_components)?; - let mut amount_buffer = Vec::new(); - prev_output.value.consensus_encode(&mut amount_buffer)?; - collateral_txin.witness.push(amount_buffer.as_slice()); - let mut scriptpubkey_buffer = Vec::new(); - output - .script_pubkey - .consensus_encode(&mut scriptpubkey_buffer)?; - collateral_txin.witness.push(scriptpubkey_buffer.as_slice()); - let mangled_signature: [u8; 63] = computed_signature[0..63].try_into().unwrap(); // chop off the last byte, so we can provide the 0x00 and 0x01 bytes on the stack collateral_txin.witness.push(mangled_signature); - collateral_txin - .witness - .push(enforce_payout_spk.clone().to_bytes()); - // Build the taproot tree of outcomes let spend_info = build_cat_taproot_leafs( outcomes, @@ -144,6 +131,10 @@ pub(crate) fn create_cat_spending_tx( // .finalize(&secp, create_nums_key()) // .unwrap(); + collateral_txin + .witness + .push(enforce_payout_spk.clone().to_bytes()); + collateral_txin.witness.push( spend_info .control_block(&(enforce_payout_spk.clone(), LeafVersion::TapScript)) @@ -160,55 +151,31 @@ pub(crate) struct ContractComponents { pub(crate) signature_components: Vec>, } -pub(crate) fn op_cat_dlc_payout(payout_spk: &ScriptBuf, amount: u64) -> ScriptBuf { +pub(crate) fn op_cat_dlc_payout(outputs: &[TxOut]) -> ScriptBuf { let mut builder = Script::builder(); // The witness program needs to have the signature components except the outputs and the pre_scriptpubkeys and pre_amounts, // followed by the output amount, then the script pubkey, // followed by the fee amount, then the fee-paying scriptpubkey // and finally the mangled signature - let payout_spk_bytes: PushBytesBuf = payout_spk.clone().into_bytes().try_into().unwrap(); + let mut buffer = Vec::new(); + for o in outputs { + o.consensus_encode(&mut buffer).unwrap(); + } + let output_hash_bytes = sha256::Hash::hash(&buffer); builder = builder .push_opcode(OP_TOALTSTACK) // move pre-computed signature minus last byte to alt stack - // .push_opcode(OP_TOALTSTACK) // push the fee-paying scriptpubkey to the alt stack - // .push_opcode(OP_TOALTSTACK) // push the fee amount to the alt stack - // .push_opcode(OP_2DUP) // make a second copy of the vault scriptpubkey and amount so we can check input = output - .push_opcode(OP_TOALTSTACK) // push the first copy of the vault scriptpubkey to the alt stack - .push_opcode(OP_TOALTSTACK) // push the first copy of the vault amount to the alt stack - .push_slice(payout_spk_bytes.as_push_bytes()) // push the payout scriptpubkey - .push_slice(amount.to_le_bytes()) // push the payout amount - .push_opcode(OP_TOALTSTACK) // push the second copy of the vault scriptpubkey to the alt stack - .push_opcode(OP_TOALTSTACK) // push the second copy of the vault amount to the alt stack // start with encoded leaf hash .push_opcode(OP_CAT) // encoded leaf hash .push_opcode(OP_CAT) // encoded leaf hash .push_opcode(OP_CAT) // input index .push_opcode(OP_CAT) // spend type - .push_opcode(OP_FROMALTSTACK) // get the output amount - .push_opcode(OP_FROMALTSTACK) // get the second copy of the scriptpubkey - .push_opcode(OP_CAT) // cat the output amount and the second copy of the scriptpubkey - .push_opcode(OP_SHA256) // hash the output + .push_slice(output_hash_bytes.into_32()) .push_opcode(OP_SWAP) // move the hashed encoded outputs below our working sigmsg .push_opcode(OP_CAT) // outputs .push_opcode(OP_CAT) // prev sequences - .push_opcode(OP_FROMALTSTACK) // get the other copy of the vault amount - .push_opcode(OP_FROMALTSTACK) // get the other copy of the vault scriptpubkey - // .push_opcode(OP_FROMALTSTACK) // get the fee amount - // .push_opcode(OP_FROMALTSTACK) // get the fee-paying scriptpubkey - // .push_opcode(OP_SWAP) // move the fee-paying scriptpubkey below the fee amount - // .push_opcode(OP_TOALTSTACK) // move fee amount to alt stack - // .push_opcode(OP_CAT) // cat the vault scriptpubkey fee-paying scriptpubkey - .push_opcode(OP_SWAP) // move the vault amount to the top of the stack - .push_opcode(OP_TOALTSTACK) // move the vault amount to the alt stack - .push_opcode(OP_SHA256) // hash the scriptpubkeys, should now be consensus encoding - .push_opcode(OP_SWAP) // move the hashed encoded scriptpubkeys below our working sigmsg .push_opcode(OP_CAT) // prev scriptpubkeys - .push_opcode(OP_FROMALTSTACK) // get the vault amount - // .push_opcode(OP_FROMALTSTACK) // get the fee amount - // .push_opcode(OP_CAT) // cat the vault amount and the fee amount - .push_opcode(OP_SHA256) // hash the amounts - .push_opcode(OP_SWAP) // move the hashed encoded amounts below our working sigmsg .push_opcode(OP_CAT) // prev amounts .push_opcode(OP_CAT) // prevouts .push_opcode(OP_CAT) // lock time @@ -259,3 +226,40 @@ pub(crate) fn add_signature_construction_and_check(builder: Builder) -> Builder .push_slice(*G_X) // push G again. TODO: DUP this from before and stick it in the alt stack or something .push_opcode(OP_CHECKSIG) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::utils::{create_address, one_bit_contract_descriptor}; + use bitcoin::consensus::serialize; + use bitcoin::Address; + use std::str::FromStr; + + #[test] + fn test_cat_create_address() { + let bitcoind_address = + Address::from_str("tb1qe65apqqe3zq7qzaw45zjr4d7fenqdymhr3gums").unwrap(); + + let contract_address = create_address(bitcoind_address.payload.script_pubkey(), 100_000); + println!("{}", contract_address); + + let outpoint = OutPoint::from_str( + "2efc5d63872c24a2f1e2f67b7f89e2ba33e8d218242964e07fbaf210970225b1:1", + ) + .unwrap(); + let prev_output = TxOut { + script_pubkey: contract_address.payload.script_pubkey(), + value: 100_000, + }; + let spending_tx = create_cat_spending_tx( + outpoint, + prev_output, + bitcoind_address.payload.script_pubkey(), + one_bit_contract_descriptor(), + &[], + ) + .unwrap(); + + println!("{}", hex::encode(serialize(&spending_tx).to_vec())); + } +} diff --git a/src/op_ctv/mod.rs b/src/op_ctv/mod.rs index 6e2417e..65f6d30 100644 --- a/src/op_ctv/mod.rs +++ b/src/op_ctv/mod.rs @@ -46,7 +46,7 @@ pub(crate) fn create_ctv_spending_tx( output: TxOut, oracle_attestation: OracleAttestation, contract_descriptor: ContractDescriptor, - oracle_info: OracleInfo + oracle_info: OracleInfo, ) -> anyhow::Result { let secp = Secp256k1::new(); @@ -146,8 +146,8 @@ mod tests { ) .unwrap(); let attestation = create_dummy_announcement().1; - let spending_tx = create_ctv_spending_tx(outpoint, out, attestation).unwrap(); - - println!("{}", hex::encode(serialize(&spending_tx).to_vec())); + // let spending_tx = create_ctv_spending_tx(outpoint, out, attestation).unwrap(); + // + // println!("{}", hex::encode(serialize(&spending_tx).to_vec())); } } diff --git a/src/utils.rs b/src/utils.rs index 8fd45e5..49c7955 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,7 +3,7 @@ use bitcoin::hashes::Hash; use bitcoin::key::XOnlyPublicKey; use bitcoin::secp256k1::{Scalar, ThirtyTwoByteHash}; use bitcoin::taproot::TaprootBuilder; -use bitcoin::{Address, Network, ScriptBuf}; +use bitcoin::{Address, Network, ScriptBuf, TxOut}; use dlc::secp256k1_zkp::hashes::sha256; use dlc::secp256k1_zkp::rand::rngs::OsRng; use dlc::secp256k1_zkp::{KeyPair, Message, Secp256k1, SecretKey}; @@ -84,7 +84,11 @@ pub fn one_bit_contract_descriptor() -> ContractDescriptor { } pub fn create_address(payout_spk: ScriptBuf, amount: u64) -> Address { - let enforce_payout_spk = op_cat_dlc_payout(&payout_spk, amount - 10_000); + let out = TxOut { + script_pubkey: payout_spk, + value: amount - 10_000, // minus 10k sats for fees + }; + let enforce_payout_spk = op_cat_dlc_payout(&[out]); let secp = Secp256k1::new(); let builder = TaprootBuilder::new()