From 980e4b8e6e1a3f5a7ed58269e13639c62f8773d4 Mon Sep 17 00:00:00 2001 From: atacann Date: Fri, 28 Feb 2025 11:24:09 +0300 Subject: [PATCH] refactor create_txhandlers params --- core/src/builder/sighash.rs | 67 ++--- core/src/builder/transaction/creator.rs | 367 ++++++++++++++++-------- core/src/builder/transaction/mod.rs | 3 +- core/src/builder/transaction/sign.rs | 82 +++--- core/src/errors.rs | 3 + core/src/lib.rs | 2 +- core/src/operator.rs | 3 - core/src/states/mod.rs | 8 +- core/src/states/round.rs | 42 +-- core/src/verifier.rs | 3 - 10 files changed, 348 insertions(+), 232 deletions(-) diff --git a/core/src/builder/sighash.rs b/core/src/builder/sighash.rs index bda9f267a..d17daa1dd 100644 --- a/core/src/builder/sighash.rs +++ b/core/src/builder/sighash.rs @@ -7,7 +7,8 @@ use crate::builder::transaction::deposit_signature_owner::EntityType; use crate::builder::transaction::{ - create_txhandlers, DepositData, OperatorData, ReimburseDbCache, TransactionType, TxHandler, + create_txhandlers, ContractContext, DepositData, OperatorData, ReimburseDbCache, + TransactionType, TxHandler, }; use crate::config::BridgeConfig; use crate::database::Database; @@ -133,23 +134,16 @@ pub fn create_nofn_sighash_stream( Err(BridgeError::NotEnoughOperators)?; } - for (operator_idx, (operator_xonly_pk, operator_reimburse_address, collateral_funding_outpoint)) in + for (operator_idx, _) in operators.iter().enumerate() { // need to create new TxHandlerDbData for each operator - let mut tx_db_data = ReimburseDbCache::new(db.clone(), operator_idx as u32, deposit_data.clone(), &config); + let mut tx_db_data = ReimburseDbCache::new_for_deposit(db.clone(), operator_idx as u32, deposit_data.clone(), config.protocol_paramset()); let mut last_ready_to_reimburse: Option = None; - let operator_data = OperatorData { - xonly_pk: *operator_xonly_pk, - reimburse_addr: operator_reimburse_address.clone(), - collateral_funding_outpoint: *collateral_funding_outpoint, - }; - - // For each sequential_collateral_tx, we have multiple kickoff_utxos as the connectors. - for round_iidx in 0..paramset.num_round_txs { + for round_idx in 0..paramset.num_round_txs { // For each kickoff_utxo, it connnects to a kickoff_tx that results in // either start_happy_reimburse_tx // or challenge_tx, which forces the operator to initiate BitVM sequence @@ -157,17 +151,22 @@ pub fn create_nofn_sighash_stream( // If the operator is honest, the sequence will end with the operator being able to send the reimburse_tx. // Otherwise, by using the disprove_tx, the operator's sequential_collateral_tx burn connector will be burned. for kickoff_idx in 0..paramset.num_kickoffs_per_round { - let partial = PartialSignatureInfo::new(operator_idx, round_iidx, kickoff_idx); + let partial = PartialSignatureInfo::new(operator_idx, round_idx, kickoff_idx); - let mut txhandlers = create_txhandlers( - nofn_xonly_pk, - TransactionType::AllNeededForDeposit, + let context = ContractContext::new_context_for_kickoffs( KickoffId { operator_idx: operator_idx as u32, - round_idx: round_iidx as u32, + round_idx: round_idx as u32, kickoff_idx: kickoff_idx as u32, }, - operator_data.clone(), + deposit_data.clone(), + config.protocol_paramset(), + nofn_xonly_pk + ); + + let mut txhandlers = create_txhandlers( + TransactionType::AllNeededForDeposit, + context, last_ready_to_reimburse, &mut tx_db_data, ).await?; @@ -200,45 +199,37 @@ pub fn create_nofn_sighash_stream( pub fn create_operator_sighash_stream( db: Database, operator_idx: usize, - collateral_funding_outpoint: OutPoint, - operator_reimburse_addr: Address, - operator_xonly_pk: XOnlyPublicKey, config: BridgeConfig, deposit_data: DepositData, nofn_xonly_pk: XOnlyPublicKey, ) -> impl Stream> { try_stream! { - let operator_data = OperatorData { - xonly_pk: operator_xonly_pk, - reimburse_addr: operator_reimburse_addr, - collateral_funding_outpoint, - }; + let mut tx_db_data = ReimburseDbCache::new_for_deposit(db.clone(), operator_idx as u32, deposit_data.clone(), config.protocol_paramset()); - let mut tx_db_data = ReimburseDbCache::new( - db.clone(), - operator_idx as u32, - deposit_data.clone(), - &config, - ); let paramset = config.protocol_paramset(); - let mut last_reimburse_generator: Option = None; + let mut last_ready_to_reimburse: Option = None; // For each round_tx, we have multiple kickoff_utxos as the connectors. for round_idx in 0..paramset.num_round_txs { for kickoff_idx in 0..paramset.num_kickoffs_per_round { let partial = PartialSignatureInfo::new(operator_idx, round_idx, kickoff_idx); - let mut txhandlers = create_txhandlers( - nofn_xonly_pk, - TransactionType::AllNeededForDeposit, + let context = ContractContext::new_context_for_kickoffs( KickoffId { operator_idx: operator_idx as u32, round_idx: round_idx as u32, kickoff_idx: kickoff_idx as u32, }, - operator_data.clone(), - last_reimburse_generator, + deposit_data.clone(), + config.protocol_paramset(), + nofn_xonly_pk + ); + + let mut txhandlers = create_txhandlers( + TransactionType::AllNeededForDeposit, + context, + last_ready_to_reimburse, &mut tx_db_data, ).await?; @@ -253,7 +244,7 @@ pub fn create_operator_sighash_stream( if sum != config.get_num_required_operator_sigs_per_kickoff() { Err(BridgeError::OperatorSighashMismatch(config.get_num_required_operator_sigs_per_kickoff(), sum))?; } - last_reimburse_generator = txhandlers.remove(&TransactionType::Reimburse); + last_ready_to_reimburse = txhandlers.remove(&TransactionType::ReadyToReimburse); } } } diff --git a/core/src/builder/transaction/creator.rs b/core/src/builder/transaction/creator.rs index 9bcffa362..d87f034a4 100644 --- a/core/src/builder/transaction/creator.rs +++ b/core/src/builder/transaction/creator.rs @@ -1,5 +1,5 @@ -use crate::actor::Actor; use crate::actor::WinternitzDerivationPath::WatchtowerChallenge; +use crate::actor::{self, Actor}; use crate::builder::script::{SpendableScript, WinternitzCommit}; use crate::builder::transaction::{ create_assert_timeout_txhandlers, create_challenge_timeout_txhandler, create_kickoff_txhandler, @@ -64,10 +64,8 @@ impl KickoffWinternitzKeys { pub struct ReimburseDbCache { pub db: Database, pub operator_idx: u32, - pub deposit_data: DepositData, + pub deposit_data: Option, pub paramset: &'static ProtocolParamset, - actor_secret_key: SecretKey, - winternitz_secret_key: Option, /// watchtower challenge addresses watchtower_challenge_hashes: Option>, /// winternitz keys to sign the kickoff tx with the blockhash @@ -78,51 +76,93 @@ pub struct ReimburseDbCache { bitvm_disprove_root_hash: Option<[u8; 32]>, /// Public hashes to acknowledge watchtower challenges challenge_ack_hashes: Option>, + /// operator data + operator_data: Option, } impl ReimburseDbCache { - pub fn new( + /// Creates a db cache that can be used to create txhandlers for a specific operator and deposit + pub fn new_for_deposit( db: Database, operator_idx: u32, deposit_data: DepositData, - config: &BridgeConfig, + paramset: &'static ProtocolParamset, ) -> Self { Self { db, operator_idx, - deposit_data, - paramset: config.protocol_paramset(), - actor_secret_key: config.secret_key, - winternitz_secret_key: config.winternitz_secret_key, + deposit_data: Some(deposit_data), + paramset, watchtower_challenge_hashes: None, kickoff_winternitz_keys: None, bitvm_assert_addr: None, bitvm_disprove_root_hash: None, challenge_ack_hashes: None, + operator_data: None, } } - pub async fn watchtower_challenge_hash(&mut self) -> Result<&[[u8; 32]], BridgeError> { - match self.watchtower_challenge_hashes { - Some(ref addr) => Ok(addr), + + /// Creates a db cache that can be used to create txhandlers for a specific operator and collateral chain + pub fn new_for_rounds( + db: Database, + operator_idx: u32, + paramset: &'static ProtocolParamset, + ) -> Self { + Self { + db, + operator_idx, + deposit_data: None, + paramset, + watchtower_challenge_hashes: None, + kickoff_winternitz_keys: None, + bitvm_assert_addr: None, + bitvm_disprove_root_hash: None, + challenge_ack_hashes: None, + operator_data: None, + } + } + + pub async fn get_operator_data(&mut self) -> Result<&OperatorData, BridgeError> { + match self.operator_data { + Some(ref data) => Ok(data), None => { - // Get all watchtower challenge addresses for the operator. - let watchtower_challenge_addr = (0..self.paramset.num_watchtowers) - .map(|i| { - self.db.get_watchtower_challenge_hash( - None, - i as u32, - self.operator_idx, - self.deposit_data.deposit_outpoint, - ) - }) - .collect::>(); - self.watchtower_challenge_hashes = - Some(futures::future::try_join_all(watchtower_challenge_addr).await?); - Ok(self - .watchtower_challenge_hashes - .as_ref() - .expect("Inserted before")) + self.operator_data = Some( + self.db + .get_operator(None, self.operator_idx as i32) + .await? + .ok_or(BridgeError::OperatorNotFound(self.operator_idx))?, + ); + Ok(self.operator_data.as_ref().expect("Inserted before")) + } + } + } + + pub async fn watchtower_challenge_hash(&mut self) -> Result<&[[u8; 32]], BridgeError> { + if let Some(deposit_data) = &self.deposit_data { + match self.watchtower_challenge_hashes { + Some(ref addr) => Ok(addr), + None => { + // Get all watchtower challenge addresses for the operator. + let watchtower_challenge_addr = (0..self.paramset.num_watchtowers) + .map(|i| { + self.db.get_watchtower_challenge_hash( + None, + i as u32, + self.operator_idx, + deposit_data.deposit_outpoint, + ) + }) + .collect::>(); + self.watchtower_challenge_hashes = + Some(futures::future::try_join_all(watchtower_challenge_addr).await?); + Ok(self + .watchtower_challenge_hashes + .as_ref() + .expect("Inserted before")) + } } + } else { + Err(BridgeError::InsufficientContext) } } @@ -147,77 +187,91 @@ impl ReimburseDbCache { } pub async fn get_bitvm_assert_hash(&mut self) -> Result<&[[u8; 32]], BridgeError> { - match self.bitvm_assert_addr { - Some(ref addr) => Ok(addr), - None => { - let (assert_addr, bitvm_hash) = self - .db - .get_bitvm_setup( - None, - self.operator_idx as i32, - self.deposit_data.deposit_outpoint, - ) - .await? - .ok_or(BridgeError::BitvmSetupNotFound( - self.operator_idx as i32, - self.deposit_data.deposit_outpoint.txid, - ))?; - self.bitvm_assert_addr = Some(assert_addr); - self.bitvm_disprove_root_hash = Some(bitvm_hash); - Ok(self.bitvm_assert_addr.as_ref().expect("Inserted before")) - } - } - } - - pub async fn get_challenge_ack_hashes(&mut self) -> Result<&[PublicHash], BridgeError> { - match self.challenge_ack_hashes { - Some(ref hashes) => Ok(hashes), - None => { - self.challenge_ack_hashes = Some( - self.db - .get_operators_challenge_ack_hashes( + if let Some(deposit_data) = &self.deposit_data { + match self.bitvm_assert_addr { + Some(ref addr) => Ok(addr), + None => { + let (assert_addr, bitvm_hash) = self + .db + .get_bitvm_setup( None, self.operator_idx as i32, - self.deposit_data.deposit_outpoint, + deposit_data.deposit_outpoint, ) .await? - .ok_or(BridgeError::WatchtowerPublicHashesNotFound( + .ok_or(BridgeError::BitvmSetupNotFound( self.operator_idx as i32, - self.deposit_data.deposit_outpoint.txid, - ))?, - ); - Ok(self.challenge_ack_hashes.as_ref().expect("Inserted before")) + deposit_data.deposit_outpoint.txid, + ))?; + self.bitvm_assert_addr = Some(assert_addr); + self.bitvm_disprove_root_hash = Some(bitvm_hash); + Ok(self.bitvm_assert_addr.as_ref().expect("Inserted before")) + } } + } else { + Err(BridgeError::InsufficientContext) + } + } + + pub async fn get_challenge_ack_hashes(&mut self) -> Result<&[PublicHash], BridgeError> { + if let Some(deposit_data) = &self.deposit_data { + match self.challenge_ack_hashes { + Some(ref hashes) => Ok(hashes), + None => { + self.challenge_ack_hashes = Some( + self.db + .get_operators_challenge_ack_hashes( + None, + self.operator_idx as i32, + deposit_data.deposit_outpoint, + ) + .await? + .ok_or(BridgeError::WatchtowerPublicHashesNotFound( + self.operator_idx as i32, + deposit_data.deposit_outpoint.txid, + ))?, + ); + Ok(self.challenge_ack_hashes.as_ref().expect("Inserted before")) + } + } + } else { + Err(BridgeError::InsufficientContext) } } pub async fn get_bitvm_disprove_root_hash(&mut self) -> Result<&[u8; 32], BridgeError> { - match self.bitvm_disprove_root_hash { - Some(ref hash) => Ok(hash), - None => { - let bitvm_hash = self - .db - .get_bitvm_root_hash( - None, - self.operator_idx as i32, - self.deposit_data.deposit_outpoint, - ) - .await? - .ok_or(BridgeError::BitvmSetupNotFound( - self.operator_idx as i32, - self.deposit_data.deposit_outpoint.txid, - ))?; - self.bitvm_disprove_root_hash = Some(bitvm_hash); - Ok(self - .bitvm_disprove_root_hash - .as_ref() - .expect("Inserted before")) + if let Some(deposit_data) = &self.deposit_data { + match self.bitvm_disprove_root_hash { + Some(ref hash) => Ok(hash), + None => { + let bitvm_hash = self + .db + .get_bitvm_root_hash( + None, + self.operator_idx as i32, + deposit_data.deposit_outpoint, + ) + .await? + .ok_or(BridgeError::BitvmSetupNotFound( + self.operator_idx as i32, + deposit_data.deposit_outpoint.txid, + ))?; + self.bitvm_disprove_root_hash = Some(bitvm_hash); + Ok(self + .bitvm_disprove_root_hash + .as_ref() + .expect("Inserted before")) + } } + } else { + Err(BridgeError::InsufficientContext) } } } -struct ContractContext { +#[derive(Debug, Clone)] +/// Context for a single operator and round, and optionally a single deposit +pub struct ContractContext { /// required operator_idx: u32, round_idx: u32, @@ -226,46 +280,90 @@ struct ContractContext { nofn_xonly_pk: Option, kickoff_idx: Option, deposit_data: Option, + signer: Option, + // TODO: why different winternitz_secret_key??? } -#[tracing::instrument(skip_all, err, fields(deposit_data = ?db_cache.deposit_data, txtype = ?transaction_type, ?kickoff_id))] +impl ContractContext { + /// Contains all necessary context for creating txhandlers for a specific operator and collateral chain + pub fn new_context_for_rounds( + operator_idx: u32, + round_idx: u32, + paramset: &'static ProtocolParamset, + ) -> Self { + Self { + operator_idx, + round_idx, + paramset, + nofn_xonly_pk: None, + kickoff_idx: None, + deposit_data: None, + signer: None, + } + } + + /// Contains all necessary context for creating txhandlers for a specific operator, kickoff utxo, and a deposit + pub fn new_context_for_kickoffs( + kickoff_id: KickoffId, + deposit_data: DepositData, + paramset: &'static ProtocolParamset, + nofn_xonly_pk: XOnlyPublicKey, + ) -> Self { + Self { + operator_idx: kickoff_id.operator_idx, + round_idx: kickoff_id.round_idx, + paramset, + nofn_xonly_pk: Some(nofn_xonly_pk), + kickoff_idx: Some(kickoff_id.kickoff_idx), + deposit_data: Some(deposit_data), + signer: None, + } + } + + /// Contains all necessary context for creating txhandlers for a specific operator, kickoff utxo, and a deposit + /// Additionally holds signer of an actor that can generate the actual winternitz public keys. + pub fn new_context_for_asserts( + kickoff_id: KickoffId, + deposit_data: DepositData, + paramset: &'static ProtocolParamset, + nofn_xonly_pk: XOnlyPublicKey, + signer: Actor, + ) -> Self { + Self { + operator_idx: kickoff_id.operator_idx, + round_idx: kickoff_id.round_idx, + paramset, + nofn_xonly_pk: Some(nofn_xonly_pk), + kickoff_idx: Some(kickoff_id.kickoff_idx), + deposit_data: Some(deposit_data), + signer: Some(signer), + } + } +} + +#[tracing::instrument(skip_all, err, fields(deposit_data = ?db_cache.deposit_data, txtype = ?transaction_type, ?context))] pub async fn create_txhandlers( - nofn_xonly_pk: XOnlyPublicKey, transaction_type: TransactionType, - kickoff_id: KickoffId, - operator_data: OperatorData, + context: ContractContext, prev_ready_to_reimburse: Option, db_cache: &mut ReimburseDbCache, ) -> Result, BridgeError> { let mut txhandlers = BTreeMap::new(); - let ReimburseDbCache { - deposit_data, - paramset, - actor_secret_key, - winternitz_secret_key, - .. - } = db_cache.clone(); - - // Create move_tx handler. This is unique for each deposit tx. - // Technically this can be also given as a parameter because it is calculated repeatedly in streams - let move_txhandler = builder::transaction::create_move_to_vault_txhandler( - deposit_data.deposit_outpoint, - deposit_data.evm_address, - &deposit_data.recovery_taproot_address, - nofn_xonly_pk, - paramset.user_takes_after, - paramset.bridge_amount, - paramset.network, - )?; - txhandlers.insert(move_txhandler.get_transaction_type(), move_txhandler); + let ReimburseDbCache { paramset, .. } = db_cache.clone(); + let operator_data = db_cache.get_operator_data().await?.clone(); let kickoff_winternitz_keys = db_cache.get_kickoff_winternitz_keys().await?; + let ContractContext { + operator_idx, + round_idx, + .. + } = context; // create round tx, ready to reimburse tx, and unspent kickoff txs let round_txhandlers = create_round_txhandlers( paramset, - kickoff_id.round_idx as usize, + round_idx as usize, &operator_data, kickoff_winternitz_keys, prev_ready_to_reimburse, @@ -275,6 +373,17 @@ pub async fn create_txhandlers( txhandlers.insert(round_txhandler.get_transaction_type(), round_txhandler); } + if matches!( + transaction_type, + TransactionType::Round + | TransactionType::ReadyToReimburse + | TransactionType::UnspentKickoff(_) + ) { + // return if only one of the collateral tx's were requested + // do not continue as we might not have the necessary context for the remaining tx's + return Ok(txhandlers); + } + // get the next round txhandler (because reimburse connectors will be in it) let next_round_txhandler = create_round_txhandler( operator_data.xonly_pk, @@ -282,10 +391,37 @@ pub async fn create_txhandlers( get_txhandler(&txhandlers, TransactionType::ReadyToReimburse)? .get_spendable_output(0)?, ), - kickoff_winternitz_keys.get_keys_for_round(kickoff_id.round_idx as usize + 1), + kickoff_winternitz_keys.get_keys_for_round(round_idx as usize + 1), paramset, )?; + let nofn_xonly_pk = context + .nofn_xonly_pk + .ok_or(BridgeError::InsufficientContext)?; + let kickoff_id = KickoffId { + operator_idx, + round_idx, + kickoff_idx: context + .kickoff_idx + .ok_or(BridgeError::InsufficientContext)?, + }; + let deposit_data = context + .deposit_data + .ok_or(BridgeError::InsufficientContext)?; + + // Create move_tx handler. This is unique for each deposit tx. + // Technically this can be also given as a parameter because it is calculated repeatedly in streams + let move_txhandler = builder::transaction::create_move_to_vault_txhandler( + deposit_data.deposit_outpoint, + deposit_data.evm_address, + &deposit_data.recovery_taproot_address, + nofn_xonly_pk, + paramset.user_takes_after, + paramset.bridge_amount, + paramset.network, + )?; + txhandlers.insert(move_txhandler.get_transaction_type(), move_txhandler); + let num_asserts = utils::COMBINED_ASSERT_DATA.num_steps.len(); let public_hashes = db_cache.get_challenge_ack_hashes().await?.to_vec(); let watchtower_challenge_hashes = db_cache.watchtower_challenge_hash().await?.to_vec(); @@ -293,7 +429,10 @@ pub async fn create_txhandlers( let kickoff_txhandler = if let TransactionType::MiniAssert(_) = transaction_type { // create scripts if any mini assert tx is specifically requested as it needs // the actual scripts to be able to spend - let actor = Actor::new(actor_secret_key, winternitz_secret_key, paramset.network); + let actor = context + .signer + .clone() + .ok_or(BridgeError::InsufficientContext)?; let mut assert_scripts: Vec> = Vec::with_capacity(utils::COMBINED_ASSERT_DATA.num_steps.len()); @@ -435,7 +574,7 @@ pub async fn create_txhandlers( paramset, ); - let actor = Actor::new(actor_secret_key, winternitz_secret_key, paramset.network); + let actor = context.signer.ok_or(BridgeError::InsufficientContext)?; let public_key = actor.derive_winternitz_pk(path)?; let watchtower_challenge_txhandler = diff --git a/core/src/builder/transaction/mod.rs b/core/src/builder/transaction/mod.rs index 8a3b07422..009d6f25b 100644 --- a/core/src/builder/transaction/mod.rs +++ b/core/src/builder/transaction/mod.rs @@ -30,7 +30,8 @@ use bitcoin::{Address, Amount, OutPoint, ScriptBuf, TxOut, XOnlyPublicKey}; // Exports to the outside pub use crate::builder::transaction::txhandler::*; pub use creator::{ - create_round_txhandlers, create_txhandlers, KickoffWinternitzKeys, ReimburseDbCache, + create_round_txhandlers, create_txhandlers, ContractContext, KickoffWinternitzKeys, + ReimburseDbCache, }; pub use operator_reimburse::create_payout_txhandler; pub use txhandler::Unsigned; diff --git a/core/src/builder/transaction/sign.rs b/core/src/builder/transaction/sign.rs index b6c019e6b..f88654b3f 100644 --- a/core/src/builder/transaction/sign.rs +++ b/core/src/builder/transaction/sign.rs @@ -10,6 +10,8 @@ use crate::watchtower::Watchtower; use crate::{builder, utils}; use bitcoin::XOnlyPublicKey; +use super::ContractContext; + pub struct TransactionRequestData { pub deposit_data: DepositData, pub transaction_type: TransactionType, @@ -32,34 +34,25 @@ pub async fn create_and_sign_txs( transaction_data: TransactionRequestData, block_hash: Option<[u8; 20]>, //to sign kickoff ) -> Result, BridgeError> { - // get operator data - let operator_data = db - .get_operator(None, transaction_data.kickoff_id.operator_idx as i32) - .await? - .ok_or(BridgeError::OperatorNotFound( - transaction_data.kickoff_id.operator_idx, - ))?; + let context = ContractContext::new_context_for_kickoffs( + transaction_data.kickoff_id, + transaction_data.deposit_data.clone(), + config.protocol_paramset(), + nofn_xonly_pk, + ); - let start_time = std::time::Instant::now(); let txhandlers = builder::transaction::create_txhandlers( - nofn_xonly_pk, transaction_data.transaction_type, - transaction_data.kickoff_id, - operator_data, + context, None, - &mut ReimburseDbCache::new( + &mut &mut ReimburseDbCache::new_for_deposit( db.clone(), transaction_data.kickoff_id.operator_idx, transaction_data.deposit_data.clone(), - &config, + config.protocol_paramset(), ), ) .await?; - tracing::trace!( - "create_txhandlers for {:?} finished in {:?}", - transaction_data.transaction_type, - start_time.elapsed() - ); // signatures saved during deposit let deposit_sigs_query = db @@ -148,35 +141,27 @@ impl Watchtower { { return Err(BridgeError::InvalidWatchtowerChallengeData); } - // get operator data - let operator_data = self - .db - .get_operator(None, transaction_data.kickoff_id.operator_idx as i32) - .await? - .ok_or(BridgeError::OperatorNotFound( - transaction_data.kickoff_id.operator_idx, - ))?; - let start_time = std::time::Instant::now(); - let mut txhandlers = builder::transaction::create_txhandlers( + let context = ContractContext::new_context_for_asserts( + transaction_data.kickoff_id, + transaction_data.deposit_data.clone(), + self.config.protocol_paramset(), nofn_xonly_pk, + self.signer.clone(), + ); + + let mut txhandlers = builder::transaction::create_txhandlers( TransactionType::WatchtowerChallenge(self.config.index as usize), - transaction_data.kickoff_id, - operator_data, + context, None, - &mut ReimburseDbCache::new( + &mut &mut ReimburseDbCache::new_for_deposit( self.db.clone(), transaction_data.kickoff_id.operator_idx, transaction_data.deposit_data.clone(), - &self.config, + self.config.protocol_paramset(), ), ) .await?; - tracing::trace!( - "create_txhandlers for {:?} finished in {:?}", - TransactionType::WatchtowerChallenge(self.config.index as usize), - start_time.elapsed() - ); let mut requested_txhandler = txhandlers .remove(&transaction_data.transaction_type) @@ -204,26 +189,23 @@ impl Operator { nofn_xonly_pk: XOnlyPublicKey, assert_data: AssertRequestData, ) -> Result { - // get operator data - let operator_data = self - .db - .get_operator(None, assert_data.kickoff_id.operator_idx as i32) - .await? - .ok_or(BridgeError::OperatorNotFound( - assert_data.kickoff_id.operator_idx, - ))?; + let context = ContractContext::new_context_for_asserts( + assert_data.kickoff_id, + assert_data.deposit_data.clone(), + self.config.protocol_paramset(), + nofn_xonly_pk, + self.signer.clone(), + ); let mut txhandlers = builder::transaction::create_txhandlers( - nofn_xonly_pk, TransactionType::MiniAssert(0), - assert_data.kickoff_id, - operator_data, + context, None, - &mut ReimburseDbCache::new( + &mut &mut ReimburseDbCache::new_for_deposit( self.db.clone(), assert_data.kickoff_id.operator_idx, assert_data.deposit_data.clone(), - &self.config, + self.config.protocol_paramset(), ), ) .await?; diff --git a/core/src/errors.rs b/core/src/errors.rs index a9064d200..f73614c39 100644 --- a/core/src/errors.rs +++ b/core/src/errors.rs @@ -172,6 +172,9 @@ pub enum BridgeError { #[error("Error while parsing a musig member: {0}")] MusigParseError(#[from] musig::ParseError), + #[error("Insufficient Context data for the requested TxHandler")] + InsufficientContext, + #[error("NoncesNotFound")] NoncesNotFound, diff --git a/core/src/lib.rs b/core/src/lib.rs index ee2062ebe..3a75f642e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -23,11 +23,11 @@ pub mod musig2; pub mod operator; pub mod rpc; pub mod servers; +pub mod states; pub mod tx_sender; pub mod utils; pub mod verifier; pub mod watchtower; -pub mod states; #[cfg(test)] pub mod test; diff --git a/core/src/operator.rs b/core/src/operator.rs index 60047c906..e179685c4 100644 --- a/core/src/operator.rs +++ b/core/src/operator.rs @@ -194,9 +194,6 @@ impl Operator { let mut sighash_stream = Box::pin(create_operator_sighash_stream( self.db.clone(), self.idx, - self.collateral_funding_outpoint, - self.reimburse_addr.clone(), - self.signer.xonly_public_key, self.config.clone(), deposit_id, self.nofn_xonly_pk, diff --git a/core/src/states/mod.rs b/core/src/states/mod.rs index f3baeaee4..e13104483 100644 --- a/core/src/states/mod.rs +++ b/core/src/states/mod.rs @@ -1,4 +1,6 @@ -use crate::builder::transaction::{DepositData, OperatorData, TransactionType, TxHandler}; +use crate::builder::transaction::{ + ContractContext, DepositData, OperatorData, TransactionType, TxHandler, +}; use crate::config::protocol::ProtocolParamset; use crate::database::Database; use crate::errors::BridgeError; @@ -95,8 +97,8 @@ pub trait Owner: Send + Sync + Clone + Default { async fn handle_duty(&self, duty: Duty) -> Result<(), BridgeError>; async fn create_txhandlers( &self, - kickoff_id: KickoffId, - deposit_data: Option, + tx_type: TransactionType, + contract_context: ContractContext, ) -> Result, BridgeError>; } diff --git a/core/src/states/round.rs b/core/src/states/round.rs index 0b1932a99..0df5f7e87 100644 --- a/core/src/states/round.rs +++ b/core/src/states/round.rs @@ -1,12 +1,9 @@ -use std::collections::{HashMap, HashSet}; - -use bitcoin::{hashes::Hash, Txid}; use statig::prelude::*; +use std::collections::{HashMap, HashSet}; use crate::{ - builder::transaction::{OperatorData, TransactionType}, + builder::transaction::{ContractContext, OperatorData, TransactionType}, errors::BridgeError, - rpc::clementine::KickoffId, states::Duty, }; @@ -24,7 +21,6 @@ pub struct RoundStateMachine { pub(crate) matchers: HashMap, operator_data: OperatorData, operator_idx: u32, - current_txid: Txid, pub(crate) dirty: bool, phantom: std::marker::PhantomData, } @@ -52,7 +48,6 @@ impl RoundStateMachine { matchers: HashMap::new(), operator_data, operator_idx, - current_txid: Txid::all_zeros(), dirty: false, phantom: std::marker::PhantomData, } @@ -111,7 +106,7 @@ impl RoundStateMachine { RoundEvent::ReadyToReimburseSent { round_idx } => { Transition(State::ready_to_reimburse(*round_idx)) } - _ => Super, + _ => Handled, } } @@ -146,7 +141,15 @@ impl RoundStateMachine { context .capture_error(async |context| { self.matchers = HashMap::new(); - let mut txhandlers = context.owner.create_txhandlers().await?; + let contract_context = ContractContext::new_context_for_rounds( + self.operator_idx, + *round_idx, + context.paramset, + ); + let mut txhandlers = context + .owner + .create_txhandlers(TransactionType::Round, contract_context) + .await?; let round_txhandler = txhandlers .remove(&TransactionType::Round) .ok_or(BridgeError::TxHandlerNotFound(TransactionType::Round))?; @@ -161,7 +164,6 @@ impl RoundStateMachine { round_idx: *round_idx, }, ); - self.current_txid = *round_txhandler.get_txid(); for idx in 1..context.paramset.num_kickoffs_per_round + 1 { self.matchers.insert( Matcher::SpentUtxo( @@ -188,7 +190,7 @@ impl RoundStateMachine { RoundEvent::RoundSent { round_idx } => { Transition(State::round_tx(*round_idx, HashSet::new())) } - _ => Super, + _ => Handled, } } @@ -201,14 +203,16 @@ impl RoundStateMachine { context .capture_error(async |context| { self.matchers = HashMap::new(); - let mut txhandlers = context.owner.create_txhandlers().await?; - let ready_to_reimburse_txhandler = txhandlers - .remove(&TransactionType::ReadyToReimburse) - .ok_or(BridgeError::TxHandlerNotFound( - TransactionType::ReadyToReimburse, - ))?; - self.current_txid = *ready_to_reimburse_txhandler.get_txid(); - let next_round_txhandlers = context.owner.create_txhandlers().await?; + // get next rounds Round tx + let contract_context = ContractContext::new_context_for_rounds( + self.operator_idx, + *round_idx + 1, + context.paramset, + ); + let next_round_txhandlers = context + .owner + .create_txhandlers(TransactionType::Round, contract_context) + .await?; let next_round_txid = next_round_txhandlers .get(&TransactionType::Round) .ok_or(BridgeError::TxHandlerNotFound(TransactionType::Round))? diff --git a/core/src/verifier.rs b/core/src/verifier.rs index cba01c85a..eb1adc186 100644 --- a/core/src/verifier.rs +++ b/core/src/verifier.rs @@ -597,9 +597,6 @@ impl Verifier { let mut sighash_stream = pin!(create_operator_sighash_stream( self.db.clone(), operator_idx, - *collateral_outpoint, - reimburse_addr.clone(), - *op_xonly_pk, self.config.clone(), DepositData { deposit_outpoint,