Greatly reduce load onto the Electrum backend

We achieve our optimizations in three ways:

1. Batching calls instead of making them individually.

To get access to the batch calls, we replace all our
calls to the HTTP interface with RPC calls.

2. Never directly make network calls based on function
calls on the wallet.

Instead, inquiring about the status of a script always
just returns information based on local data. With every
call, we check when we last refreshed the local data and
do so if the data is considered to be too old. This
interval is configurable.

3. Use electrum's notification feature to get updated
with the latest blockheight.

Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Rishab Sharma <rishflab@hotmail.com>
This commit is contained in:
rishflab 2021-03-11 18:16:00 +11:00 committed by Thomas Eizinger
parent e17cbadccb
commit e5c0158597
No known key found for this signature in database
GPG key ID: 651AC83A6C6C8B96
16 changed files with 628 additions and 378 deletions

View file

@ -1,12 +1,11 @@
use crate::bitcoin::{
poll_until_block_height_is_gte, BlockHeight, CancelTimelock, EncryptedSignature,
PunishTimelock, TxCancel, TxLock, TxRefund,
CancelTimelock, EncryptedSignature, PunishTimelock, TxCancel, TxLock, TxRefund,
};
use crate::protocol::alice;
use crate::protocol::alice::event_loop::EventLoopHandle;
use crate::protocol::alice::TransferProof;
use crate::{bitcoin, monero};
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use futures::future::{select, Either};
use futures::pin_mut;
use libp2p::PeerId;
@ -61,11 +60,11 @@ pub async fn publish_cancel_transaction(
tx_cancel_sig_bob: bitcoin::Signature,
bitcoin_wallet: &bitcoin::Wallet,
) -> Result<bitcoin::TxCancel> {
// First wait for cancel timelock to expire
let tx_lock_height = bitcoin_wallet
.transaction_block_height(tx_lock.txid())
bitcoin_wallet
.watch_until_status(tx_lock.txid(), tx_lock.script_pubkey(), |status| {
status.is_confirmed_with(cancel_timelock)
})
.await?;
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + cancel_timelock).await?;
let tx_cancel = bitcoin::TxCancel::new(&tx_lock, cancel_timelock, a.public(), B);
@ -98,26 +97,36 @@ pub async fn publish_cancel_transaction(
pub async fn wait_for_bitcoin_refund(
tx_cancel: &TxCancel,
cancel_tx_height: BlockHeight,
punish_timelock: PunishTimelock,
refund_address: &bitcoin::Address,
bitcoin_wallet: &bitcoin::Wallet,
) -> Result<(bitcoin::TxRefund, Option<bitcoin::Transaction>)> {
let punish_timelock_expired =
poll_until_block_height_is_gte(bitcoin_wallet, cancel_tx_height + punish_timelock);
let tx_refund = bitcoin::TxRefund::new(tx_cancel, refund_address);
// TODO(Franck): This only checks the mempool, need to cater for the case where
// the transaction goes directly in a block
let seen_refund_tx = bitcoin_wallet.watch_for_raw_transaction(tx_refund.txid());
let seen_refund_tx = bitcoin_wallet.watch_until_status(
tx_refund.txid(),
refund_address.script_pubkey(),
|status| status.has_been_seen(),
);
let punish_timelock_expired =
bitcoin_wallet.watch_until_status(tx_cancel.txid(), tx_cancel.script_pubkey(), |status| {
status.is_confirmed_with(punish_timelock)
});
pin_mut!(punish_timelock_expired);
pin_mut!(seen_refund_tx);
match select(punish_timelock_expired, seen_refund_tx).await {
Either::Left(_) => Ok((tx_refund, None)),
Either::Right((published_refund_tx, _)) => Ok((tx_refund, Some(published_refund_tx?))),
Either::Right((Ok(()), _)) => {
let published_refund_tx = bitcoin_wallet.get_raw_transaction(tx_refund.txid()).await?;
Ok((tx_refund, Some(published_refund_tx)))
}
Either::Right((Err(e), _)) => {
bail!(e.context("Failed to monitor refund transaction"))
}
}
}