mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-01-18 13:34:22 +01:00
393 lines
12 KiB
Rust
393 lines
12 KiB
Rust
#![cfg(test)]
|
|
|
|
use anyhow::Result;
|
|
use async_trait::async_trait;
|
|
use boltz_client::{
|
|
boltz::{
|
|
ChainFees, ChainMinerFees, ChainPair, ChainSwapDetails, CreateChainResponse,
|
|
CreateReverseResponse, CreateSubmarineResponse, Leaf, PairLimits, PairMinerFees,
|
|
ReverseFees, ReverseLimits, ReversePair, SubmarineClaimTxResponse, SubmarineFees,
|
|
SubmarinePair, SwapTree,
|
|
},
|
|
util::secrets::Preimage,
|
|
Amount, PublicKey,
|
|
};
|
|
use sdk_common::invoice::parse_invoice;
|
|
use std::sync::Mutex;
|
|
|
|
use crate::{
|
|
ensure_sdk,
|
|
error::{PaymentError, SdkError},
|
|
model::{Direction, SendSwap, Swap, Transaction as SdkTransaction, Utxo},
|
|
swapper::{ProxyUrlFetcher, Swapper},
|
|
test_utils::generate_random_string,
|
|
utils,
|
|
};
|
|
|
|
use super::status_stream::MockStatusStream;
|
|
|
|
#[derive(Default)]
|
|
pub struct ZeroAmountSwapMockConfig {
|
|
pub user_lockup_sat: u64,
|
|
pub onchain_fee_increase_sat: u64,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct MockSwapper {
|
|
zero_amount_swap_mock_config: Mutex<ZeroAmountSwapMockConfig>,
|
|
}
|
|
|
|
impl MockSwapper {
|
|
pub(crate) fn new() -> Self {
|
|
MockSwapper::default()
|
|
}
|
|
|
|
fn mock_swap_tree() -> SwapTree {
|
|
SwapTree {
|
|
claim_leaf: Leaf {
|
|
output: "".to_string(),
|
|
version: 2,
|
|
},
|
|
refund_leaf: Leaf {
|
|
output: "".to_string(),
|
|
version: 2,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn mock_public_key() -> PublicKey {
|
|
utils::generate_keypair().public_key().into()
|
|
}
|
|
|
|
fn mock_swap_details() -> ChainSwapDetails {
|
|
ChainSwapDetails {
|
|
swap_tree: Self::mock_swap_tree(),
|
|
lockup_address: "".to_string(),
|
|
server_public_key: Self::mock_public_key(),
|
|
timeout_block_height: 0,
|
|
amount: 0,
|
|
blinding_key: None,
|
|
refund_address: None,
|
|
claim_address: None,
|
|
bip21: None,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn set_zero_amount_swap_mock_config(&self, config: ZeroAmountSwapMockConfig) {
|
|
*self.zero_amount_swap_mock_config.lock().unwrap() = config;
|
|
}
|
|
|
|
fn new_chain_pair() -> ChainPair {
|
|
ChainPair {
|
|
hash: generate_random_string(10),
|
|
rate: 0.0,
|
|
limits: PairLimits {
|
|
maximal: u64::MAX,
|
|
minimal: 0,
|
|
maximal_zero_conf: 100_000,
|
|
},
|
|
fees: ChainFees {
|
|
percentage: 0.1,
|
|
miner_fees: ChainMinerFees {
|
|
server: 100,
|
|
user: PairMinerFees {
|
|
lockup: 100,
|
|
claim: 100,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
async fn get_zero_amount_swap_server_lockup_sat(&self) -> u64 {
|
|
let zero_amount_swap_mock_config = self.zero_amount_swap_mock_config.lock().unwrap();
|
|
|
|
let pair = Self::new_chain_pair();
|
|
let fees = pair
|
|
.fees
|
|
.boltz(zero_amount_swap_mock_config.user_lockup_sat)
|
|
+ pair.fees.server()
|
|
+ zero_amount_swap_mock_config.onchain_fee_increase_sat;
|
|
|
|
zero_amount_swap_mock_config.user_lockup_sat - fees
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Swapper for MockSwapper {
|
|
async fn create_chain_swap(
|
|
&self,
|
|
_req: boltz_client::swaps::boltz::CreateChainRequest,
|
|
) -> Result<CreateChainResponse, PaymentError> {
|
|
Ok(CreateChainResponse {
|
|
id: generate_random_string(4),
|
|
claim_details: Self::mock_swap_details(),
|
|
lockup_details: Self::mock_swap_details(),
|
|
})
|
|
}
|
|
|
|
async fn create_send_swap(
|
|
&self,
|
|
req: boltz_client::swaps::boltz::CreateSubmarineRequest,
|
|
) -> Result<CreateSubmarineResponse, PaymentError> {
|
|
let invoice = parse_invoice(&req.invoice)
|
|
.map_err(|err| PaymentError::invalid_invoice(&err.to_string()))?;
|
|
let Some(amount_msat) = invoice.amount_msat else {
|
|
return Err(PaymentError::invalid_invoice(
|
|
"Invoice does not contain an amount",
|
|
));
|
|
};
|
|
|
|
Ok(CreateSubmarineResponse {
|
|
accept_zero_conf: false,
|
|
address: "".to_string(),
|
|
bip21: "".to_string(),
|
|
claim_public_key: Self::mock_public_key(),
|
|
expected_amount: amount_msat / 1000,
|
|
id: generate_random_string(4),
|
|
referral_id: None,
|
|
swap_tree: Self::mock_swap_tree(),
|
|
timeout_block_height: 1459611,
|
|
blinding_key: None,
|
|
})
|
|
}
|
|
|
|
async fn get_chain_pair(
|
|
&self,
|
|
_direction: Direction,
|
|
) -> anyhow::Result<Option<ChainPair>, PaymentError> {
|
|
Ok(Some(Self::new_chain_pair()))
|
|
}
|
|
|
|
async fn get_chain_pairs(
|
|
&self,
|
|
) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError> {
|
|
let test_pair = Some(ChainPair {
|
|
hash: generate_random_string(10),
|
|
rate: 0.0,
|
|
limits: PairLimits {
|
|
maximal: u64::MAX,
|
|
minimal: 0,
|
|
maximal_zero_conf: 100_000,
|
|
},
|
|
fees: ChainFees {
|
|
percentage: 0.1,
|
|
miner_fees: ChainMinerFees {
|
|
server: 100,
|
|
user: PairMinerFees {
|
|
lockup: 100,
|
|
claim: 100,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
Ok((test_pair.clone(), test_pair))
|
|
}
|
|
|
|
async fn get_submarine_preimage(&self, _swap_id: &str) -> Result<String, PaymentError> {
|
|
Ok(Preimage::new().to_string().unwrap())
|
|
}
|
|
|
|
async fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError> {
|
|
Ok(Some(SubmarinePair {
|
|
hash: generate_random_string(10),
|
|
rate: 0.0,
|
|
limits: PairLimits {
|
|
maximal: 25_000_000,
|
|
minimal: 1_000,
|
|
maximal_zero_conf: 250_000,
|
|
},
|
|
fees: SubmarineFees {
|
|
percentage: 0.1,
|
|
miner_fees: 14,
|
|
},
|
|
}))
|
|
}
|
|
|
|
async fn get_send_claim_tx_details(
|
|
&self,
|
|
_swap: &SendSwap,
|
|
) -> Result<SubmarineClaimTxResponse, PaymentError> {
|
|
Ok(SubmarineClaimTxResponse {
|
|
preimage: Preimage::new()
|
|
.to_string()
|
|
.expect("Expected valid preimage"),
|
|
pub_nonce: "".to_string(),
|
|
public_key: Self::mock_public_key(),
|
|
transaction_hash: "".to_string(),
|
|
})
|
|
}
|
|
|
|
async fn create_claim_tx(
|
|
&self,
|
|
swap: Swap,
|
|
_claim_address: Option<String>,
|
|
) -> Result<SdkTransaction, PaymentError> {
|
|
let btc_tx = SdkTransaction::Bitcoin(boltz_client::bitcoin::Transaction {
|
|
version: lwk_wollet::bitcoin::transaction::Version::TWO,
|
|
lock_time: boltz_client::LockTime::ZERO,
|
|
input: vec![],
|
|
output: vec![],
|
|
});
|
|
let lbtc_tx = SdkTransaction::Liquid(boltz_client::elements::Transaction {
|
|
version: 2,
|
|
lock_time: boltz_client::ElementsLockTime::ZERO,
|
|
input: vec![],
|
|
output: vec![],
|
|
});
|
|
|
|
Ok(match &swap {
|
|
Swap::Chain(swap) => match swap.direction {
|
|
Direction::Incoming => lbtc_tx,
|
|
Direction::Outgoing => btc_tx,
|
|
},
|
|
Swap::Receive(_) => lbtc_tx,
|
|
Swap::Send(_) => unimplemented!(),
|
|
})
|
|
}
|
|
|
|
async fn estimate_refund_broadcast(
|
|
&self,
|
|
_swap: Swap,
|
|
_refund_address: &str,
|
|
_fee_rate_sat_per_vb: Option<f64>,
|
|
) -> Result<(u32, u64), SdkError> {
|
|
Ok((0, 0))
|
|
}
|
|
|
|
async fn create_refund_tx(
|
|
&self,
|
|
swap: Swap,
|
|
_refund_address: &str,
|
|
_utxos: Vec<Utxo>,
|
|
_broadcast_fee_rate_sat_per_vb: Option<f64>,
|
|
_is_cooperative: bool,
|
|
) -> Result<SdkTransaction, PaymentError> {
|
|
let btc_tx = SdkTransaction::Bitcoin(boltz_client::bitcoin::Transaction {
|
|
version: lwk_wollet::bitcoin::transaction::Version::TWO,
|
|
lock_time: boltz_client::LockTime::ZERO,
|
|
input: vec![],
|
|
output: vec![],
|
|
});
|
|
let lbtc_tx = SdkTransaction::Liquid(boltz_client::elements::Transaction {
|
|
version: 2,
|
|
lock_time: boltz_client::ElementsLockTime::ZERO,
|
|
input: vec![],
|
|
output: vec![],
|
|
});
|
|
|
|
Ok(match &swap {
|
|
Swap::Chain(swap) => match swap.direction {
|
|
Direction::Incoming => btc_tx,
|
|
Direction::Outgoing => lbtc_tx,
|
|
},
|
|
Swap::Send(_) => lbtc_tx,
|
|
Swap::Receive(_) => unimplemented!(),
|
|
})
|
|
}
|
|
|
|
async fn claim_send_swap_cooperative(
|
|
&self,
|
|
_swap: &SendSwap,
|
|
_claim_tx_response: boltz_client::swaps::boltz::SubmarineClaimTxResponse,
|
|
_refund_address: &str,
|
|
) -> Result<(), PaymentError> {
|
|
Ok(())
|
|
}
|
|
|
|
async fn create_receive_swap(
|
|
&self,
|
|
_req: boltz_client::swaps::boltz::CreateReverseRequest,
|
|
) -> Result<CreateReverseResponse, PaymentError> {
|
|
Ok(CreateReverseResponse {
|
|
id: generate_random_string(4),
|
|
invoice: "".to_string(),
|
|
swap_tree: Self::mock_swap_tree(),
|
|
lockup_address: "".to_string(),
|
|
refund_public_key: Self::mock_public_key(),
|
|
timeout_block_height: 0,
|
|
onchain_amount: 0,
|
|
blinding_key: None,
|
|
})
|
|
}
|
|
|
|
async fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError> {
|
|
Ok(Some(ReversePair {
|
|
hash: "".to_string(),
|
|
rate: 0.0,
|
|
limits: ReverseLimits {
|
|
maximal: 25_000_000,
|
|
minimal: 1_000,
|
|
},
|
|
fees: ReverseFees {
|
|
percentage: 0.25,
|
|
miner_fees: PairMinerFees {
|
|
lockup: 14,
|
|
claim: 26,
|
|
},
|
|
},
|
|
}))
|
|
}
|
|
|
|
async fn broadcast_tx(
|
|
&self,
|
|
_chain: boltz_client::network::Chain,
|
|
tx_hex: &str,
|
|
) -> Result<String, PaymentError> {
|
|
let tx = utils::deserialize_tx_hex(tx_hex)?;
|
|
Ok(tx.txid().to_string())
|
|
}
|
|
|
|
fn create_status_stream(&self) -> Box<dyn crate::swapper::SwapperStatusStream> {
|
|
Box::new(MockStatusStream::new())
|
|
}
|
|
|
|
async fn check_for_mrh(
|
|
&self,
|
|
_invoice: &str,
|
|
) -> Result<Option<(String, boltz_client::bitcoin::Amount)>, PaymentError> {
|
|
// Ok(Some(("".to_string(), 0.0)))
|
|
unimplemented!()
|
|
}
|
|
|
|
async fn get_bolt12_invoice(
|
|
&self,
|
|
_offer: &str,
|
|
_amount_sat: u64,
|
|
) -> Result<String, PaymentError> {
|
|
unimplemented!()
|
|
}
|
|
|
|
async fn get_zero_amount_chain_swap_quote(&self, _swap_id: &str) -> Result<Amount, SdkError> {
|
|
let server_lockup_amount_sat = self.get_zero_amount_swap_server_lockup_sat().await;
|
|
Ok(Amount::from_sat(server_lockup_amount_sat))
|
|
}
|
|
|
|
async fn accept_zero_amount_chain_swap_quote(
|
|
&self,
|
|
_swap_id: &str,
|
|
server_lockup_sat: u64,
|
|
) -> Result<(), PaymentError> {
|
|
ensure_sdk!(
|
|
server_lockup_sat == self.get_zero_amount_swap_server_lockup_sat().await,
|
|
PaymentError::InvalidOrExpiredFees
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub(crate) struct MockProxyUrlFetcher {}
|
|
|
|
impl MockProxyUrlFetcher {
|
|
pub(crate) fn new() -> Self {
|
|
Self {}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ProxyUrlFetcher for MockProxyUrlFetcher {
|
|
async fn fetch(&self) -> Result<&Option<String>> {
|
|
Ok(&None)
|
|
}
|
|
}
|