Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Maker fees & Document fees workflow. #355

Merged
merged 5 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion maker.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Think about this?
KnowWhoami marked this conversation as resolved.
Show resolved Hide resolved
[maker_config]
# Listening port
port = 6102
Expand All @@ -11,7 +12,7 @@ directory_servers_refresh_interval_secs = 43200
# Time interval to close a connection if no response is received
idle_connection_timeout = 300
# Absolute coinswap fee
absolute_fee_sats = 1000
base_fee_sats = 1000
# Fee rate per swap amount in ppb.
amount_relative_fee_ppb = 10000000
# Fee rate for timelocked contract in ppb
Expand Down
11 changes: 6 additions & 5 deletions src/bin/directoryd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
#[clap(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
struct Cli {
/// Optional network type.
#[clap(long, short = 'n', default_value = "tor", possible_values = &["tor", "clearnet"])]
network: String,
/// Optional DNS data directory. Default value : "~/.coinswap/dns"
#[clap(long, short = 'd')]
data_directory: Option<PathBuf>,
Expand Down Expand Up @@ -53,15 +50,19 @@

let rpc_network = bitcoin::Network::from_str(&args.rpc_network).unwrap();

let conn_type = ConnectionType::from_str(&args.network)?;

let rpc_config = RPCConfig {
url: args.rpc,
auth: Auth::UserPass(args.auth.0, args.auth.1),
network: rpc_network,
wallet_name: "random".to_string(), // we can put anything here as it will get updated in the init.
};

let conn_type = if cfg!(feature = "integration-test") {
ConnectionType::CLEARNET

Check warning on line 61 in src/bin/directoryd.rs

View check run for this annotation

Codecov / codecov/patch

src/bin/directoryd.rs#L61

Added line #L61 was not covered by tests
} else {
ConnectionType::TOR
};

#[cfg(feature = "tor")]
{
if conn_type == ConnectionType::TOR {
Expand Down
11 changes: 6 additions & 5 deletions src/bin/makerd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
#[clap(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
struct Cli {
/// Optional Connection Network Type
#[clap(long, default_value = "tor", possible_values = &["tor", "clearnet"])]
network: String,
/// Optional DNS data directory. Default value : "~/.coinswap/maker"
#[clap(long, short = 'd')]
data_directory: Option<PathBuf>,
Expand Down Expand Up @@ -60,15 +57,19 @@

let rpc_network = bitcoin::Network::from_str(&args.rpc_network).unwrap();

let conn_type = ConnectionType::from_str(&args.network)?;

let rpc_config = RPCConfig {
url: args.rpc,
auth: Auth::UserPass(args.auth.0, args.auth.1),
network: rpc_network,
wallet_name: "random".to_string(), // we can put anything here as it will get updated in the init.
};

let conn_type = if cfg!(feature = "integration-test") {
ConnectionType::CLEARNET

Check warning on line 68 in src/bin/makerd.rs

View check run for this annotation

Codecov / codecov/patch

src/bin/makerd.rs#L68

Added line #L68 was not covered by tests
} else {
ConnectionType::TOR
};

#[cfg(feature = "tor")]
{
if conn_type == ConnectionType::TOR {
Expand Down
11 changes: 6 additions & 5 deletions src/bin/taker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
#[clap(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
struct Cli {
/// Optional Connection Network Type
#[clap(long, default_value = "clearnet",short= 'c', possible_values = &["tor","clearnet"])]
connection_type: String,
/// Optional DNS data directory. Default value : "~/.coinswap/taker"
#[clap(long, short = 'd')]
data_directory: Option<PathBuf>,
Expand Down Expand Up @@ -56,7 +53,7 @@
pub tx_count: u32,
/// Sets the required on-chain confirmations.
#[clap(name = "required_confirms", default_value = "1000")]
pub required_confirms: u64,
pub required_confirms: u32,
/// List of sub commands to process various endpoints of taker cli app.
#[clap(subcommand)]
command: Commands,
Expand Down Expand Up @@ -101,7 +98,11 @@
let args = Cli::parse();

let rpc_network = bitcoin::Network::from_str(&args.bitcoin_network).unwrap();
let connection_type = ConnectionType::from_str(&args.connection_type)?;
let connection_type = if cfg!(feature = "integration-test") {
ConnectionType::CLEARNET

Check warning on line 102 in src/bin/taker.rs

View check run for this annotation

Codecov / codecov/patch

src/bin/taker.rs#L102

Added line #L102 was not covered by tests
} else {
ConnectionType::TOR
};

let rpc_config = RPCConfig {
url: args.rpc,
Expand Down
72 changes: 63 additions & 9 deletions src/maker/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
messages::{FidelityProof, ReqContractSigsForSender},
Hash160,
},
utill::{get_maker_dir, redeemscript_to_scriptpubkey, ConnectionType},
utill::{get_maker_dir, redeemscript_to_scriptpubkey, ConnectionType, HEART_BEAT_INTERVAL},
wallet::{RPCConfig, SwapCoin, WalletSwapCoin},
};
use bitcoin::{
Expand Down Expand Up @@ -46,9 +46,63 @@ use crate::{

use super::{config::MakerConfig, error::MakerError};

use crate::maker::server::{
HEART_BEAT_INTERVAL_SECS, MIN_CONTRACT_REACTION_TIME, REQUIRED_CONFIRMS,
};
/// Interval for health checks on a stable RPC connection with bitcoind.
pub const RPC_PING_INTERVAL: Duration = Duration::from_secs(60);

// Currently we don't refresh address at DNS. The Maker only post it once at startup.
// If the address record gets deleted, or the DNS gets blasted, the Maker won't know.
// TODO: Make the maker repost their address to DNS once a day in spawned thread.
// pub const DIRECTORY_SERVERS_REFRESH_INTERVAL_SECS: u64 = Duartion::from_days(1); // Once a day.

/// Maker triggers the recovery mechanism, if Taker is idle for more than 300 secs.
pub const IDLE_CONNECTION_TIMEOUT: Duration = Duration::from_secs(300);

/// Number of confirmation required funding transaction.
pub const REQUIRED_CONFIRMS: u32 = 1;

/// The minimum difference in locktime (in blocks) between the incoming and outgoing swaps.
///
/// This value specifies the reaction time, in blocks, available to a Maker
/// to claim the refund transaction in case of recovery.
///
/// According to [BOLT #2](https://github.com/lightning/bolts/blob/aa5207aeaa32d841353dd2df3ce725a4046d528d/02-peer-protocol.md?plain=1#L1798),
/// the estimated minimum `cltv_expiry_delta` is 18 blocks.
/// To enhance safety, the default value is set to 20 blocks.
pub const MIN_CONTRACT_REACTION_TIME: u16 = 20;

/// # Fee Parameters for Coinswap
///
/// These parameters define the fees charged by Makers in a coinswap transaction.
///
/// TODO: These parameters are currently hardcoded. Consider making them configurable for Makers in the future.
///p
/// - `BASE_FEE`: A fixed base fee charged by the Maker for providing its services
/// - `AMOUNT_RELATIVE_FEE_PCT`: A percentage fee based on the swap amount.
/// - `TIME_RELATIVE_FEE_PCT`: A percentage fee based on the refund locktime (duration the Maker must wait for a refund).
///
/// The coinswap fee increases with both the swap amount and the refund locktime.
/// Refer to `REFUND_LOCKTIME` and `REFUND_LOCKTIME_STEP` in `taker::api.rs` for related parameters.
///
/// ### Fee Calculation
/// The total fee for a swap is calculated as:
/// `total_fee = base_fee + (swap_amount * amount_relative_fee_pct) / 100 + (swap_amount * refund_locktime * time_relative_fee_pct) / 100`
///
/// ### Example (Default Values)
/// For a swap amount of 100,000 sats and a refund locktime of 20 blocks:
/// - `base_fee` = 1,000 sats
/// - `amount_relative_fee` = (100,000 * 2.5) / 100 = 2,500 sats
/// - `time_relative_fee` = (100,000 * 20 * 0.1) / 100 = 2,000 sats
/// - `total_fee` = 5,500 sats (5.5%)
///
/// Fee rates are designed to asymptotically approach 5% of the swap amount as the swap amount increases..
pub const BASE_FEE: u64 = 1000;
pub const AMOUNT_RELATIVE_FEE_PCT: f64 = 2.50;
pub const TIME_RELATIVE_FEE_PCT: f64 = 0.10;

/// Minimum Coinswap amount; makers will not accept amounts below this.
pub const MIN_SWAP_AMOUNT: u64 = 100000;

// What's the use of RefundLocktimeStep?

/// Used to configure the maker for testing purposes.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -278,7 +332,7 @@ impl Maker {
// check that the new locktime is sufficently short enough compared to the
// locktime in the provided funding tx
let locktime = read_contract_locktime(&funding_info.contract_redeemscript)?;
if locktime - message.next_locktime < MIN_CONTRACT_REACTION_TIME {
if locktime - message.refund_locktime < MIN_CONTRACT_REACTION_TIME {
return Err(MakerError::General(
"Next hop locktime too close to current hop locktime",
));
Expand All @@ -298,7 +352,7 @@ impl Maker {
)
.map_err(WalletError::Rpc)?
{
if txout.confirmations < (REQUIRED_CONFIRMS as u32) {
if txout.confirmations < REQUIRED_CONFIRMS {
return Err(MakerError::General(
"funding tx not confirmed to required depth",
));
Expand Down Expand Up @@ -525,7 +579,7 @@ pub fn check_for_broadcasted_contracts(maker: Arc<Maker>) -> Result<(), MakerErr
}
} // All locks are cleared here.

std::thread::sleep(Duration::from_secs(HEART_BEAT_INTERVAL_SECS));
std::thread::sleep(HEART_BEAT_INTERVAL);
}

Ok(())
Expand Down Expand Up @@ -558,7 +612,7 @@ pub fn check_for_idle_states(maker: Arc<Maker>) -> Result<(), MakerError> {
ip,
no_response_since
);
if no_response_since > std::time::Duration::from_secs(60) {
if no_response_since > IDLE_CONNECTION_TIMEOUT {
log::error!(
"[{}] Potential Dropped Connection from {}",
maker.config.port,
Expand Down Expand Up @@ -610,7 +664,7 @@ pub fn check_for_idle_states(maker: Arc<Maker>) -> Result<(), MakerError> {
}
} // All locks are cleared here

std::thread::sleep(Duration::from_secs(HEART_BEAT_INTERVAL_SECS));
std::thread::sleep(HEART_BEAT_INTERVAL);
}

Ok(())
Expand Down
41 changes: 11 additions & 30 deletions src/maker/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,21 @@
use crate::utill::parse_toml;
use std::{io, path::Path};

use bitcoin::Amount;
use std::io::Write;

use crate::utill::{get_maker_dir, parse_field, ConnectionType};

use super::api::MIN_SWAP_AMOUNT;

/// Maker Configuration, controlling various maker behavior.
#[derive(Debug, Clone, PartialEq)]
pub struct MakerConfig {
/// Network listening port
pub port: u16,
/// RPC listening port
pub rpc_port: u16,
/// Absolute coinswap fee
pub absolute_fee_sats: Amount,
/// Fee rate for timelocked contract in parts per billion (PPB).
/// Similar to `DEFAULT_AMOUNT_RELATIVE_FEE_PPB`, calculated as (amount * fee_ppb) / 1_000_000_000.
pub time_relative_fee_ppb: Amount,
/// Minimum timelock difference between contract transaction of two hops
pub min_size: u64,
/// Minimum swap size.
pub min_swap_amount: u64,
/// Socks port
pub socks_port: u16,
/// Directory server address (can be clearnet or onion)
Expand All @@ -39,9 +35,7 @@ impl Default for MakerConfig {
Self {
port: 6102,
rpc_port: 6103,
absolute_fee_sats: Amount::from_sat(1000),
time_relative_fee_ppb: Amount::from_sat(100_000),
min_size: 10_000,
min_swap_amount: MIN_SWAP_AMOUNT,
socks_port: 19050,
directory_server_address: "127.0.0.1:8080".to_string(),
fidelity_value: 5_000_000, // 5 million sats
Expand Down Expand Up @@ -97,21 +91,15 @@ impl MakerConfig {
Ok(MakerConfig {
port: parse_field(config_map.get("port"), default_config.port),
rpc_port: parse_field(config_map.get("rpc_port"), default_config.rpc_port),
absolute_fee_sats: parse_field(
config_map.get("absolute_fee_sats"),
default_config.absolute_fee_sats,
),
time_relative_fee_ppb: parse_field(
config_map.get("time_relative_fee_ppb"),
default_config.time_relative_fee_ppb,
min_swap_amount: parse_field(
config_map.get("min_swap_amount"),
default_config.min_swap_amount,
),
min_size: parse_field(config_map.get("min_size"), default_config.min_size),
socks_port: parse_field(config_map.get("socks_port"), default_config.socks_port),
directory_server_address: parse_field(
config_map.get("directory_server_address"),
default_config.directory_server_address,
),

fidelity_value: parse_field(
config_map.get("fidelity_value"),
default_config.fidelity_value,
Expand All @@ -132,19 +120,15 @@ impl MakerConfig {
let toml_data = format!(
"port = {}
rpc_port = {}
absolute_fee_sats = {}
time_relative_fee_ppb = {}
min_size = {}
min_swap_amount = {}
socks_port = {}
directory_server_address = {}
fidelity_value = {}
fidelity_timelock = {}
connection_type = {:?}",
self.port,
self.rpc_port,
self.absolute_fee_sats,
self.time_relative_fee_ppb,
self.min_size,
self.min_swap_amount,
self.socks_port,
self.directory_server_address,
self.fidelity_value,
Expand Down Expand Up @@ -187,12 +171,9 @@ mod tests {
[maker_config]
port = 6102
rpc_port = 6103
absolute_fee_sats = 1000
amount_relative_fee_ppb = 10000000
time_relative_fee_ppb = 100000
required_confirms = 1
min_contract_reaction_time = 48
min_size = 10000
min_swap_amount = 100000
socks_port = 19050
"#;
let config_path = create_temp_config(contents, "valid_maker_config.toml");
Expand Down
Loading
Loading