depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

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:
Mbtc-wire/src/loops/worker.rs | 63++++++++++++++++++++++++++++++++++++++-------------------------
Mdocs/presentation.tex | 4++--
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);