Skip to content

Commit

Permalink
[r2r] Extend swap watcher node functionality for UTXO (#1496)
Browse files Browse the repository at this point in the history
* add initial swap watcher node functionality

* broadcast watcher message immediately after taker payment is sent

* rename the WatcherData struct to TakerSwapWatcherData

* use separate SwapWatcherMsg for watchers

* make taker send the entire spending transaction to watcher nodes

* broadcast watcher message periodically in wait_for_taker_payment_spend

* check if transaction outputs is empty in p2sh_spending_tx

* fix minor details

* move utxo coin generation methods to docker_tests_common

* use Mm2TestConf for watcher node tests

* prevent running multiple watcher threads for the same swap

* remove async from spawn_taker_swap_watcher

* check if htlc spend fee is greater than the transaction output

* add empty line at the end of docker_tests_common

* discard prev_transaction mutability after modification

* check if transaction outputs is empty in utxo_common functions

* use the drop_mutability macro

* move all watcher related code to the swap_watcher module

* improve the taker_swap_watchers usage

* remove unused types and structs

* check if the transaction script has instructions

* check if the transaction has any inputs

* fix minor stuff

* make watchers validate taker payment at most once

* release taker_swap_watchers lock after inserting the uuid to the set

* fix the error type in instruction iterator

* reorder taker_swap_watchers lock/release structure

* use separate files for large json test strings

* remove watcher arguments from test helper functions

* propagate watcher messages whether the node is watcher or not

* remove swap lock related code from the swap_watcher

* remove running_swaps related code from swap_watcher

* check if a watcher with the same uuid exists before spawning a thread

* fix minor problems

* remove AtomicSwap implementation from swap_watcher

* make taker trigger a WatcherMessageSent event only if it is actually sent

* remove an unnecessary line

* log error if watcher message could not be sent

* improve mutex usage for the taker_swap_watchers

* fix formatting

* add state machines, watcher refund and ValidatePaymentError

* use DuplicateCache for taker_swap_watchers

* remove println statements

* make the watchers wait for half the locktime before spending the maker payment

* make the watchers wait for the taker before refunding

* add refund transaction confirmation for watchers

* check at watchers if the taker payment is already spent or refunded

* validate the public keys in the watcher message

* validate taker fees at watchers

* fix state implementation return types

* fix a bug in test_watcher_node

* change the name of test_watcher_node

* fix a bug in test_validate_maker_payment_malicious

* Fix ValidatePaymentError usage

* add preimage suffix to watcher-related transaction methods

* fix error handling

* separate WatcherOps from SwapOps

* fix error match usage

* reduce bchd_grpc module visibility to pub(crate)

* fix a bug in qrc20_tests

* inline WatcherOps functions

* increase dynamic fees for watcher preimages

* fix ethereum watcher preimage fee approximation

* take back unnecessary changes

* inline SwapOps functions

* use taker fee hash for logs instead of uuid

* extend TAKER_SWAP_ENTRY_TIMEOUT to 6 hours

* remove swap_unique_data field from WatcherSearchForSwapTxSpendInput struct

* fix error handling

* remove unnecessary taker swap removal in Stopped state

* improve match usage

* use WAIT_FOR_TAKER_PAYMENT_INTERVAL const

* inline DuplicateCache remove method

* add functions for wait taker/maker payment confirmation durations

* improve error handling

* rename check_all_inputs_signed_by_pub method

* add small fixes

* fix WatcherError naming
  • Loading branch information
caglaryucekaya authored Oct 26, 2022
1 parent 4de3cf2 commit 4d2071f
Show file tree
Hide file tree
Showing 25 changed files with 1,682 additions and 719 deletions.
2 changes: 1 addition & 1 deletion mm2src/coins/coin_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub enum ValidatePaymentError {
InternalError(String),
// Problem with deserializing the transaction, or one of the transaction parts is invalid.
TxDeserializationError(String),
InvalidInput(String),
InvalidParameter(String),
InvalidRpcResponse(String),
SPVError(SPVError),
UnexpectedPaymentState(String),
Expand Down
93 changes: 65 additions & 28 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CoinBalance, CoinFu
TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr,
TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult,
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError,
VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest,
WithdrawResult};
VerificationResult, WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult};

pub use rlp;

Expand Down Expand Up @@ -104,6 +104,8 @@ const ETH_DECIMALS: u8 = 18;

/// Take into account that the dynamic fee may increase by 3% during the swap.
const GAS_PRICE_APPROXIMATION_PERCENT_ON_START_SWAP: u64 = 3;
/// Take into account that the dynamic fee may increase until the locktime is expired
const GAS_PRICE_APPROXIMATION_PERCENT_ON_WATCHER_PREIMAGE: u64 = 3;
/// Take into account that the dynamic fee may increase at each of the following stages:
/// - it may increase by 2% until a swap is started;
/// - it may increase by 3% during the swap.
Expand Down Expand Up @@ -798,21 +800,6 @@ impl SwapOps for EthCoin {
)
}

fn send_taker_spends_maker_payment_preimage(&self, _preimage: &[u8], _secret: &[u8]) -> TransactionFut {
unimplemented!();
}

fn create_taker_spends_maker_payment_preimage(
&self,
_maker_payment_tx: &[u8],
_time_lock: u32,
_maker_pub: &[u8],
_secret_hash: &[u8],
_swap_unique_data: &[u8],
) -> TransactionFut {
unimplemented!();
}

fn send_taker_spends_maker_payment(
&self,
maker_payment_tx: &[u8],
Expand Down Expand Up @@ -981,7 +968,7 @@ impl SwapOps for EthCoin {
let swap_contract_address = try_f!(input
.swap_contract_address
.try_to_address()
.map_to_mm(ValidatePaymentError::InvalidInput));
.map_to_mm(ValidatePaymentError::InvalidParameter));
self.validate_payment(
&input.payment_tx,
input.time_lock,
Expand All @@ -996,7 +983,7 @@ impl SwapOps for EthCoin {
let swap_contract_address = try_f!(input
.swap_contract_address
.try_to_address()
.map_to_mm(ValidatePaymentError::InvalidInput));
.map_to_mm(ValidatePaymentError::InvalidParameter));
self.validate_payment(
&input.payment_tx,
input.time_lock,
Expand All @@ -1007,13 +994,6 @@ impl SwapOps for EthCoin {
)
}

fn watcher_validate_taker_payment(
&self,
_input: WatcherValidatePaymentInput,
) -> Box<dyn Future<Item = (), Error = MmError<ValidatePaymentError>> + Send> {
unimplemented!();
}

fn check_if_my_payment_sent(
&self,
time_lock: u32,
Expand Down Expand Up @@ -1113,6 +1093,10 @@ impl SwapOps for EthCoin {
.await
}

fn check_tx_signed_by_pub(&self, _tx: &[u8], _expected_pub: &[u8]) -> Result<bool, String> {
unimplemented!();
}

fn extract_secret(&self, _secret_hash: &[u8], spend_tx: &[u8]) -> Result<Vec<u8>, String> {
let unverified: UnverifiedTransaction = try_s!(rlp::decode(spend_tx));
let function = try_s!(SWAP_CONTRACT.function("receiverSpend"));
Expand Down Expand Up @@ -1155,6 +1139,7 @@ impl SwapOps for EthCoin {
}
}

#[inline]
fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> keys::KeyPair {
key_pair_from_secret(self.key_pair.secret()).expect("valid key")
}
Expand All @@ -1167,6 +1152,55 @@ impl SwapOps for EthCoin {
}
}

#[async_trait]
impl WatcherOps for EthCoin {
fn send_taker_spends_maker_payment_preimage(&self, _preimage: &[u8], _secret: &[u8]) -> TransactionFut {
unimplemented!();
}

fn create_taker_spends_maker_payment_preimage(
&self,
_maker_payment_tx: &[u8],
_time_lock: u32,
_maker_pub: &[u8],
_secret_hash: &[u8],
_swap_unique_data: &[u8],
) -> TransactionFut {
unimplemented!();
}

fn create_taker_refunds_payment_preimage(
&self,
_taker_payment_tx: &[u8],
_time_lock: u32,
_maker_pub: &[u8],
_secret_hash: &[u8],
_swap_contract_address: &Option<BytesJson>,
_swap_unique_data: &[u8],
) -> TransactionFut {
unimplemented!();
}

fn send_watcher_refunds_taker_payment_preimage(&self, _taker_refunds_payment: &[u8]) -> TransactionFut {
unimplemented!();
}

fn watcher_validate_taker_fee(&self, _taker_fee_hash: Vec<u8>, _verified_pub: Vec<u8>) -> ValidatePaymentFut<()> {
unimplemented!();
}

fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> {
unimplemented!();
}

async fn watcher_search_for_swap_tx_spend(
&self,
_input: WatcherSearchForSwapTxSpendInput<'_>,
) -> Result<Option<FoundSwapTxSpend>, String> {
unimplemented!();
}
}

#[cfg_attr(test, mockable)]
impl MarketCoinOps for EthCoin {
fn ticker(&self) -> &str { &self.ticker[..] }
Expand Down Expand Up @@ -2787,7 +2821,7 @@ impl EthCoin {
let tx =
try_f!(SignedEthTx::new(unsigned)
.map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string())));
let sender = try_f!(addr_from_raw_pubkey(sender_pub).map_to_mm(ValidatePaymentError::InvalidInput));
let sender = try_f!(addr_from_raw_pubkey(sender_pub).map_to_mm(ValidatePaymentError::InvalidParameter));
let expected_value = try_f!(wei_from_big_decimal(&amount, self.decimals));
let selfi = self.clone();
let secret_hash = secret_hash.to_vec();
Expand All @@ -2814,7 +2848,7 @@ impl EthCoin {
let tx_from_rpc = match tx_from_rpc {
Some(t) => t,
None => {
return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!(
return MmError::err(ValidatePaymentError::InvalidRpcResponse(format!(
"Didn't find provided tx {:?} on ETH node",
tx
)))
Expand Down Expand Up @@ -3876,5 +3910,8 @@ fn increase_gas_price_by_stage(gas_price: U256, level: &FeeApproxStage) -> U256
FeeApproxStage::TradePreimage => {
increase_by_percent_one_gwei(gas_price, GAS_PRICE_APPROXIMATION_PERCENT_ON_TRADE_PREIMAGE)
},
FeeApproxStage::WatcherPreimage => {
increase_by_percent_one_gwei(gas_price, GAS_PRICE_APPROXIMATION_PERCENT_ON_WATCHER_PREIMAGE)
},
}
}
81 changes: 56 additions & 25 deletions mm2src/coins/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod ln_storage;
mod ln_utils;

use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum};
use crate::coin_errors::{MyAddressError, ValidatePaymentError};
use crate::coin_errors::MyAddressError;
use crate::lightning::ln_conf::OurChannelsConfigs;
use crate::lightning::ln_errors::{TrustedNodeError, TrustedNodeResult, UpdateChannelError, UpdateChannelResult};
use crate::lightning::ln_events::init_abortable_events;
Expand All @@ -26,8 +26,8 @@ use crate::{BalanceFut, CoinBalance, CoinFutSpawner, FeeApproxStage, FoundSwapTx
SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut,
TradePreimageResult, TradePreimageValue, TransactionEnum, TransactionFut, TxMarshalingErr,
UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateOtherPubKeyErr,
ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult,
WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest};
ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WatcherOps,
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest};
use async_trait::async_trait;
use bitcoin::hashes::Hash;
use bitcoin_hashes::sha256::Hash as Sha256;
Expand Down Expand Up @@ -319,17 +319,6 @@ impl SwapOps for LightningCoin {
unimplemented!()
}

fn create_taker_spends_maker_payment_preimage(
&self,
_maker_payment_tx: &[u8],
_time_lock: u32,
_maker_pub: &[u8],
_secret_hash: &[u8],
_swap_unique_data: &[u8],
) -> TransactionFut {
unimplemented!();
}

fn send_taker_spends_maker_payment(
&self,
_maker_payment_tx: &[u8],
Expand All @@ -343,10 +332,6 @@ impl SwapOps for LightningCoin {
unimplemented!()
}

fn send_taker_spends_maker_payment_preimage(&self, _preimage: &[u8], _secret: &[u8]) -> TransactionFut {
unimplemented!();
}

fn send_taker_refunds_payment(
&self,
_taker_payment_tx: &[u8],
Expand Down Expand Up @@ -387,13 +372,6 @@ impl SwapOps for LightningCoin {

fn validate_taker_payment(&self, _input: ValidatePaymentInput) -> ValidatePaymentFut<()> { unimplemented!() }

fn watcher_validate_taker_payment(
&self,
_input: WatcherValidatePaymentInput,
) -> Box<dyn Future<Item = (), Error = MmError<ValidatePaymentError>> + Send> {
unimplemented!();
}

fn check_if_my_payment_sent(
&self,
_time_lock: u32,
Expand Down Expand Up @@ -421,6 +399,10 @@ impl SwapOps for LightningCoin {
unimplemented!()
}

fn check_tx_signed_by_pub(&self, _tx: &[u8], _expected_pub: &[u8]) -> Result<bool, String> {
unimplemented!();
}

fn extract_secret(&self, _secret_hash: &[u8], _spend_tx: &[u8]) -> Result<Vec<u8>, String> { unimplemented!() }

fn negotiate_swap_contract_addr(
Expand All @@ -435,6 +417,55 @@ impl SwapOps for LightningCoin {
fn validate_other_pubkey(&self, _raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr> { unimplemented!() }
}

#[async_trait]
impl WatcherOps for LightningCoin {
fn create_taker_spends_maker_payment_preimage(
&self,
_maker_payment_tx: &[u8],
_time_lock: u32,
_maker_pub: &[u8],
_secret_hash: &[u8],
_swap_unique_data: &[u8],
) -> TransactionFut {
unimplemented!();
}

fn send_taker_spends_maker_payment_preimage(&self, _preimage: &[u8], _secret: &[u8]) -> TransactionFut {
unimplemented!();
}

fn create_taker_refunds_payment_preimage(
&self,
_taker_payment_tx: &[u8],
_time_lock: u32,
_maker_pub: &[u8],
_secret_hash: &[u8],
_swap_contract_address: &Option<BytesJson>,
_swap_unique_data: &[u8],
) -> TransactionFut {
unimplemented!();
}

fn send_watcher_refunds_taker_payment_preimage(&self, _taker_refunds_payment: &[u8]) -> TransactionFut {
unimplemented!();
}

fn watcher_validate_taker_fee(&self, _taker_fee_hash: Vec<u8>, _verified_pub: Vec<u8>) -> ValidatePaymentFut<()> {
unimplemented!();
}

fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> {
unimplemented!();
}

async fn watcher_search_for_swap_tx_spend(
&self,
_input: WatcherSearchForSwapTxSpendInput<'_>,
) -> Result<Option<FoundSwapTxSpend>, String> {
unimplemented!();
}
}

impl MarketCoinOps for LightningCoin {
fn ticker(&self) -> &str { &self.conf.ticker }

Expand Down
Loading

0 comments on commit 4d2071f

Please sign in to comment.