From 37620818c25dd23ee326113fd72030684c08f697 Mon Sep 17 00:00:00 2001 From: rot13maxi Date: Thu, 15 Feb 2024 18:21:53 -0500 Subject: [PATCH] make ben happy --- src/main.rs | 78 +++++++------ src/vault/contract.rs | 193 +++++++++++++++++++++----------- src/vault/mod.rs | 2 +- src/vault/script.rs | 66 +++++------ src/vault/signature_building.rs | 60 +++++++--- src/wallet.rs | 17 +-- 6 files changed, 254 insertions(+), 162 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8505e28..5ab9c55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,22 +2,21 @@ use std::path::PathBuf; use std::str::FromStr; use anyhow::Result; -use bitcoin::{Address, Amount, OutPoint, TxOut}; use bitcoin::consensus::Encodable; +use bitcoin::{Address, Amount, OutPoint, TxOut}; use bitcoincore_rpc::{RawTx, RpcApi}; use clap::Parser; use log::{debug, error, info}; use crate::settings::Settings; -use crate::vault::contract::{VaultCovenant, VaultState}; use crate::vault::contract::VaultState::{Completed, Inactive, Triggered}; +use crate::vault::contract::{VaultCovenant, VaultState}; use crate::wallet::Wallet; mod settings; mod vault; mod wallet; - #[derive(Parser)] struct Cli { #[arg(short, long, default_value = "settings.toml")] @@ -48,7 +47,10 @@ fn main() -> Result<()> { Ok(settings) => settings, Err(e) => { error!("Error reading settings file: {}", e); - info!("Creating a new settings file at {}", args.settings_file.display()); + info!( + "Creating a new settings file at {}", + args.settings_file.display() + ); let settings = Settings::default(); settings.to_toml_file(&args.settings_file)?; settings @@ -73,16 +75,23 @@ fn status(settings: &Settings) -> Result<()> { e })?; let client = Wallet::create_rpc_client(settings, None); - let latest_vault_transaction = client.get_raw_transaction(&vault.get_current_outpoint()?.txid, None)?; + let latest_vault_transaction = + client.get_raw_transaction(&vault.get_current_outpoint()?.txid, None)?; let latest_state_onchain: VaultState = (latest_vault_transaction, vault.address()?).into(); if latest_state_onchain == vault.get_state() { - info!("Vault state is consistent with the latest on-chain transaction: {:?}", latest_state_onchain); + info!( + "Vault state is consistent with the latest on-chain transaction: {:?}", + latest_state_onchain + ); } else if latest_state_onchain == Triggered { error!("Onchain state is Triggered, but the internal vault state is not. You can MIGHT BE GETTING ROBBED! Run the `cancel` command to cancel the withdrawal and SAVE YOUR MONEY!"); } else if vault.get_state() == Completed { info!("Vault state is Completed. This is expected after a successful withdrawal."); } else { - error!("Vault state is inconsistent with the latest on-chain transaction: {:?}", latest_state_onchain); + error!( + "Vault state is inconsistent with the latest on-chain transaction: {:?}", + latest_state_onchain + ); } Ok(()) } @@ -95,10 +104,13 @@ fn cancel(settings: &Settings) -> Result<()> { let fee_paying_address = fee_wallet.get_new_address()?; let fee_paying_utxo = miner_wallet.send(&fee_paying_address, Amount::from_sat(10_000))?; - let cancel_tx = vault.create_cancel_tx(&fee_paying_utxo, TxOut { - script_pubkey: fee_paying_address.script_pubkey(), - value: Amount::from_sat(10_000), - })?; + let cancel_tx = vault.create_cancel_tx( + &fee_paying_utxo, + TxOut { + script_pubkey: fee_paying_address.script_pubkey(), + value: Amount::from_sat(10_000), + }, + )?; let signed_tx = fee_wallet.sign_tx(&cancel_tx)?; let mut serialized_tx = Vec::new(); @@ -107,10 +119,7 @@ fn cancel(settings: &Settings) -> Result<()> { let txid = fee_wallet.broadcast_tx(&serialized_tx, None)?; info!("sent txid: {}", txid); miner_wallet.mine_blocks(Some(1))?; - vault.set_current_outpoint(OutPoint { - txid, - vout: 0, - }); + vault.set_current_outpoint(OutPoint { txid, vout: 0 }); vault.set_state(Inactive); vault.to_file(&settings.vault_file)?; @@ -130,12 +139,15 @@ fn complete(settings: &Settings) -> Result<()> { let fee_paying_utxo = miner_wallet.send(&fee_paying_address, Amount::from_sat(10_000))?; info!("need to mine {timelock_in_blocks} blocks for the timelock"); miner_wallet.mine_blocks(Some(timelock_in_blocks as u64))?; - let compete_tx = vault.create_complete_tx(&fee_paying_utxo, TxOut { - script_pubkey: fee_paying_address.script_pubkey(), - value: Amount::from_sat(10_000), - }, - &withdrawal_address, - &trigger_tx)?; + let compete_tx = vault.create_complete_tx( + &fee_paying_utxo, + TxOut { + script_pubkey: fee_paying_address.script_pubkey(), + value: Amount::from_sat(10_000), + }, + &withdrawal_address, + &trigger_tx, + )?; let signed_tx = fee_wallet.sign_tx(&compete_tx)?; let mut serialized_tx = Vec::new(); signed_tx.consensus_encode(&mut serialized_tx).unwrap(); @@ -143,12 +155,7 @@ fn complete(settings: &Settings) -> Result<()> { let txid = fee_wallet.broadcast_tx(&serialized_tx, None)?; info!("sent txid: {}", txid); miner_wallet.mine_blocks(Some(1))?; - vault.set_current_outpoint( - OutPoint { - txid, - vout: 0, - } - ); + vault.set_current_outpoint(OutPoint { txid, vout: 0 }); vault.set_state(Completed); vault.to_file(&settings.vault_file)?; @@ -166,10 +173,14 @@ fn trigger(destination: &str, steal: bool, settings: &Settings) -> Result<()> { let fee_paying_address = fee_wallet.get_new_address()?; let fee_paying_utxo = miner_wallet.send(&fee_paying_address, Amount::from_sat(10_000))?; miner_wallet.mine_blocks(Some(1))?; - let trigger_tx = vault.create_trigger_tx(&fee_paying_utxo, TxOut { - script_pubkey: fee_paying_address.script_pubkey(), - value: Amount::from_sat(10_000), - }, &withdrawal_address)?; + let trigger_tx = vault.create_trigger_tx( + &fee_paying_utxo, + TxOut { + script_pubkey: fee_paying_address.script_pubkey(), + value: Amount::from_sat(10_000), + }, + &withdrawal_address, + )?; let signed_tx = fee_wallet.sign_tx(&trigger_tx)?; let mut serialized_tx = Vec::new(); signed_tx.consensus_encode(&mut serialized_tx).unwrap(); @@ -178,10 +189,7 @@ fn trigger(destination: &str, steal: bool, settings: &Settings) -> Result<()> { info!("sent trigger transaction txid: {}", txid); miner_wallet.mine_blocks(Some(1))?; - vault.set_current_outpoint(OutPoint { - txid, - vout: 0, - }); + vault.set_current_outpoint(OutPoint { txid, vout: 0 }); if !steal { vault.set_withdrawal_address(Some(withdrawal_address)); vault.set_trigger_transaction(Some(trigger_tx)); diff --git a/src/vault/contract.rs b/src/vault/contract.rs index 833f736..7add32e 100644 --- a/src/vault/contract.rs +++ b/src/vault/contract.rs @@ -1,32 +1,35 @@ -use std::str::FromStr; use anyhow::{anyhow, Result}; -use bitcoin::{Address, Amount, Network, OutPoint, Sequence, TapLeafHash, TapSighashType, Transaction, TxIn, TxOut, XOnlyPublicKey}; use bitcoin::absolute::LockTime; use bitcoin::consensus::Encodable; -use bitcoin::hashes::{Hash, sha256}; +use bitcoin::hashes::{sha256, Hash}; use bitcoin::hex::{Case, DisplayHex}; use bitcoin::key::Secp256k1; use bitcoin::secp256k1::ThirtyTwoByteHash; use bitcoin::taproot::{LeafVersion, TaprootBuilder, TaprootSpendInfo}; use bitcoin::transaction::Version; +use bitcoin::{ + Address, Amount, Network, OutPoint, Sequence, TapLeafHash, TapSighashType, Transaction, TxIn, + TxOut, XOnlyPublicKey, +}; use bitcoincore_rpc::jsonrpc::serde_json; use log::{debug, info}; -use secp256kfun::{G, Point}; use secp256kfun::marker::{EvenY, NonZero, Public}; +use secp256kfun::{Point, G}; use serde::{Deserialize, Serialize}; +use std::str::FromStr; use crate::settings::Settings; -use crate::vault::signature_building; -use crate::vault::script::{vault_cancel_withdrawal, vault_complete_withdrawal, vault_trigger_withdrawal}; -use crate::vault::signature_building::{ - get_sigmsg_components, TxCommitmentSpec, +use crate::vault::script::{ + vault_cancel_withdrawal, vault_complete_withdrawal, vault_trigger_withdrawal, }; +use crate::vault::signature_building; +use crate::vault::signature_building::{get_sigmsg_components, TxCommitmentSpec}; #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] pub(crate) enum VaultState { Inactive, Triggered, - Completed + Completed, } /// Get the vault state from the transaction and the vault address @@ -35,7 +38,9 @@ impl From<(Transaction, Address)> for VaultState { let (tx, address) = spec; if tx.output.len() == 2 && tx.output.get(1).unwrap().value == Amount::from_sat(546) { VaultState::Triggered - } else if tx.output.len() == 1 && tx.output.first().unwrap().script_pubkey != address.script_pubkey() { + } else if tx.output.len() == 1 + && tx.output.first().unwrap().script_pubkey != address.script_pubkey() + { VaultState::Completed } else { VaultState::Inactive @@ -78,7 +83,9 @@ impl VaultCovenant { } pub(crate) fn from_file(filename: &Option) -> Result { - let filename = filename.clone().unwrap_or("vault_covenant.json".to_string()); + let filename = filename + .clone() + .unwrap_or("vault_covenant.json".to_string()); info!("reading vault covenant from file: {}", filename); let file = std::fs::File::open(filename)?; let covenant: VaultCovenant = serde_json::from_reader(file)?; @@ -86,7 +93,9 @@ impl VaultCovenant { } pub(crate) fn to_file(&self, filename: &Option) -> Result<()> { - let filename = filename.clone().unwrap_or("vault_covenant.json".to_string()); + let filename = filename + .clone() + .unwrap_or("vault_covenant.json".to_string()); info!("writing vault covenant to file: {}", filename); let file = std::fs::File::create(filename)?; serde_json::to_writer(file, self)?; @@ -109,9 +118,12 @@ impl VaultCovenant { } pub(crate) fn get_withdrawal_address(&self) -> Result
{ - Ok( - Address::from_str(self.withdrawal_address.as_ref().ok_or(anyhow!("no withdrawal address"))?)?.require_network(self.network)? - ) + Ok(Address::from_str( + self.withdrawal_address + .as_ref() + .ok_or(anyhow!("no withdrawal address"))?, + )? + .require_network(self.network)?) } pub(crate) fn set_trigger_transaction(&mut self, txn: Option) { @@ -119,7 +131,9 @@ impl VaultCovenant { } pub(crate) fn get_trigger_transaction(&self) -> Result { - self.trigger_transaction.clone().ok_or(anyhow!("no trigger transaction")) + self.trigger_transaction + .clone() + .ok_or(anyhow!("no trigger transaction")) } pub(crate) fn set_state(&mut self, state: VaultState) { @@ -154,13 +168,16 @@ impl VaultCovenant { .expect("finalizing taproot spend info with a NUMS point should always work")) } - pub(crate) fn create_trigger_tx(&self, - fee_paying_utxo: &OutPoint, - fee_paying_output: TxOut, - target_address: &Address, + pub(crate) fn create_trigger_tx( + &self, + fee_paying_utxo: &OutPoint, + fee_paying_output: TxOut, + target_address: &Address, ) -> Result { let mut vault_txin = TxIn { - previous_output: self.current_outpoint.ok_or(anyhow!("no current outpoint"))?, + previous_output: self + .current_outpoint + .ok_or(anyhow!("no current outpoint"))?, ..Default::default() }; let fee_txin = TxIn { @@ -176,7 +193,6 @@ impl VaultCovenant { value: Amount::from_sat(546), }; - let txn = Transaction { lock_time: LockTime::ZERO, version: Version::TWO, @@ -184,7 +200,6 @@ impl VaultCovenant { output: vec![vault_output.clone(), target_output.clone()], }; - let tx_commitment_spec = TxCommitmentSpec { prev_sciptpubkeys: false, prev_amounts: false, @@ -192,7 +207,8 @@ impl VaultCovenant { ..Default::default() }; - let leaf_hash = TapLeafHash::from_script(&vault_trigger_withdrawal(), LeafVersion::TapScript); + let leaf_hash = + TapLeafHash::from_script(&vault_trigger_withdrawal(), LeafVersion::TapScript); let vault_txout = TxOut { script_pubkey: self.address()?.script_pubkey().clone(), value: self.amount, @@ -204,7 +220,6 @@ impl VaultCovenant { leaf_hash, )?; - let mut txn = contract_components.transaction; let witness_components = get_sigmsg_components( &tx_commitment_spec, @@ -225,29 +240,42 @@ impl VaultCovenant { } let mut target_scriptpubkey_buffer = Vec::new(); - target_output.script_pubkey.consensus_encode(&mut target_scriptpubkey_buffer)?; - vault_txin.witness.push(target_scriptpubkey_buffer.as_slice()); + target_output + .script_pubkey + .consensus_encode(&mut target_scriptpubkey_buffer)?; + vault_txin + .witness + .push(target_scriptpubkey_buffer.as_slice()); let mut amount_buffer = Vec::new(); self.amount.consensus_encode(&mut amount_buffer)?; vault_txin.witness.push(amount_buffer.as_slice()); let mut scriptpubkey_buffer = Vec::new(); - vault_output.script_pubkey.consensus_encode(&mut scriptpubkey_buffer)?; + vault_output + .script_pubkey + .consensus_encode(&mut scriptpubkey_buffer)?; vault_txin.witness.push(scriptpubkey_buffer.as_slice()); let mut fee_amount_buffer = Vec::new(); - fee_paying_output.value.consensus_encode(&mut fee_amount_buffer)?; + fee_paying_output + .value + .consensus_encode(&mut fee_amount_buffer)?; vault_txin.witness.push(fee_amount_buffer.as_slice()); let mut fee_scriptpubkey_buffer = Vec::new(); - fee_paying_output.script_pubkey.consensus_encode(&mut fee_scriptpubkey_buffer)?; + fee_paying_output + .script_pubkey + .consensus_encode(&mut fee_scriptpubkey_buffer)?; vault_txin.witness.push(fee_scriptpubkey_buffer.as_slice()); - let computed_signature = - signature_building::compute_signature_from_components(&contract_components.signature_components)?; + let computed_signature = signature_building::compute_signature_from_components( + &contract_components.signature_components, + )?; 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 vault_txin.witness.push(mangled_signature); - vault_txin.witness.push(vault_trigger_withdrawal().to_bytes()); + vault_txin + .witness + .push(vault_trigger_withdrawal().to_bytes()); vault_txin.witness.push( self.taproot_spend_info()? .control_block(&(vault_trigger_withdrawal().clone(), LeafVersion::TapScript)) @@ -259,14 +287,17 @@ impl VaultCovenant { Ok(txn) } - pub(crate) fn create_complete_tx(&self, - fee_paying_utxo: &OutPoint, - fee_paying_output: TxOut, - target_address: &Address, - trigger_tx: &Transaction, + pub(crate) fn create_complete_tx( + &self, + fee_paying_utxo: &OutPoint, + fee_paying_output: TxOut, + target_address: &Address, + trigger_tx: &Transaction, ) -> Result { let mut vault_txin = TxIn { - previous_output: self.current_outpoint.ok_or(anyhow!("no current outpoint"))?, + previous_output: self + .current_outpoint + .ok_or(anyhow!("no current outpoint"))?, sequence: Sequence::from_height(self.timelock_in_blocks), ..Default::default() }; @@ -293,7 +324,10 @@ impl VaultCovenant { ..Default::default() }; - let leaf_hash = TapLeafHash::from_script(&vault_complete_withdrawal(self.timelock_in_blocks), LeafVersion::TapScript); + let leaf_hash = TapLeafHash::from_script( + &vault_complete_withdrawal(self.timelock_in_blocks), + LeafVersion::TapScript, + ); let vault_txout = TxOut { script_pubkey: self.address()?.script_pubkey().clone(), value: self.amount, @@ -305,7 +339,6 @@ impl VaultCovenant { leaf_hash, )?; - let mut txn = contract_components.transaction; let witness_components = get_sigmsg_components( &tx_commitment_spec, @@ -344,36 +377,52 @@ impl VaultCovenant { } let mut locktime_buffer = Vec::new(); - trigger_tx.lock_time.consensus_encode(&mut locktime_buffer)?; + trigger_tx + .lock_time + .consensus_encode(&mut locktime_buffer)?; vault_txin.witness.push(locktime_buffer.as_slice()); - let mut vault_scriptpubkey_buffer = Vec::new(); - self.address()?.script_pubkey().consensus_encode(&mut vault_scriptpubkey_buffer)?; - vault_txin.witness.push(vault_scriptpubkey_buffer.as_slice()); + self.address()? + .script_pubkey() + .consensus_encode(&mut vault_scriptpubkey_buffer)?; + vault_txin + .witness + .push(vault_scriptpubkey_buffer.as_slice()); let mut amount_buffer = Vec::new(); self.amount.consensus_encode(&mut amount_buffer)?; vault_txin.witness.push(amount_buffer.as_slice()); let mut target_scriptpubkey_buffer = Vec::new(); - target_output.script_pubkey.consensus_encode(&mut target_scriptpubkey_buffer)?; - vault_txin.witness.push(target_scriptpubkey_buffer.as_slice()); + target_output + .script_pubkey + .consensus_encode(&mut target_scriptpubkey_buffer)?; + vault_txin + .witness + .push(target_scriptpubkey_buffer.as_slice()); let mut fee_paying_prevout_buffer = Vec::new(); fee_paying_utxo.consensus_encode(&mut fee_paying_prevout_buffer)?; - vault_txin.witness.push(fee_paying_prevout_buffer.as_slice()); + vault_txin + .witness + .push(fee_paying_prevout_buffer.as_slice()); - - let computed_signature = - signature_building::compute_signature_from_components(&contract_components.signature_components)?; + let computed_signature = signature_building::compute_signature_from_components( + &contract_components.signature_components, + )?; 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 vault_txin.witness.push(mangled_signature); - vault_txin.witness.push(vault_complete_withdrawal(self.timelock_in_blocks).to_bytes()); + vault_txin + .witness + .push(vault_complete_withdrawal(self.timelock_in_blocks).to_bytes()); vault_txin.witness.push( self.taproot_spend_info()? - .control_block(&(vault_complete_withdrawal(self.timelock_in_blocks).clone(), LeafVersion::TapScript)) + .control_block(&( + vault_complete_withdrawal(self.timelock_in_blocks).clone(), + LeafVersion::TapScript, + )) .expect("control block should work") .serialize(), ); @@ -383,12 +432,15 @@ impl VaultCovenant { Ok(txn) } - pub(crate) fn create_cancel_tx(&self, - fee_paying_utxo: &OutPoint, - fee_paying_output: TxOut, + pub(crate) fn create_cancel_tx( + &self, + fee_paying_utxo: &OutPoint, + fee_paying_output: TxOut, ) -> Result { let mut vault_txin = TxIn { - previous_output: self.current_outpoint.ok_or(anyhow!("no current outpoint"))?, + previous_output: self + .current_outpoint + .ok_or(anyhow!("no current outpoint"))?, ..Default::default() }; let fee_txin = TxIn { @@ -407,7 +459,6 @@ impl VaultCovenant { output: vec![output.clone()], }; - let tx_commitment_spec = TxCommitmentSpec { prev_sciptpubkeys: false, prev_amounts: false, @@ -415,7 +466,8 @@ impl VaultCovenant { ..Default::default() }; - let leaf_hash = TapLeafHash::from_script(&vault_cancel_withdrawal(), LeafVersion::TapScript); + let leaf_hash = + TapLeafHash::from_script(&vault_cancel_withdrawal(), LeafVersion::TapScript); let vault_txout = TxOut { script_pubkey: self.address()?.script_pubkey().clone(), value: self.amount, @@ -427,7 +479,6 @@ impl VaultCovenant { leaf_hash, )?; - let mut txn = contract_components.transaction; let witness_components = get_sigmsg_components( &tx_commitment_spec, @@ -446,28 +497,36 @@ impl VaultCovenant { ); vault_txin.witness.push(component.as_slice()); } - let computed_signature = - signature_building::compute_signature_from_components(&contract_components.signature_components)?; + let computed_signature = signature_building::compute_signature_from_components( + &contract_components.signature_components, + )?; let mut amount_buffer = Vec::new(); self.amount.consensus_encode(&mut amount_buffer)?; vault_txin.witness.push(amount_buffer.as_slice()); let mut scriptpubkey_buffer = Vec::new(); - output.script_pubkey.consensus_encode(&mut scriptpubkey_buffer)?; + output + .script_pubkey + .consensus_encode(&mut scriptpubkey_buffer)?; vault_txin.witness.push(scriptpubkey_buffer.as_slice()); let mut fee_amount_buffer = Vec::new(); - fee_paying_output.value.consensus_encode(&mut fee_amount_buffer)?; + fee_paying_output + .value + .consensus_encode(&mut fee_amount_buffer)?; vault_txin.witness.push(fee_amount_buffer.as_slice()); let mut fee_scriptpubkey_buffer = Vec::new(); - fee_paying_output.script_pubkey.consensus_encode(&mut fee_scriptpubkey_buffer)?; + fee_paying_output + .script_pubkey + .consensus_encode(&mut fee_scriptpubkey_buffer)?; vault_txin.witness.push(fee_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 vault_txin.witness.push(mangled_signature); - vault_txin.witness.push(vault_cancel_withdrawal().to_bytes()); + vault_txin + .witness + .push(vault_cancel_withdrawal().to_bytes()); vault_txin.witness.push( self.taproot_spend_info()? .control_block(&(vault_cancel_withdrawal().clone(), LeafVersion::TapScript)) diff --git a/src/vault/mod.rs b/src/vault/mod.rs index cb7ba4e..26c283c 100644 --- a/src/vault/mod.rs +++ b/src/vault/mod.rs @@ -1,3 +1,3 @@ +pub(crate) mod contract; pub(crate) mod script; pub(crate) mod signature_building; -pub(crate) mod contract; diff --git a/src/vault/script.rs b/src/vault/script.rs index 1128abe..bf53067 100644 --- a/src/vault/script.rs +++ b/src/vault/script.rs @@ -1,8 +1,10 @@ -use bitcoin::{Script, ScriptBuf, Sequence}; -use bitcoin::opcodes::all::{OP_2DUP, OP_CAT, OP_CHECKSIG, OP_CSV, OP_DROP, OP_DUP, OP_EQUALVERIFY, OP_FROMALTSTACK, OP_HASH256, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK}; -use bitcoin::script::Builder; use crate::vault::signature_building::{BIP0340_CHALLENGE_TAG, DUST_AMOUNT, G_X, TAPSIGHASH_TAG}; - +use bitcoin::opcodes::all::{ + OP_2DUP, OP_CAT, OP_CHECKSIG, OP_CSV, OP_DROP, OP_DUP, OP_EQUALVERIFY, OP_FROMALTSTACK, + OP_HASH256, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK, +}; +use bitcoin::script::Builder; +use bitcoin::{Script, ScriptBuf, Sequence}; pub(crate) fn vault_trigger_withdrawal() -> ScriptBuf { let mut builder = Script::builder(); @@ -16,7 +18,7 @@ pub(crate) fn vault_trigger_withdrawal() -> ScriptBuf { .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 scriptpubkey to the alt stack .push_opcode(OP_TOALTSTACK) // push the first copy of the vault amount to the alt stack .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 @@ -27,12 +29,12 @@ pub(crate) fn vault_trigger_withdrawal() -> ScriptBuf { .push_opcode(OP_CAT) // input index .push_opcode(OP_CAT) // spend type .push_slice(*DUST_AMOUNT) // push the dust amount for the target output - .push_opcode(OP_FROMALTSTACK)// get the target scriptpubkey + .push_opcode(OP_FROMALTSTACK) // get the target scriptpubkey .push_opcode(OP_CAT) // cat the dust amount and the target scriptpubkey - .push_opcode(OP_FROMALTSTACK)// get the output amount + .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_SWAP)// put the outputs in the right order (vault then target) + .push_opcode(OP_SWAP) // put the outputs in the right order (vault then target) .push_opcode(OP_CAT) // cat the vault output and target output together .push_opcode(OP_SHA256) // hash the output .push_opcode(OP_SWAP) // move the hashed encoded outputs below our working sigmsg @@ -40,17 +42,17 @@ pub(crate) fn vault_trigger_withdrawal() -> ScriptBuf { .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 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_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_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 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 @@ -67,7 +69,6 @@ pub(crate) fn vault_trigger_withdrawal() -> ScriptBuf { } pub(crate) fn vault_complete_withdrawal(timelock_in_blocks: u16) -> ScriptBuf { - let mut builder = Script::builder(); // The witness program needs to have the signature components except the outputs, prevouts, // followed by the previous transaction version, inputs, and locktime @@ -75,9 +76,9 @@ pub(crate) fn vault_complete_withdrawal(timelock_in_blocks: u16) -> ScriptBuf { // followed by the fee-paying txout // and finally the mangled signature builder = builder - .push_sequence(Sequence::from_height(timelock_in_blocks)) - .push_opcode(OP_CSV) // check relative timelock on withdrawal - .push_opcode(OP_DROP) // drop the result + .push_sequence(Sequence::from_height(timelock_in_blocks)) + .push_opcode(OP_CSV) // check relative timelock on withdrawal + .push_opcode(OP_DROP) // drop the result .push_opcode(OP_TOALTSTACK) // move pre-computed signature minus last byte to alt stack .push_opcode(OP_TOALTSTACK) // move the fee-paying txout to the alt stack .push_opcode(OP_DUP) // make a second copy of the target scriptpubkey so we can use it later @@ -86,7 +87,7 @@ pub(crate) fn vault_complete_withdrawal(timelock_in_blocks: u16) -> ScriptBuf { .push_opcode(OP_SWAP) // swap the dust amount to the top of the stack .push_opcode(OP_CAT) // consensus-encode the second output for the previous TX .push_opcode(OP_SWAP) // get the vault amount to the top of the stack - .push_opcode(OP_DUP)// make a second copy of the vault amount so we can use it later + .push_opcode(OP_DUP) // make a second copy of the vault amount so we can use it later .push_opcode(OP_FROMALTSTACK) // get the target scriptpubkey .push_opcode(OP_CAT) // cat the target scriptpubkey and the vault amount. .push_opcode(OP_SHA256) // hash the target SPK + vault amount, this is our encoded output commitment @@ -99,34 +100,33 @@ pub(crate) fn vault_complete_withdrawal(timelock_in_blocks: u16) -> ScriptBuf { .push_int(2) // add the number of outputs from the previous TX .push_opcode(OP_SWAP) .push_opcode(OP_CAT) // cat the outputs with their count from the previous TX - .push_opcode(OP_SWAP)// move the outputs down, and the previous TX locktime to the top of the stack + .push_opcode(OP_SWAP) // move the outputs down, and the previous TX locktime to the top of the stack .push_opcode(OP_CAT) // cat the previous TX locktime with the outputs - .push_opcode(OP_CAT)// we had to split the input into two chunks + .push_opcode(OP_CAT) // we had to split the input into two chunks .push_opcode(OP_CAT) // add the inputs .push_opcode(OP_CAT) // add the previous TX version - .push_opcode(OP_HASH256)// hash the whole thing twice to get the TXID + .push_opcode(OP_HASH256) // hash the whole thing twice to get the TXID .push_opcode(OP_FROMALTSTACK) // get the output commitment .push_opcode(OP_SWAP) // move the output commitment below the TXID - .push_opcode(OP_TOALTSTACK)// move the TXID to the alt stack - .push_opcode(OP_TOALTSTACK)// move the output commitment to the alt stack - + .push_opcode(OP_TOALTSTACK) // move the TXID to the alt stack + .push_opcode(OP_TOALTSTACK) // move the output commitment 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 commitment + .push_opcode(OP_FROMALTSTACK) // get the output commitment .push_opcode(OP_SWAP) // move the output commitment below our working sigmsg .push_opcode(OP_CAT) // outputs .push_opcode(OP_CAT) // prev sequences .push_opcode(OP_CAT) // prev scriptpubkeys .push_opcode(OP_CAT) // prev amounts - .push_opcode(OP_FROMALTSTACK)// get the previous TXID from the alt stack + .push_opcode(OP_FROMALTSTACK) // get the previous TXID from the alt stack .push_slice([0x00u8, 0x00u8, 0x00u8, 0x00u8]) // add the output index for the previous TX .push_opcode(OP_FROMALTSTACK) // get the fee-paying txout .push_opcode(OP_CAT) .push_opcode(OP_CAT) // smoosh the fee-paying txout with the previous TXID and output index - .push_opcode(OP_SHA256)// hash the whole thing to get the prevout commitment + .push_opcode(OP_SHA256) // hash the whole thing to get the prevout commitment .push_opcode(OP_SWAP) // move the hashed prevout commitment below our working sigmsg .push_opcode(OP_CAT) // prevouts .push_opcode(OP_CAT) // lock time @@ -148,7 +148,7 @@ pub(crate) fn vault_cancel_withdrawal() -> ScriptBuf { .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 scriptpubkey to the alt stack .push_opcode(OP_TOALTSTACK) // push the first copy of the vault amount to the alt stack .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 @@ -157,7 +157,7 @@ pub(crate) fn vault_cancel_withdrawal() -> ScriptBuf { .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 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 @@ -166,17 +166,17 @@ pub(crate) fn vault_cancel_withdrawal() -> ScriptBuf { .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 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_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_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 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 @@ -230,4 +230,4 @@ pub(crate) fn add_signature_construction_and_check(builder: Builder) -> Builder .push_opcode(OP_CAT) .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) -} \ No newline at end of file +} diff --git a/src/vault/signature_building.rs b/src/vault/signature_building.rs index 9b34f5c..3167ec2 100644 --- a/src/vault/signature_building.rs +++ b/src/vault/signature_building.rs @@ -1,26 +1,24 @@ use anyhow::Result; -use bitcoin::{Amount, Sequence, TapLeafHash, TapSighash, TapSighashType, Transaction, TxOut}; +use bitcoin::absolute::LockTime; use bitcoin::consensus::Encodable; -use bitcoin::hashes::{Hash, HashEngine, sha256}; +use bitcoin::hashes::{sha256, Hash, HashEngine}; use bitcoin::hex::{Case, DisplayHex}; use bitcoin::secp256k1::ThirtyTwoByteHash; use bitcoin::sighash::{Annex, Error}; -use log::debug; -use bitcoin::absolute::LockTime; +use bitcoin::{Amount, Sequence, TapLeafHash, TapSighash, TapSighashType, Transaction, TxOut}; use lazy_static::lazy_static; +use log::debug; use secp256kfun::G; - lazy_static! { pub(crate) static ref G_X: [u8; 32] = G.into_point_with_even_y().0.to_xonly_bytes(); - - pub (crate) static ref TAPSIGHASH_TAG: [u8; 10] = { + pub(crate) static ref TAPSIGHASH_TAG: [u8; 10] = { let mut tag = [0u8; 10]; let val = "TapSighash".as_bytes(); tag.copy_from_slice(val); tag }; - pub (crate) static ref BIP0340_CHALLENGE_TAG: [u8; 17] = { + pub(crate) static ref BIP0340_CHALLENGE_TAG: [u8; 17] = { let mut tag = [0u8; 17]; let val = "BIP0340/challenge".as_bytes(); tag.copy_from_slice(val); @@ -36,7 +34,6 @@ lazy_static! { }; } - #[derive()] pub(crate) struct TxCommitmentSpec { pub(crate) epoch: bool, @@ -154,7 +151,10 @@ pub(crate) fn get_sigmsg_components>( let hash = sha256::Hash::hash(&buffer); hash.consensus_encode(&mut prev_amounts).unwrap(); - debug!("prev_amounts: {:?}", prev_amounts.to_hex_string(Case::Lower)); + debug!( + "prev_amounts: {:?}", + prev_amounts.to_hex_string(Case::Lower) + ); components.push(prev_amounts); } if spec.prev_sciptpubkeys { @@ -163,11 +163,17 @@ pub(crate) fn get_sigmsg_components>( for p in prevouts { p.script_pubkey.consensus_encode(&mut buffer).unwrap(); } - debug!("prev_sciptpubkeys buffer: {:?}", buffer.to_hex_string(Case::Lower)); + debug!( + "prev_sciptpubkeys buffer: {:?}", + buffer.to_hex_string(Case::Lower) + ); let hash = sha256::Hash::hash(&buffer); hash.consensus_encode(&mut prev_sciptpubkeys).unwrap(); - debug!("prev_sciptpubkeys: {:?}", prev_sciptpubkeys.to_hex_string(Case::Lower)); + debug!( + "prev_sciptpubkeys: {:?}", + prev_sciptpubkeys.to_hex_string(Case::Lower) + ); components.push(prev_sciptpubkeys); } if spec.sequences { @@ -206,7 +212,10 @@ pub(crate) fn get_sigmsg_components>( spend_type |= 2u8; } spend_type.consensus_encode(&mut encoded_spend_type)?; - debug!("spend_type: {:?}", encoded_spend_type.to_hex_string(Case::Lower)); + debug!( + "spend_type: {:?}", + encoded_spend_type.to_hex_string(Case::Lower) + ); components.push(encoded_spend_type); } @@ -243,7 +252,10 @@ pub(crate) fn get_sigmsg_components>( previous_output .script_pubkey .consensus_encode(&mut script_pubkey)?; - debug!("input script_pubkey: {:?}", script_pubkey.to_hex_string(Case::Lower)); + debug!( + "input script_pubkey: {:?}", + script_pubkey.to_hex_string(Case::Lower) + ); components.push(script_pubkey); let mut sequence = Vec::new(); txin.sequence.consensus_encode(&mut sequence)?; @@ -286,7 +298,10 @@ pub(crate) fn get_sigmsg_components>( .consensus_encode(&mut enc)?; let hash = sha256::Hash::from_engine(enc); hash.consensus_encode(&mut encoded_single_output)?; - debug!("single_output: {:?}", encoded_single_output.to_hex_string(Case::Lower)); + debug!( + "single_output: {:?}", + encoded_single_output.to_hex_string(Case::Lower) + ); components.push(encoded_single_output); } @@ -303,15 +318,24 @@ pub(crate) fn get_sigmsg_components>( let mut encoded_leaf_hash = Vec::new(); hash.as_byte_array() .consensus_encode(&mut encoded_leaf_hash)?; - debug!("leaf_hash: {:?}", encoded_leaf_hash.to_hex_string(Case::Lower)); + debug!( + "leaf_hash: {:?}", + encoded_leaf_hash.to_hex_string(Case::Lower) + ); components.push(encoded_leaf_hash); let mut encoded_leaf_hash = Vec::new(); KEY_VERSION_0.consensus_encode(&mut encoded_leaf_hash)?; - debug!("leaf_ver: {:?}", encoded_leaf_hash.to_hex_string(Case::Lower)); + debug!( + "leaf_ver: {:?}", + encoded_leaf_hash.to_hex_string(Case::Lower) + ); components.push(encoded_leaf_hash); let mut encoded_leaf_hash = Vec::new(); code_separator_pos.consensus_encode(&mut encoded_leaf_hash)?; - debug!("code_separator_pos: {:?}", encoded_leaf_hash.to_hex_string(Case::Lower)); + debug!( + "code_separator_pos: {:?}", + encoded_leaf_hash.to_hex_string(Case::Lower) + ); components.push(encoded_leaf_hash); } } diff --git a/src/wallet.rs b/src/wallet.rs index 1bef8a8..b2e1017 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use bitcoin::{Address, Amount, Network, OutPoint, Transaction, Txid}; -use bitcoincore_rpc::{Auth, Client, RawTx, RpcApi}; use bitcoincore_rpc::jsonrpc::serde_json::{json, Value}; +use bitcoincore_rpc::{Auth, Client, RawTx, RpcApi}; use log::{debug, info}; use serde::Deserialize; @@ -71,7 +71,7 @@ impl Wallet { let url = match wallet_name { None => format!("http://127.0.0.1:{port}"), - Some(name) => format!("http://127.0.0.1:{}/wallet/{name}", port) + Some(name) => format!("http://127.0.0.1:{}/wallet/{name}", port), }; Client::new(&url, auth.clone()).unwrap() @@ -91,10 +91,7 @@ impl Wallet { }; let txid = self.client.call( "sendrawtransaction", - &[ - json!(tx.raw_hex()), - json!(max_fee_rate), - ], + &[json!(tx.raw_hex()), json!(max_fee_rate)], )?; Ok(txid) } @@ -144,8 +141,12 @@ impl Wallet { } pub(crate) fn sign_tx(&self, tx: &Transaction) -> Result { - let signed = self.client.sign_raw_transaction_with_wallet(tx, None, None)?; - signed.transaction().map_err(|e| anyhow!("signing failed: {}", e)) + let signed = self + .client + .sign_raw_transaction_with_wallet(tx, None, None)?; + signed + .transaction() + .map_err(|e| anyhow!("signing failed: {}", e)) } }