feat: Add RPC endpoint for requesting raw cancel and refund transactions

This commit is contained in:
binarybaron 2024-08-01 18:33:50 +02:00
parent 47b0fdbdee
commit 787827e50f
No known key found for this signature in database
GPG Key ID: 99B75D3E1476A26E
7 changed files with 83 additions and 25 deletions

20
Cargo.lock generated
View File

@ -743,7 +743,7 @@ dependencies = [
"nom", "nom",
"pathdiff", "pathdiff",
"serde", "serde",
"toml 0.8.17", "toml 0.8.16",
] ]
[[package]] [[package]]
@ -4580,7 +4580,7 @@ dependencies = [
"tokio-tar", "tokio-tar",
"tokio-tungstenite", "tokio-tungstenite",
"tokio-util", "tokio-util",
"toml 0.8.17", "toml 0.8.16",
"torut", "torut",
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@ -4920,9 +4920,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.17" version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a44eede9b727419af8095cb2d72fab15487a541f54647ad4414b34096ee4631" checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
@ -4932,18 +4932,18 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.8" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.18" version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [ dependencies = [
"indexmap 2.1.0", "indexmap 2.1.0",
"serde", "serde",
@ -5767,9 +5767,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.16" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View File

@ -7,9 +7,12 @@ use crate::network::swarm;
use crate::protocol::bob::{BobState, Swap}; use crate::protocol::bob::{BobState, Swap};
use crate::protocol::{bob, State}; use crate::protocol::{bob, State};
use crate::{bitcoin, cli, monero, rpc}; use crate::{bitcoin, cli, monero, rpc};
use crate::database::SwapStateVecExt;
use anyhow::{bail, Context as AnyContext, Result}; use anyhow::{bail, Context as AnyContext, Result};
use ::bitcoin::consensus::encode::serialize_hex;
use comfy_table::Table; use comfy_table::Table;
use libp2p::core::Multiaddr; use libp2p::core::Multiaddr;
use monero_rpc::wallet::BlockHeight;
use qrcode::render::unicode; use qrcode::render::unicode;
use qrcode::QrCode; use qrcode::QrCode;
use rust_decimal::prelude::FromPrimitive; use rust_decimal::prelude::FromPrimitive;
@ -69,6 +72,9 @@ pub enum Method {
swap_id: Uuid, swap_id: Uuid,
}, },
GetRawStates, GetRawStates,
GetRawTransactions {
swap_id: Uuid,
},
} }
impl Method { impl Method {
@ -164,6 +170,14 @@ impl Method {
method_name = "WithdrawBtc", method_name = "WithdrawBtc",
log_reference_id = field::Empty log_reference_id = field::Empty
) )
},
Method::GetRawTransactions { swap_id } => {
debug_span!(
"method",
method_name = "GetRawTransactions",
swap_id=%swap_id,
log_reference_id = field::Empty
)
} }
}; };
if let Some(log_reference_id) = log_reference_id { if let Some(log_reference_id) = log_reference_id {
@ -660,15 +674,7 @@ impl Request {
let latest_state: BobState = state.try_into()?; let latest_state: BobState = state.try_into()?;
let all_states = context.db.get_states(swap_id).await?; let all_states = context.db.get_states(swap_id).await?;
let state3 = all_states let state3 = all_states
.iter() .find_state3()?;
.find_map(|s| {
if let State::Bob(BobState::BtcLocked { state3, .. }) = s {
Some(state3)
} else {
None
}
})
.context("Failed to get \"BtcLocked\" state")?;
let swap_start_date = context.db.get_swap_start_date(swap_id).await?; let swap_start_date = context.db.get_swap_start_date(swap_id).await?;
let peer_id = context.db.get_peer_id(swap_id).await?; let peer_id = context.db.get_peer_id(swap_id).await?;
@ -915,6 +921,19 @@ impl Request {
Method::GetCurrentSwap => Ok(json!({ Method::GetCurrentSwap => Ok(json!({
"swap_id": context.swap_lock.get_current_swap_id().await "swap_id": context.swap_lock.get_current_swap_id().await
})), })),
Method::GetRawTransactions { swap_id } => {
let all_states = context.db.get_states(swap_id).await?;
let state3 = all_states.find_state3()?;
let cancelledState = state3.cancel(BlockHeight { height: 0 });
let txCancel = cancelledState.construct_tx_cancel()?;
let txRefund = cancelledState.signed_refund_transaction()?;
Ok(json!({
"tx_cancel": txCancel.serialize_hex(),
"tx_refund": serialize_hex(&txRefund),
}))
}
} }
} }

View File

@ -16,6 +16,7 @@ use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::ops::Add; use std::ops::Add;
use bdk::bitcoin::consensus::encode::{serialize_hex, serialize};
/// Represent a timelock, expressed in relative block height as defined in /// Represent a timelock, expressed in relative block height as defined in
/// [BIP68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki). /// [BIP68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki).
@ -161,6 +162,10 @@ impl TxCancel {
self.inner.txid() self.inner.txid()
} }
pub fn serialize_hex(&self) -> String {
serialize_hex(&self.inner)
}
pub fn digest(&self) -> Sighash { pub fn digest(&self) -> Sighash {
self.digest self.digest
} }

View File

@ -1,10 +1,12 @@
pub use alice::Alice; pub use alice::Alice;
pub use bob::Bob; pub use bob::Bob;
use monero_rpc::wallet::BlockHeight;
pub use sqlite::SqliteDatabase; pub use sqlite::SqliteDatabase;
use crate::fs::ensure_directory_exists; use crate::fs::ensure_directory_exists;
use crate::protocol::bob::{BobState, State2, State3};
use crate::protocol::{Database, State}; use crate::protocol::{Database, State};
use anyhow::{bail, Result}; use anyhow::{bail, Result, anyhow};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Display; use std::fmt::Display;
use std::path::Path; use std::path::Path;
@ -105,3 +107,18 @@ pub async fn open_db(
Ok(Arc::new(sqlite)) Ok(Arc::new(sqlite))
} }
} }
pub trait SwapStateVecExt {
fn find_state3(&self) -> Result<State3>;
}
impl SwapStateVecExt for Vec<State> {
fn find_state3(&self) -> Result<State3> {
self.iter()
.find_map(|state| match state {
State::Bob(BobState::BtcLocked {state3, ..}) => Some(state3.clone()),
_ => None
})
.ok_or_else(|| anyhow!("No BobState::BtcLocked found"))
}
}

View File

@ -1,5 +1,6 @@
use crate::database::Swap; use crate::database::Swap;
use crate::monero::{Address, TransferProof}; use crate::monero::{Address, TransferProof};
use crate::protocol::bob::{BobState, State3};
use crate::protocol::{Database, State}; use crate::protocol::{Database, State};
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;

View File

@ -727,11 +727,7 @@ impl State6 {
&self, &self,
bitcoin_wallet: &bitcoin::Wallet, bitcoin_wallet: &bitcoin::Wallet,
) -> Result<(Txid, Subscription)> { ) -> Result<(Txid, Subscription)> {
let transaction = self let transaction = self.signed_cancel_transaction()?;
.construct_tx_cancel()?
.complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone())
.context("Failed to complete Bitcoin cancel transaction")?;
let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?;
Ok((tx_id, subscription)) Ok((tx_id, subscription))
@ -760,6 +756,10 @@ impl State6 {
Ok(signed_tx_refund) Ok(signed_tx_refund)
} }
pub fn signed_cancel_transaction(&self) -> Result<Transaction> {
self.construct_tx_cancel()?.complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone())
}
pub fn tx_lock_id(&self) -> bitcoin::Txid { pub fn tx_lock_id(&self) -> bitcoin::Txid {
self.tx_lock.txid() self.tx_lock.txid()
} }

View File

@ -213,6 +213,22 @@ pub fn register_modules(context: Arc<Context>) -> Result<RpcModule<Arc<Context>>
execute_request(params, Method::GetCurrentSwap, &context).await execute_request(params, Method::GetCurrentSwap, &context).await
})?; })?;
module.register_async_method("get_raw_transactions", |params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let swap_id = params.get("swap_id").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string())
})?;
let swap_id = as_uuid(swap_id).ok_or_else(|| {
jsonrpsee_core::Error::Custom("Could not parse swap_id".to_string())
})?;
execute_request(params_raw, Method::GetRawTransactions {
swap_id
}, &context).await
})?;
Ok(module) Ok(module)
} }