Skip to content

fix(platform)!: a withdrawal failure from core should never cause platform to stop #1821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/rs-drive-abci/src/error/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<_, AbciError>>()?;

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::<Result<_, AbciError>>()?;

self.append_signatures_and_broadcast_withdrawal_transactions(
unsigned_withdrawal_transactions,
signatures,
platform_version,
)?;
}

// Update platform (drive abci) state

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<C> Platform<C>
where
Expand All @@ -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)
{
Expand Down Expand Up @@ -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!(
Expand All @@ -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<u8>)>) -> 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(())
}