commit 0c9b92207adf196edbbca426830d6ffd4ad2032f
parent 485e60e4f799ec819d132f91e5cd5641a2d43084
Author: Antoine A <>
Date: Wed, 9 Feb 2022 18:09:56 +0100
eth-wire: move bump out of sync_chain to prevent stuck synchronization caused by excessive transaction fees
Diffstat:
2 files changed, 40 insertions(+), 27 deletions(-)
diff --git a/btc-wire/src/loops/worker.rs b/btc-wire/src/loops/worker.rs
@@ -97,13 +97,31 @@ pub fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectDb, state: &WireSt
}
// Sync chain
- sync_chain(rpc, db, state, &mut status)?;
+ let stuck = sync_chain(rpc, db, state, &mut status)?;
// As we are now in sync with the blockchain if a transaction has Requested status it have not been sent
// Send requested withdraws
while withdraw(db, rpc)? {}
+ // Bump stuck transactions
+ if let Some(stuck) = stuck {
+ for id in stuck {
+ let bump = rpc.bump_fee(&id)?;
+ fail_point("(injected) fail bump", 0.3)?;
+ let row = db.query_one(
+ "UPDATE tx_out SET txid=$1 WHERE txid=$2 RETURNING wtid",
+ &[&bump.txid.as_ref(), &id.as_ref()],
+ )?;
+ info!(
+ ">> (bump) {} replace {} with {}",
+ base32(row.get(0)),
+ id,
+ bump.txid
+ );
+ }
+ }
+
let bounce_fee = BtcAmount::from_sat(state.config.bounce_fee);
// Send requested bounce
while bounce(db, rpc, &bounce_fee)? {}
@@ -133,13 +151,13 @@ fn last_hash(db: &mut Client) -> Result<BlockHash, postgres::Error> {
Ok(BlockHash::from_slice(row.get(0)).unwrap())
}
-/// Parse new transactions, return true if the database is up to date with the latest mined block
+/// Parse new transactions, return stuck transactions if the database is up to date with the latest mined block
fn sync_chain(
rpc: &mut Rpc,
db: &mut Client,
state: &WireState,
status: &mut bool,
-) -> LoopResult<bool> {
+) -> LoopResult<Option<Vec<Txid>>> {
// Get stored last_hash
let last_hash = last_hash(db)?;
let min_confirmations = state.confirmation.load(Ordering::SeqCst);
@@ -184,12 +202,18 @@ fn sync_chain(
}
}
if !new_status {
- return Ok(false);
+ return Ok(None);
}
+ let mut stuck = vec![];
+
for (id, (category, confirmations)) in txs {
match category {
- Category::Send => sync_chain_outgoing(&id, confirmations, rpc, db, state)?,
+ Category::Send => {
+ if sync_chain_outgoing(&id, confirmations, rpc, db, state)? {
+ stuck.push(id);
+ }
+ }
Category::Receive if confirmations >= min_confirmations as i32 => {
sync_chain_incoming_confirmed(&id, rpc, db)?
}
@@ -205,7 +229,7 @@ fn sync_chain(
&[&lastblock.as_ref()],
)?;
- Ok(true)
+ Ok(Some(stuck))
}
/// Sync database with removed transactions, return false if bitcoin backing is compromised
@@ -325,7 +349,7 @@ fn sync_chain_incoming_confirmed(
Ok(())
}
-/// Sync database with an outgoing withdraw transaction
+/// Sync database with an outgoing withdraw transaction, return true if stuck
fn sync_chain_withdraw(
id: &Txid,
full: &TransactionFull,
@@ -334,7 +358,7 @@ fn sync_chain_withdraw(
db: &mut Client,
confirmations: i32,
state: &WireState,
-) -> LoopResult<()> {
+) -> LoopResult<bool> {
let credit_addr = full.details[0].address.as_ref().unwrap();
let amount = btc_to_taler(&full.amount);
@@ -431,23 +455,12 @@ fn sync_chain_withdraw(
.unwrap()
.as_secs();
if now - full.time > delay as u64 {
- let bump = rpc.bump_fee(id)?;
- fail_point("(injected) fail bump", 0.3)?;
- db.execute(
- "UPDATE tx_out SET txid=$1 WHERE txid=$2",
- &[&bump.txid.as_ref(), &id.as_ref()],
- )?;
- info!(
- ">> (bump) {} replace {} with {}",
- base32(wtid),
- id,
- bump.txid
- );
+ return Ok(true);
}
}
}
}
- Ok(())
+ Ok(false)
}
/// Sync database with an outgoing bounce transaction
@@ -507,21 +520,21 @@ fn sync_chain_bounce(
Ok(())
}
-/// Sync database with an outgoing transaction
+/// Sync database with an outgoing transaction, return true if stuck
fn sync_chain_outgoing(
id: &Txid,
confirmations: i32,
rpc: &mut Rpc,
db: &mut Client,
state: &WireState,
-) -> LoopResult<()> {
+) -> LoopResult<bool> {
match rpc
.get_tx_op_return(id)
.map(|(full, bytes)| (full, OutMetadata::decode(&bytes)))
{
Ok((full, Ok(info))) => match info {
OutMetadata::Withdraw { wtid, .. } => {
- sync_chain_withdraw(id, &full, &wtid, rpc, db, confirmations, state)?
+ return sync_chain_withdraw(id, &full, &wtid, rpc, db, confirmations, state);
}
OutMetadata::Bounce { bounced } => sync_chain_bounce(id, &bounced, db, confirmations)?,
},
@@ -531,7 +544,7 @@ fn sync_chain_outgoing(
GetOpReturnErr::RPC(e) => return Err(e)?,
},
}
- Ok(())
+ Ok(false)
}
/// Send a withdraw transaction on the blockchain, return false if no more requested transaction are found
diff --git a/docs/presentation.tex b/docs/presentation.tex
@@ -318,8 +318,8 @@
\node[rect, right= 1cm of wa1](wo1) {Wait for notification};
\node[rect, below=4mm of wo1](wo2) {Synchronize chain};
\node[rect, below=4mm of wo2](wo3) {Withdraw};
- \node[rect, below=4mm of wo3](wo4) {Bounce};
- \node[rect, below=4mm of wo4](wo5) {Bump};
+ \node[rect, below=4mm of wo3](wo4) {Bump};
+ \node[rect, below=4mm of wo4](wo5) {Bounce};
\node[above=1mm of wo1]{Worker};
\draw[-stealth] (wo1) -- (wo2);
\draw[-stealth] (wo2) -- (wo3);