Skip to content

Commit a31d654

Browse files
committed
add loop for taker timelock recovery
1 parent a709efa commit a31d654

File tree

1 file changed

+103
-66
lines changed

1 file changed

+103
-66
lines changed

src/taker/api2.rs

Lines changed: 103 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,86 +1808,123 @@ impl Taker {
18081808
outgoing_swapcoins.len()
18091809
);
18101810

1811-
for outgoing in &outgoing_swapcoins {
1812-
let contract_txid = outgoing.contract_tx.compute_txid();
1813-
log::info!(
1814-
"Checking recovery options for outgoing contract: {}",
1815-
contract_txid
1816-
);
1811+
let mut pending_recovery: Vec<_> = outgoing_swapcoins.iter().collect();
18171812

1818-
// Check if contract has been spent
1819-
let outpoint = bitcoin::OutPoint {
1820-
txid: contract_txid,
1821-
vout: 0,
1822-
};
1823-
self.watch_service.watch_request(outpoint);
1813+
// loop until all contracts are recovered
1814+
while !pending_recovery.is_empty() {
1815+
let mut recovered_indices = Vec::new();
18241816

1825-
let spending_result = if let Some(event) = self.watch_service.wait_for_event() {
1826-
match event {
1827-
WatcherEvent::UtxoSpent { spending_tx, .. } => spending_tx,
1828-
_ => None,
1829-
}
1830-
} else {
1831-
None
1832-
};
1817+
for (idx, outgoing) in pending_recovery.iter().enumerate() {
1818+
let contract_txid = outgoing.contract_tx.compute_txid();
1819+
log::info!(
1820+
"Checking recovery options for outgoing contract: {}",
1821+
contract_txid
1822+
);
18331823

1834-
match spending_result {
1835-
Some(spending_tx) => {
1836-
log::info!("Outgoing contract {} already spent", contract_txid);
1824+
// Check if contract has been spent
1825+
let outpoint = bitcoin::OutPoint {
1826+
txid: contract_txid,
1827+
vout: 0,
1828+
};
1829+
self.watch_service.watch_request(outpoint);
18371830

1838-
match crate::protocol::contract2::detect_taproot_spending_path(
1839-
&spending_tx,
1840-
outpoint,
1841-
)? {
1842-
TaprootSpendingPath::KeyPath => {
1843-
log::info!("Contract spent cooperatively - no recovery needed");
1844-
continue;
1845-
}
1846-
TaprootSpendingPath::Hashlock { .. } => {
1847-
log::info!("Contract spent by receiver via hashlock - swap partially completed");
1848-
continue;
1849-
}
1850-
TaprootSpendingPath::Timelock => {
1851-
log::warn!("We already recovered this via timelock");
1852-
continue;
1853-
}
1831+
let spending_result = if let Some(event) = self.watch_service.wait_for_event() {
1832+
match event {
1833+
WatcherEvent::UtxoSpent { spending_tx, .. } => spending_tx,
1834+
_ => None,
18541835
}
1855-
}
1856-
None => {
1857-
// Contract not spent - check if timelock matured
1858-
log::info!(
1859-
"Outgoing contract {} not spent - checking timelock maturity",
1860-
contract_txid
1861-
);
1836+
} else {
1837+
None
1838+
};
18621839

1863-
if let Some(timelock) = outgoing.get_timelock() {
1864-
if crate::protocol::contract2::is_timelock_mature(
1865-
&self.wallet.rpc,
1866-
&contract_txid,
1867-
timelock,
1868-
)? {
1869-
log::info!("Timelock matured, attempting recovery");
1840+
match spending_result {
1841+
Some(spending_tx) => {
1842+
log::info!("Outgoing contract {} already spent", contract_txid);
18701843

1871-
match self
1844+
match crate::protocol::contract2::detect_taproot_spending_path(
1845+
&spending_tx,
1846+
outpoint,
1847+
)? {
1848+
TaprootSpendingPath::KeyPath => {
1849+
log::info!("Contract spent cooperatively - no recovery needed");
1850+
recovered_indices.push(idx);
1851+
}
1852+
TaprootSpendingPath::Hashlock { .. } => {
1853+
log::info!("Contract spent by receiver via hashlock - swap partially completed");
1854+
recovered_indices.push(idx);
1855+
}
1856+
TaprootSpendingPath::Timelock => {
1857+
log::warn!("We already recovered this via timelock");
1858+
recovered_indices.push(idx);
1859+
}
1860+
}
1861+
}
1862+
None => {
1863+
// Contract not spent - check if timelock matured
1864+
if let Some(timelock) = outgoing.get_timelock() {
1865+
// get current confirmations to calculate remaining blocks
1866+
let confirmations: u32 = self
18721867
.wallet
1873-
.spend_via_timelock_v2(outgoing, &self.watch_service)
1874-
{
1875-
Ok(txid) => {
1876-
log::info!("Successfully recovered outgoing contract via timelock: {}", txid);
1877-
}
1878-
Err(e) => {
1879-
log::error!("Failed to spend via timelock: {:?}", e);
1868+
.rpc
1869+
.get_raw_transaction_info(&contract_txid, None)
1870+
.ok()
1871+
.and_then(|info| info.confirmations)
1872+
.unwrap_or(0);
1873+
1874+
let remaining_blocks = if confirmations > timelock {
1875+
0
1876+
} else {
1877+
timelock - confirmations + 1
1878+
};
1879+
1880+
if remaining_blocks == 0 {
1881+
log::info!("Timelock matured, attempting recovery");
1882+
1883+
match self
1884+
.wallet
1885+
.spend_via_timelock_v2(outgoing, &self.watch_service)
1886+
{
1887+
Ok(txid) => {
1888+
log::info!("Successfully recovered outgoing contract via timelock: {}", txid);
1889+
recovered_indices.push(idx);
1890+
}
1891+
Err(e) => {
1892+
log::error!("Failed to spend via timelock: {:?}", e);
1893+
}
18801894
}
1895+
} else {
1896+
log::info!(
1897+
"Timelock not yet mature for contract {} - {} blocks remaining (confirmations: {}, required: {})",
1898+
contract_txid,
1899+
remaining_blocks,
1900+
confirmations,
1901+
timelock + 1
1902+
);
18811903
}
1882-
} else {
1883-
log::info!(
1884-
"Timelock not yet mature for contract {}",
1885-
contract_txid
1886-
);
18871904
}
18881905
}
18891906
}
18901907
}
1908+
1909+
// remove recovered contracts
1910+
for idx in recovered_indices.into_iter().rev() {
1911+
pending_recovery.remove(idx);
1912+
}
1913+
1914+
// If there are still pending contracts, wait before checking again
1915+
if !pending_recovery.is_empty() {
1916+
let wait_time = if cfg!(feature = "integration-test") {
1917+
std::time::Duration::from_secs(10)
1918+
} else {
1919+
std::time::Duration::from_secs(10 * 60)
1920+
};
1921+
log::info!(
1922+
"Waiting {:?} before checking {} remaining contracts...",
1923+
wait_time,
1924+
pending_recovery.len()
1925+
);
1926+
std::thread::sleep(wait_time);
1927+
}
18911928
}
18921929
}
18931930

0 commit comments

Comments
 (0)