Skip to content

Commit

Permalink
feat: Retry certain failed transactions against latest microblocks
Browse files Browse the repository at this point in the history
  • Loading branch information
jbencin committed Jun 30, 2023
1 parent 59bdfa6 commit b0dce01
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 20 deletions.
68 changes: 51 additions & 17 deletions src/chainstate/stacks/db/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6373,29 +6373,63 @@ impl StacksChainState {

match res {
Ok(x) => Ok(x),
Err(MemPoolRejection::BadNonces(mismatch_error)) => {
// try again, but against the _unconfirmed_ chain tip, if we
// (a) have one, and (b) the expected nonce is less than the given one.
if self.unconfirmed_state.is_some()
&& mismatch_error.expected < mismatch_error.actual
{
debug!("Transaction {} is unminable in the confirmed chain tip due to nonce {} != {}; trying the unconfirmed chain tip",
&tx.txid(), mismatch_error.expected, mismatch_error.actual);
Err(e) => match e {
MemPoolRejection::BadNonces(mismatch_error) => {
// try again, but against the _unconfirmed_ chain tip, if we
// (a) have one, and (b) the expected nonce is less than the given one.
if self.unconfirmed_state.is_some()
&& mismatch_error.expected < mismatch_error.actual
{
debug!("Transaction {} is unminable in the confirmed chain tip due to nonce {} != {}; trying the unconfirmed chain tip",
&tx.txid(), mismatch_error.expected, mismatch_error.actual);
self.with_read_only_unconfirmed_clarity_tx(&NULL_BURN_STATE_DB, |conn| {
StacksChainState::can_include_tx(conn, &conf, true, tx, tx_size)
})
.map_err(|_| {
MemPoolRejection::NoSuchChainTip(
current_consensus_hash.clone(),
current_block.clone(),
)
})?
.expect("BUG: do not have unconfirmed state, despite being Some(..)")
} else {
Err(MemPoolRejection::BadNonces(mismatch_error))
}
}
// Error cases where retrying against `unconfirmed_state` may succeed
MemPoolRejection::FeeTooLow(_, _) |
MemPoolRejection::NotEnoughFunds(_, _) |
MemPoolRejection::NoSuchContract |
MemPoolRejection::NoSuchPublicFunction |
MemPoolRejection::PoisonMicroblocksDoNotConflict |
MemPoolRejection::InvalidMicroblocks |
MemPoolRejection::NoAnchorBlockWithPubkeyHash(_) |
MemPoolRejection::NoAnchorBlockWithPubkeyHashes(_) |
MemPoolRejection::NoSuchChainTip(_, _) |
MemPoolRejection::DBError(_) |
MemPoolRejection::EstimatorError(_) |
MemPoolRejection::Other(_) => {
debug!("Retry transaction {} against unconfirmed state", tx.txid());
self.with_read_only_unconfirmed_clarity_tx(&NULL_BURN_STATE_DB, |conn| {
StacksChainState::can_include_tx(conn, &conf, true, tx, tx_size)
})
.map_err(|_| {
MemPoolRejection::NoSuchChainTip(
current_consensus_hash.clone(),
current_block.clone(),
)
})?
.map_err(|_| e)?
.expect("BUG: do not have unconfirmed state, despite being Some(..)")
} else {
Err(MemPoolRejection::BadNonces(mismatch_error))
}
// Error cases where we should not retry against `unconfirmed_state`
MemPoolRejection::SerializationFailure(_) |
MemPoolRejection::DeserializationFailure(_) |
MemPoolRejection::FailedToValidate(_) |
MemPoolRejection::BadFunctionArgument(_) |
MemPoolRejection::ContractAlreadyExists(_) |
MemPoolRejection::BadAddressVersionByte |
MemPoolRejection::NoCoinbaseViaMempool |
MemPoolRejection::TooMuchChaining { .. } |
MemPoolRejection::ConflictingNonceInMempool |
MemPoolRejection::BadTransactionVersion |
MemPoolRejection::TransferAmountMustBePositive |
MemPoolRejection::TransferRecipientIsSender(_) => Err(e)
}
Err(e) => Err(e),
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/chainstate/stacks/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1885,7 +1885,7 @@ impl StacksChainState {
return None;
}
Err(e) => {
warn!("Failed to query for {}: {:?}", parent_tip, &e);
warn!("Failed to query for {parent_tip}: {e:?}");
return None;
}
}
Expand All @@ -1903,14 +1903,14 @@ impl StacksChainState {
where
F: FnOnce(&mut ClarityReadOnlyConnection) -> R,
{
if let Some(ref unconfirmed) = self.unconfirmed_state.as_ref() {
if let Some(unconfirmed) = &self.unconfirmed_state {
if !unconfirmed.is_readable() {
return Ok(None);
}
}

let mut unconfirmed_state_opt = self.unconfirmed_state.take();
let res = if let Some(ref mut unconfirmed_state) = unconfirmed_state_opt {
let res = if let Some(unconfirmed_state) = unconfirmed_state_opt.as_mut() {
let mut conn = unconfirmed_state
.clarity_inst
.read_only_connection_checked(
Expand Down

0 comments on commit b0dce01

Please sign in to comment.