Skip to content

Commit

Permalink
chore(tests-e2e): Reproduce some problems related to #1912
Browse files Browse the repository at this point in the history
  • Loading branch information
luckysori committed Feb 2, 2024
1 parent 1bc842c commit c0af623
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 44 deletions.
4 changes: 4 additions & 0 deletions crates/ln-dlc-node/src/ldk_node_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ where
.address)
}

pub(crate) fn get_new_address(&self) -> Result<Address> {
Ok(self.bdk_lock().get_address(AddressIndex::New)?.address)
}

pub fn is_mine(&self, script: &Script) -> Result<bool> {
Ok(self.bdk_lock().is_mine(script)?)
}
Expand Down
4 changes: 4 additions & 0 deletions crates/ln-dlc-node/src/ln_dlc_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ impl<S: TenTenOneStorage, N: Storage> LnDlcWallet<S, N> {
self.address_cache.read().clone()
}

pub fn new_address(&self) -> Result<Address> {
self.ldk_wallet().get_new_address()
}

pub fn is_mine(&self, script: &Script) -> Result<bool> {
self.ldk_wallet().is_mine(script)
}
Expand Down
6 changes: 6 additions & 0 deletions crates/ln-dlc-node/src/node/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ impl<S: TenTenOneStorage, N: Storage> Node<S, N> {
self.wallet.unused_address()
}

pub fn get_new_address(&self) -> Result<Address> {
self.wallet
.new_address()
.context("Failed to get new address")
}

pub fn get_blockchain_height(&self) -> Result<u64> {
self.wallet
.get_blockchain_height()
Expand Down
147 changes: 141 additions & 6 deletions crates/tests-e2e/src/bitcoind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use bitcoin::Amount;
use reqwest::Client;
use reqwest::Response;
use serde::Deserialize;
use serde_json::json;
use std::time::Duration;

/// A wrapper over the bitcoind HTTP API
Expand All @@ -27,12 +28,7 @@ impl Bitcoind {

/// Instructs `bitcoind` to generate to address.
pub async fn mine(&self, n: u16) -> Result<()> {
#[derive(Deserialize, Debug)]
struct BitcoindResponse {
result: String,
}

let response: BitcoindResponse = self
let response: GetNewAddressResponse = self
.client
.post(&self.host)
.body(r#"{"jsonrpc": "1.0", "method": "getnewaddress", "params": []}"#.to_string())
Expand Down Expand Up @@ -75,6 +71,106 @@ impl Bitcoind {
Ok(response)
}

pub async fn send_multiple_utxos_to_address<F>(
&self,
address_fn: F,
utxo_amount: Amount,
n_utxos: u64,
) -> Result<()>
where
F: Fn() -> Address,
{
let total_amount = utxo_amount * n_utxos;

let response: ListUnspentResponse = self
.client
.post(&self.host)
.body(r#"{"jsonrpc": "1.0", "method": "listunspent", "params": []}"#)
.send()
.await?
.json()
.await?;

let utxo = response
.result
.iter()
// We try to find one UTXO that can cover the whole transaction. We could cover the
// amount with multiple UTXOs too, but this is simpler and will probably succeed.
.find(|utxo| utxo.spendable && utxo.amount >= total_amount)
.expect("to find UTXO to cover multi-payment");

let mut outputs = serde_json::value::Map::new();

for _ in 0..n_utxos {
let address = address_fn();
outputs.insert(address.to_string(), json!(utxo_amount.to_btc()));
}

let create_raw_tx_request = json!(
{
"jsonrpc": "1.0",
"method": "createrawtransaction",
"params":
[
[ {"txid": utxo.txid, "vout": utxo.vout} ],
outputs
]
}
);

let create_raw_tx_response: CreateRawTransactionResponse = self
.client
.post(&self.host)
.json(&create_raw_tx_request)
.send()
.await?
.json()
.await?;

let sign_raw_tx_with_wallet_request = json!(
{
"jsonrpc": "1.0",
"method": "signrawtransactionwithwallet",
"params": [ create_raw_tx_response.result ]
}
);

let sign_raw_tx_with_wallet_response: SignRawTransactionWithWalletResponse = self
.client
.post(&self.host)
.json(&sign_raw_tx_with_wallet_request)
.send()
.await?
.json()
.await?;

let send_raw_tx_request = json!(
{
"jsonrpc": "1.0",
"method": "sendrawtransaction",
"params": [ sign_raw_tx_with_wallet_response.result.hex, 0 ]
}
);

let send_raw_tx_response: SendRawTransactionResponse = self
.client
.post(&self.host)
.json(&send_raw_tx_request)
.send()
.await?
.json()
.await?;

tracing::info!(
txid = %send_raw_tx_response.result,
%utxo_amount,
%n_utxos,
"Published multi-utxo transaction"
);

Ok(())
}

pub async fn post(&self, endpoint: &str, body: Option<String>) -> Result<Response> {
let mut builder = self.client.post(endpoint.to_string());
if let Some(body) = body {
Expand All @@ -88,3 +184,42 @@ impl Bitcoind {
Ok(response)
}
}

#[derive(Deserialize, Debug)]
struct GetNewAddressResponse {
result: String,
}

#[derive(Deserialize, Debug)]
struct ListUnspentResponse {
result: Vec<Utxo>,
}

#[derive(Deserialize, Debug)]
struct Utxo {
txid: String,
vout: usize,
#[serde(with = "bitcoin::util::amount::serde::as_btc")]
amount: Amount,
spendable: bool,
}

#[derive(Deserialize, Debug)]
struct CreateRawTransactionResponse {
result: String,
}

#[derive(Deserialize, Debug)]
struct SignRawTransactionWithWalletResponse {
result: SignRawTransactionWithWalletResponseBody,
}

#[derive(Deserialize, Debug)]
struct SignRawTransactionWithWalletResponseBody {
hex: String,
}

#[derive(Deserialize, Debug)]
struct SendRawTransactionResponse {
result: String,
}
63 changes: 39 additions & 24 deletions crates/tests-e2e/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ pub struct TestSetup {
}

impl TestSetup {
/// Start test with a running app and a funded wallet.
pub async fn new_after_funding(fund_amount: Option<Amount>) -> Self {
pub async fn new() -> Self {
init_tracing();

let client = init_reqwest();
Expand All @@ -35,17 +34,6 @@ impl TestSetup {

assert!(coordinator.is_running().await);

// Ensure that the coordinator has a free UTXO available.
let address = coordinator.get_new_address().await.unwrap();

bitcoind
.send_to_address(&address, Amount::ONE_BTC)
.await
.unwrap();

bitcoind.mine(1).await.unwrap();
coordinator.sync_node().await.unwrap();

// App setup

let app = run_app(None).await;
Expand All @@ -62,37 +50,64 @@ impl TestSetup {
"App should start with empty off-chain wallet"
);

let fund_amount = fund_amount.unwrap_or(Amount::ONE_BTC);
Self {
app,
coordinator,
bitcoind,
}
}

pub async fn fund_coordinator(&self, amount: Amount) {
// Ensure that the coordinator has a free UTXO available.
let address = self.coordinator.get_new_address().await.unwrap();

self.bitcoind
.send_to_address(&address, amount)
.await
.unwrap();

self.bitcoind.mine(1).await.unwrap();
self.coordinator.sync_node().await.unwrap();

// TODO: Get coordinator balance to verify this claim.
tracing::info!("Successfully funded coordinator");
}

pub async fn fund_app(&self, fund_amount: Amount) {
let address = api::get_unused_address();
let address = &address.0.parse().unwrap();

bitcoind
self.bitcoind
.send_to_address(address, fund_amount)
.await
.unwrap();

bitcoind.mine(1).await.unwrap();
self.bitcoind.mine(1).await.unwrap();

wait_until!({
refresh_wallet_info();
app.rx.wallet_info().unwrap().balances.on_chain == fund_amount.to_sat()
self.app.rx.wallet_info().unwrap().balances.on_chain >= fund_amount.to_sat()
});

let on_chain_balance = app.rx.wallet_info().unwrap().balances.on_chain;
let on_chain_balance = self.app.rx.wallet_info().unwrap().balances.on_chain;

tracing::info!(%fund_amount, %on_chain_balance, "Successfully funded app");
}

Self {
app,
coordinator,
bitcoind,
}
/// Start test with a running app and a funded wallet.
pub async fn new_after_funding() -> Self {
let setup = Self::new().await;

setup.fund_coordinator(Amount::ONE_BTC).await;

setup.fund_app(Amount::ONE_BTC).await;

setup
}

/// Start test with a running app with a funded wallet and an open position.
pub async fn new_with_open_position() -> Self {
let setup = Self::new_after_funding(None).await;
let setup = Self::new_after_funding().await;
let rx = &setup.app.rx;

tracing::info!("Opening a position");
Expand Down
2 changes: 1 addition & 1 deletion crates/tests-e2e/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tests_e2e::setup::TestSetup;
#[tokio::test(flavor = "multi_thread")]
#[ignore = "need to be run with 'just e2e' command"]
async fn app_can_be_funded_with_bitcoind() -> Result<()> {
TestSetup::new_after_funding(None).await;
TestSetup::new_after_funding().await;

Ok(())
}
20 changes: 8 additions & 12 deletions crates/tests-e2e/tests/open_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,20 @@ use tests_e2e::setup::TestSetup;
use tests_e2e::wait_until;
use tokio::task::spawn_blocking;

fn dummy_order() -> NewOrder {
NewOrder {
#[tokio::test(flavor = "multi_thread")]
#[ignore = "need to be run with 'just e2e' command"]
async fn can_open_position() {
let test = TestSetup::new_after_funding().await;
let app = &test.app;

let order = NewOrder {
leverage: 2.0,
contract_symbol: ContractSymbol::BtcUsd,
direction: api::Direction::Long,
quantity: 1.0,
order_type: Box::new(OrderType::Market),
stable: false,
}
}

#[tokio::test(flavor = "multi_thread")]
#[ignore = "need to be run with 'just e2e' command"]
async fn can_open_position() {
let test = TestSetup::new_after_funding(None).await;
let app = &test.app;

let order = dummy_order();
};
spawn_blocking({
let order = order.clone();
move || api::submit_order(order).unwrap()
Expand Down
Loading

0 comments on commit c0af623

Please sign in to comment.