From adfd6df70afe691d13042803b746d1f45b65e7d4 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 18 Jun 2024 16:04:24 +0800 Subject: [PATCH] e2e(cch): pay btc invoice for ReceiveBTC order --- src/cch/actor.rs | 82 +++++++++++-------- .../cross-chain-hub/01-add-btc-invoice.bru | 6 +- .../02-create-send-btc-order.bru | 5 ++ .../04-node1-open-channel-to-node3.bru | 3 +- .../e2e/cross-chain-hub/07-node1-add-tlc.bru | 2 +- .../cross-chain-hub/08-check-btc-received.bru | 10 +-- .../09-create-receive-btc-order.bru | 9 +- .../cross-chain-hub/10-pay-btc-invoice.bru | 47 +++++++++++ 8 files changed, 121 insertions(+), 43 deletions(-) create mode 100644 tests/bruno/e2e/cross-chain-hub/10-pay-btc-invoice.bru diff --git a/src/cch/actor.rs b/src/cch/actor.rs index 375be4899..2fb3d699b 100644 --- a/src/cch/actor.rs +++ b/src/cch/actor.rs @@ -199,38 +199,26 @@ impl Actor for CchActor { } Ok(()) } - CchMessage::SettleSendBTCOrder(event) => self - .settle_send_btc_order(state, event) - .await - .map_err(Into::into), + CchMessage::SettleSendBTCOrder(event) => { + log::debug!("settle_send_btc_order {:?}", event); + if let Err(err) = self.settle_send_btc_order(state, event).await { + log::error!("settle_send_btc_order failed: {}", err); + } + Ok(()) + } CchMessage::SettleReceiveBTCOrder(event) => { - if event.preimage.is_some() { - log::info!( - "SettleReceiveBTCOrder: payment_hash={}, status={:?}", - event.payment_hash, - event.status - ); - // TODO: 1. Create a CKB payment to the payee to get preimage when event.status is Accepted - // TODO: 2. Subscribe to the CKB payment events, once it's settled, use the preimage to settle the BTC payment via invoicesrpc `settle_invoice`. - match state - .orders_db - .update_receive_btc_order(&event.payment_hash, event.preimage, event.status) - .await - { - Err(CchDbError::NotFound(_)) => { - // ignore payments not found in the db - Ok(()) - } - result => result.map_err(Into::into), - } - } else { - Ok(()) + log::debug!("settle_receive_btc_order {:?}", event); + if let Err(err) = self.settle_receive_btc_order(state, event).await { + log::error!("settle_receive_btc_order failed: {}", err); } + Ok(()) + } + CchMessage::TlcNotification(tlc_notification) => { + if let Err(err) = self.handle_tlc_notification(state, tlc_notification).await { + log::error!("handle_tlc_notification failed: {}", err); + } + Ok(()) } - CchMessage::TlcNotification(tlc_notification) => self - .handle_tlc_notification(state, tlc_notification) - .await - .map_err(Into::into), } } } @@ -504,6 +492,35 @@ impl CchActor { Ok(order) } + + async fn settle_receive_btc_order( + &self, + state: &mut CchState, + event: SettleReceiveBTCOrderEvent, + ) -> Result<()> { + if event.preimage.is_some() { + log::info!( + "SettleReceiveBTCOrder: payment_hash={}, status={:?}", + event.payment_hash, + event.status + ); + // TODO: 1. Create a CKB payment to the payee to get preimage when event.status is Accepted + // TODO: 2. Subscribe to the CKB payment events, once it's settled, use the preimage to settle the BTC payment via invoicesrpc `settle_invoice`. + match state + .orders_db + .update_receive_btc_order(&event.payment_hash, event.preimage, event.status) + .await + { + Err(CchDbError::NotFound(_)) => { + // ignore payments not found in the db + Ok(()) + } + result => result.map_err(Into::into), + } + } else { + Ok(()) + } + } } struct LndPaymentsTracker { @@ -698,13 +715,14 @@ impl LndInvoiceTracker { // Return true to quit the tracker async fn on_invoice(&self, invoice: lnrpc::Invoice) -> Result { - log::debug!("[LndPaymentsTracker] invoice: {:?}", invoice); + log::debug!("[LndInvoiceTracker] invoice: {:?}", invoice); let status = lnrpc::invoice::InvoiceState::try_from(invoice.state) .map(Into::into) .unwrap_or(CchOrderStatus::Pending); let event = CchMessage::SettleReceiveBTCOrder(SettleReceiveBTCOrderEvent { - payment_hash: hex::encode(invoice.r_hash), - preimage: (!invoice.r_preimage.is_empty()).then_some(hex::encode(invoice.r_preimage)), + payment_hash: format!("0x{}", hex::encode(invoice.r_hash)), + preimage: (!invoice.r_preimage.is_empty()) + .then(|| format!("0x{}", hex::encode(invoice.r_preimage))), status, }); self.cch_actor.cast(event)?; diff --git a/tests/bruno/e2e/cross-chain-hub/01-add-btc-invoice.bru b/tests/bruno/e2e/cross-chain-hub/01-add-btc-invoice.bru index 136037087..381292e23 100644 --- a/tests/bruno/e2e/cross-chain-hub/01-add-btc-invoice.bru +++ b/tests/bruno/e2e/cross-chain-hub/01-add-btc-invoice.bru @@ -11,7 +11,11 @@ post { } body:json { - {"value":5000} + {"value":20000} +} + +assert { + res.status: eq 200 } script:post-response { diff --git a/tests/bruno/e2e/cross-chain-hub/02-create-send-btc-order.bru b/tests/bruno/e2e/cross-chain-hub/02-create-send-btc-order.bru index 7066c0720..3d27ba973 100644 --- a/tests/bruno/e2e/cross-chain-hub/02-create-send-btc-order.bru +++ b/tests/bruno/e2e/cross-chain-hub/02-create-send-btc-order.bru @@ -29,6 +29,11 @@ body:json { } } +assert { + res.status: eq 200 + res.body.error: isUndefined +} + script:post-response { if (res.body.result) { bru.setVar("CKB_PAY_REQ", res.body.result.ckb_pay_req); diff --git a/tests/bruno/e2e/cross-chain-hub/04-node1-open-channel-to-node3.bru b/tests/bruno/e2e/cross-chain-hub/04-node1-open-channel-to-node3.bru index b469dcee3..74868737f 100644 --- a/tests/bruno/e2e/cross-chain-hub/04-node1-open-channel-to-node3.bru +++ b/tests/bruno/e2e/cross-chain-hub/04-node1-open-channel-to-node3.bru @@ -15,7 +15,6 @@ headers { Accept: application/json } - body:json { { "id": "42", @@ -24,7 +23,7 @@ body:json { "params": [ { "peer_id": "{{NODE3_PEERID}}", - "funding_amount": "0x2710", + "funding_amount": "0xc350", "funding_udt_type_script": { "code_hash": "0xe1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419", "hash_type": "data1", diff --git a/tests/bruno/e2e/cross-chain-hub/07-node1-add-tlc.bru b/tests/bruno/e2e/cross-chain-hub/07-node1-add-tlc.bru index 84fac0c13..a07e23990 100644 --- a/tests/bruno/e2e/cross-chain-hub/07-node1-add-tlc.bru +++ b/tests/bruno/e2e/cross-chain-hub/07-node1-add-tlc.bru @@ -23,7 +23,7 @@ body:json { "params": [ { "channel_id": "{{N1N3_CHANNEL_ID}}", - "amount": "0x1388", + "amount": "0x4e20", "payment_hash": "{{PAYMENT_HASH}}", "expiry": 40 } diff --git a/tests/bruno/e2e/cross-chain-hub/08-check-btc-received.bru b/tests/bruno/e2e/cross-chain-hub/08-check-btc-received.bru index 7393341ff..32a067673 100644 --- a/tests/bruno/e2e/cross-chain-hub/08-check-btc-received.bru +++ b/tests/bruno/e2e/cross-chain-hub/08-check-btc-received.bru @@ -14,6 +14,10 @@ vars:post-response { max_iterations: 10 } +assert { + res.status: eq 200 +} + script:pre-request { if(bru.getVar("iteration") === undefined){ bru.setVar("iteration", 0); @@ -27,7 +31,7 @@ script:post-response { console.log(`Try ${i+1}/${n}`); } - if (parseInt(res.body.balance, 10) > 0) { + if (parseInt(res.body.local_balance.sat, 10) > 0) { console.log("Bob has received the payment"); bru.setVar("iteration", 0); } else if (i+1 < n) { @@ -39,7 +43,3 @@ script:post-response { throw new Error("Bob has not received the payment"); } } - -docs { - BTC user generates an invoice via lnd. -} diff --git a/tests/bruno/e2e/cross-chain-hub/09-create-receive-btc-order.bru b/tests/bruno/e2e/cross-chain-hub/09-create-receive-btc-order.bru index d1b5c04f7..1644615fc 100644 --- a/tests/bruno/e2e/cross-chain-hub/09-create-receive-btc-order.bru +++ b/tests/bruno/e2e/cross-chain-hub/09-create-receive-btc-order.bru @@ -24,13 +24,18 @@ body:json { { "payment_hash": "{{PAYMENT_HASH}}", "channel_id": "{{N1N3_CHANNEL_ID}}", - "amount_sats": "0x7d0", + "amount_sats": "0x1", "final_tlc_expiry": "0x3c" } ] } } +assert { + res.status: eq 200 + res.body.error: isUndefined +} + script:pre-request { const uuid = require('uuid'); const CryptoJS = require("crypto-js"); @@ -52,5 +57,5 @@ script:post-response { } docs { - CKB user sends the received BTC invoice to the cross-chain hub to exchange a CKB invoice. + CKB user requests a BTC invoice to receive BTC from Bitcoin user. } diff --git a/tests/bruno/e2e/cross-chain-hub/10-pay-btc-invoice.bru b/tests/bruno/e2e/cross-chain-hub/10-pay-btc-invoice.bru new file mode 100644 index 000000000..4efb8a203 --- /dev/null +++ b/tests/bruno/e2e/cross-chain-hub/10-pay-btc-invoice.bru @@ -0,0 +1,47 @@ +meta { + name: 10-pay-btc-invoice + type: http + seq: 10 +} + +post { + url: {{LND_BOB_RPC_URL}}/v2/router/send + body: json + auth: none +} + +body:json { + { + "payment_request": "{{BTC_PAY_REQ}}", + "timeout_seconds": 1 + } +} + +assert { + res.status: eq 409 +} + +script:pre-request { + const axios = require('axios'); + + const url = bru.getEnvVar("LND_BOB_RPC_URL") + "/v2/router/send"; + const body = { + payment_request: bru.getVar("BTC_PAY_REQ"), + timeout_seconds: 1 + }; + console.log(url); + console.log(body); + + await axios({ + method: 'POST', + url: url, + data: body, + responseType: 'stream' + }); +} + +docs { + Send payment via lnd RPC https://lightning.engineering/api-docs/api/lnd/router/send-payment-v2. + + This is a server-streaming RPC which will block Bruno. The workaround is sending the request in the pre-script so the Bruno request will return 409 because the payment is already sent. +}