From bf52c0379f1efc083ae0c05c73861cf0e39a16d0 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 18 Apr 2024 20:09:12 +0700 Subject: [PATCH] fix: a withdrawal failure from core should never cause platform to stop --- packages/rs-drive-abci/src/error/execution.rs | 4 ++ .../engine/finalize_block_proposal/v0/mod.rs | 46 ++++++++++--------- .../v0/mod.rs | 46 +++++++++++++++++-- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/packages/rs-drive-abci/src/error/execution.rs b/packages/rs-drive-abci/src/error/execution.rs index 165bbe4f753..dfa77b882cf 100644 --- a/packages/rs-drive-abci/src/error/execution.rs +++ b/packages/rs-drive-abci/src/error/execution.rs @@ -121,4 +121,8 @@ pub enum ExecutionError { /// General Bls Error #[error("bls error: {0}")] BlsErrorGeneral(#[from] BlsError), + + /// General IO Error + #[error("io error: {0}")] + IOError(#[from] std::io::Error), } diff --git a/packages/rs-drive-abci/src/execution/engine/finalize_block_proposal/v0/mod.rs b/packages/rs-drive-abci/src/execution/engine/finalize_block_proposal/v0/mod.rs index 3eccc5a02da..ae78f603616 100644 --- a/packages/rs-drive-abci/src/execution/engine/finalize_block_proposal/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/engine/finalize_block_proposal/v0/mod.rs @@ -204,28 +204,30 @@ where .unsigned_withdrawal_transactions_mut() .drain(); - // Drain signatures instead of cloning - let signatures = commit_info - .threshold_vote_extensions - .drain(..) - .map(|vote_extension| { - let signature_bytes: [u8; 96] = - vote_extension.signature.try_into().map_err(|e| { - AbciError::BadRequestDataSize(format!( - "invalid vote extension signature size: {}", - hex::encode(e) - )) - })?; - - Ok(BLSSignature::from(signature_bytes)) - }) - .collect::>()?; - - self.append_signatures_and_broadcast_withdrawal_transactions( - unsigned_withdrawal_transactions, - signatures, - platform_version, - )?; + if !unsigned_withdrawal_transactions.is_empty() { + // Drain signatures instead of cloning + let signatures = commit_info + .threshold_vote_extensions + .drain(..) + .map(|vote_extension| { + let signature_bytes: [u8; 96] = + vote_extension.signature.try_into().map_err(|e| { + AbciError::BadRequestDataSize(format!( + "invalid vote extension signature size: {}", + hex::encode(e) + )) + })?; + + Ok(BLSSignature::from(signature_bytes)) + }) + .collect::>()?; + + self.append_signatures_and_broadcast_withdrawal_transactions( + unsigned_withdrawal_transactions, + signatures, + platform_version, + )?; + } // Update platform (drive abci) state diff --git a/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs index 035bc11a79c..30b429c6b60 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs @@ -9,8 +9,12 @@ use crate::rpc::core::{ use dashcore_rpc::jsonrpc; use dashcore_rpc::Error as CoreRPCError; use dpp::dashcore::bls_sig_utils::BLSSignature; -use dpp::dashcore::consensus; use dpp::dashcore::transaction::special_transaction::TransactionPayload::AssetUnlockPayloadType; +use dpp::dashcore::{consensus, Txid}; + +use std::fs::{self, File}; +use std::io::Write; +use std::time::{SystemTime, UNIX_EPOCH}; impl Platform where @@ -36,6 +40,8 @@ where unsigned_withdrawal_transactions.len(), ); + let mut transaction_submission_failures = vec![]; + for (mut transaction, signature) in unsigned_withdrawal_transactions.into_iter().zip(signatures) { @@ -65,12 +71,15 @@ where index ); } - // Ignore errors that can happen during blockchain catching. + // Ignore errors that can happen during blockchain synchronization. // They will be logged with dashcore_rpc Err(CoreRPCError::JsonRpc(jsonrpc::error::Error::Rpc(e))) if e.code == CORE_RPC_TX_ALREADY_IN_CHAIN || e.message == CORE_RPC_ERROR_ASSET_UNLOCK_NO_ACTIVE_QUORUM - || e.message == CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED => {} + || e.message == CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED => + { + // These will never work again + } // Errors that can happen if we created invalid tx or Core isn't responding Err(e) => { tracing::error!( @@ -80,12 +89,39 @@ where index, e ); - - return Err(e.into()); + // These errors might allow the state transition to be broadcast in the future + transaction_submission_failures.push((transaction.txid(), tx_bytes)); } } } + store_transaction_failures(transaction_submission_failures) + .map_err(|e| Error::Execution(e.into()))?; + Ok(()) } } + +// Function to handle the storage of transaction submission failures +fn store_transaction_failures(failures: Vec<(Txid, Vec)>) -> std::io::Result<()> { + // Ensure the directory exists + let dir_path = "transaction_submission_failures"; + fs::create_dir_all(dir_path)?; + + // Get the current timestamp + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("expected system time to be after unix epoch time") + .as_secs(); + + for (tx_id, transaction) in failures { + // Create the file name + let file_name = format!("{}/tx_{}_{}.dat", dir_path, timestamp, tx_id); + + // Write the bytes to the file + let mut file = File::create(file_name)?; + file.write_all(&transaction)?; + } + + Ok(()) +}