2020-10-15 22:14:39 +00:00
#![ warn(
unused_extern_crates ,
missing_copy_implementations ,
rust_2018_idioms ,
clippy ::cast_possible_truncation ,
clippy ::cast_sign_loss ,
clippy ::fallible_impl_from ,
clippy ::cast_precision_loss ,
clippy ::cast_possible_wrap ,
clippy ::dbg_macro
) ]
#![ forbid(unsafe_code) ]
2020-12-23 03:33:29 +00:00
#![ allow(non_snake_case) ]
2020-10-15 22:14:39 +00:00
2021-01-27 02:33:32 +00:00
use crate ::{
2021-02-01 05:10:43 +00:00
cli ::{ Cancel , Command , Options , Resume } ,
2021-01-29 06:31:18 +00:00
config ::{
2021-01-27 02:33:32 +00:00
initial_setup , query_user_for_initial_testnet_config , read_config , ConfigNotInitialized ,
} ,
2021-01-29 06:27:50 +00:00
execution_params ::GetExecutionParams ,
2021-02-01 05:10:43 +00:00
protocol ::bob ::cancel ::CancelError ,
2021-01-27 02:33:32 +00:00
} ;
2021-01-18 10:57:17 +00:00
use anyhow ::{ Context , Result } ;
2021-01-21 03:00:37 +00:00
use database ::Database ;
2021-01-27 02:33:32 +00:00
use fs ::{ default_config_path , default_data_dir } ;
2021-01-15 05:58:16 +00:00
use log ::LevelFilter ;
2020-12-04 05:27:17 +00:00
use prettytable ::{ row , Table } ;
2021-01-21 03:00:37 +00:00
use protocol ::{ alice , bob , bob ::Builder , SwapAmounts } ;
2021-01-27 02:33:32 +00:00
use std ::{ path ::PathBuf , sync ::Arc } ;
2020-12-04 05:27:17 +00:00
use structopt ::StructOpt ;
2021-01-21 03:00:37 +00:00
use trace ::init_tracing ;
2021-01-15 05:58:16 +00:00
use tracing ::info ;
2021-01-21 02:43:25 +00:00
use uuid ::Uuid ;
2021-01-21 03:00:37 +00:00
pub mod bitcoin ;
2021-01-29 06:31:18 +00:00
pub mod config ;
2021-01-21 03:00:37 +00:00
pub mod database ;
2021-01-29 06:27:50 +00:00
pub mod execution_params ;
2021-01-21 03:00:37 +00:00
pub mod monero ;
pub mod network ;
pub mod protocol ;
pub mod seed ;
pub mod trace ;
2021-01-14 00:35:10 +00:00
2021-01-22 03:56:11 +00:00
mod cli ;
mod fs ;
mod serde_peer_id ;
2020-12-04 05:27:17 +00:00
#[ macro_use ]
extern crate prettytable ;
2020-10-15 22:14:39 +00:00
#[ tokio::main ]
async fn main ( ) -> Result < ( ) > {
2021-01-11 06:38:59 +00:00
init_tracing ( LevelFilter ::Info ) . expect ( " initialize tracing " ) ;
2020-12-04 05:27:17 +00:00
let opt = Options ::from_args ( ) ;
2021-01-27 02:33:32 +00:00
let data_dir = if let Some ( data_dir ) = opt . data_dir {
data_dir
} else {
default_data_dir ( ) . context ( " unable to determine default data path " ) ?
} ;
2020-12-04 05:27:17 +00:00
2021-01-08 01:04:48 +00:00
info! (
" Database and Seed will be stored in directory: {} " ,
2021-01-27 02:33:32 +00:00
data_dir . display ( )
2021-01-08 01:04:48 +00:00
) ;
2021-01-18 10:24:13 +00:00
2021-01-27 02:33:32 +00:00
let db_path = data_dir . join ( " database " ) ;
2021-01-29 06:31:18 +00:00
let seed = config ::seed ::Seed ::from_file_or_generate ( & data_dir )
2021-01-08 01:04:48 +00:00
. expect ( " Could not retrieve/initialize seed " )
. into ( ) ;
2020-12-15 10:26:02 +00:00
2021-01-27 02:33:32 +00:00
// hardcode to testnet/stagenet
let bitcoin_network = bitcoin ::Network ::Testnet ;
let monero_network = monero ::Network ::Stagenet ;
2021-01-29 06:27:50 +00:00
let execution_params = execution_params ::Testnet ::get_execution_params ( ) ;
2021-01-27 02:33:32 +00:00
2020-12-15 10:26:02 +00:00
match opt . cmd {
Command ::SellXmr {
2020-12-04 05:27:17 +00:00
listen_addr ,
send_monero ,
receive_bitcoin ,
2021-01-27 02:33:32 +00:00
config ,
2020-12-04 05:27:17 +00:00
} = > {
2021-01-18 10:24:13 +00:00
let swap_amounts = SwapAmounts {
xmr : send_monero ,
btc : receive_bitcoin ,
} ;
2021-01-27 02:33:32 +00:00
let ( bitcoin_wallet , monero_wallet ) =
init_wallets ( config . path , bitcoin_network , monero_network ) . await ? ;
2020-12-04 05:27:17 +00:00
2020-12-07 01:47:21 +00:00
let swap_id = Uuid ::new_v4 ( ) ;
2021-01-18 10:57:17 +00:00
info! (
2021-01-20 02:30:35 +00:00
" Swap sending {} and receiving {} started with ID {} " ,
2021-01-18 10:57:17 +00:00
send_monero , receive_bitcoin , swap_id
) ;
2021-01-19 04:21:40 +00:00
let alice_factory = alice ::Builder ::new (
2021-01-18 10:24:13 +00:00
seed ,
2021-01-27 02:33:32 +00:00
execution_params ,
2020-12-15 10:26:02 +00:00
swap_id ,
2021-01-20 02:36:38 +00:00
Arc ::new ( bitcoin_wallet ) ,
Arc ::new ( monero_wallet ) ,
2021-01-18 10:24:13 +00:00
db_path ,
listen_addr ,
2021-01-22 02:33:31 +00:00
) ;
2021-01-19 04:21:40 +00:00
let ( swap , mut event_loop ) =
alice_factory . with_init_params ( swap_amounts ) . build ( ) . await ? ;
2021-01-18 10:24:13 +00:00
tokio ::spawn ( async move { event_loop . run ( ) . await } ) ;
alice ::run ( swap ) . await ? ;
2020-12-04 05:27:17 +00:00
}
2020-12-15 10:26:02 +00:00
Command ::BuyXmr {
2020-12-18 06:39:04 +00:00
alice_peer_id ,
2020-12-04 05:27:17 +00:00
alice_addr ,
send_bitcoin ,
receive_monero ,
2021-01-27 02:33:32 +00:00
config ,
2020-12-04 05:27:17 +00:00
} = > {
2021-01-18 10:57:17 +00:00
let swap_amounts = SwapAmounts {
btc : send_bitcoin ,
xmr : receive_monero ,
} ;
2021-01-27 02:33:32 +00:00
let ( bitcoin_wallet , monero_wallet ) =
init_wallets ( config . path , bitcoin_network , monero_network ) . await ? ;
2020-12-04 05:27:17 +00:00
2020-12-10 03:59:09 +00:00
let swap_id = Uuid ::new_v4 ( ) ;
2021-01-18 10:57:17 +00:00
2020-12-10 03:59:09 +00:00
info! (
2021-01-20 02:30:35 +00:00
" Swap sending {} and receiving {} started with ID {} " ,
2020-12-10 03:59:09 +00:00
send_bitcoin , receive_monero , swap_id
) ;
2021-01-19 04:21:40 +00:00
let bob_factory = Builder ::new (
2021-01-18 10:57:17 +00:00
seed ,
db_path ,
2020-12-18 06:39:04 +00:00
swap_id ,
2021-01-20 02:36:38 +00:00
Arc ::new ( bitcoin_wallet ) ,
Arc ::new ( monero_wallet ) ,
2020-12-18 06:39:04 +00:00
alice_addr ,
2021-01-18 10:57:17 +00:00
alice_peer_id ,
2021-01-27 02:33:32 +00:00
execution_params ,
2021-01-18 10:57:17 +00:00
) ;
2021-01-19 01:43:20 +00:00
let ( swap , event_loop ) = bob_factory . with_init_params ( swap_amounts ) . build ( ) . await ? ;
2021-01-18 10:57:17 +00:00
tokio ::spawn ( async move { event_loop . run ( ) . await } ) ;
bob ::run ( swap ) . await ? ;
2020-12-04 05:27:17 +00:00
}
2020-12-15 10:26:02 +00:00
Command ::History = > {
2020-12-04 05:27:17 +00:00
let mut table = Table ::new ( ) ;
table . add_row ( row! [ " SWAP ID " , " STATE " ] ) ;
2021-01-18 10:57:17 +00:00
let db = Database ::open ( db_path . as_path ( ) ) . context ( " Could not open database " ) ? ;
2020-12-04 05:27:17 +00:00
for ( swap_id , state ) in db . all ( ) ? {
table . add_row ( row! [ swap_id , state ] ) ;
}
// Print the table to stdout
table . printstd ( ) ;
}
2020-12-21 03:43:44 +00:00
Command ::Resume ( Resume ::SellXmr {
2020-12-15 10:26:02 +00:00
swap_id ,
listen_addr ,
2021-01-27 02:33:32 +00:00
config ,
2020-12-21 03:43:44 +00:00
} ) = > {
2021-01-27 02:33:32 +00:00
let ( bitcoin_wallet , monero_wallet ) =
init_wallets ( config . path , bitcoin_network , monero_network ) . await ? ;
2021-01-18 10:24:13 +00:00
2021-01-19 04:21:40 +00:00
let alice_factory = alice ::Builder ::new (
2021-01-18 10:24:13 +00:00
seed ,
2021-01-27 02:33:32 +00:00
execution_params ,
2020-12-21 03:43:44 +00:00
swap_id ,
2021-01-20 02:36:38 +00:00
Arc ::new ( bitcoin_wallet ) ,
Arc ::new ( monero_wallet ) ,
2021-01-18 10:24:13 +00:00
db_path ,
listen_addr ,
2021-01-22 02:33:31 +00:00
) ;
2021-01-19 04:21:40 +00:00
let ( swap , mut event_loop ) = alice_factory . build ( ) . await ? ;
2021-01-18 10:24:13 +00:00
tokio ::spawn ( async move { event_loop . run ( ) . await } ) ;
alice ::run ( swap ) . await ? ;
2020-12-21 03:43:44 +00:00
}
Command ::Resume ( Resume ::BuyXmr {
swap_id ,
2020-12-18 06:39:04 +00:00
alice_peer_id ,
alice_addr ,
2021-01-27 02:33:32 +00:00
config ,
2020-12-21 03:43:44 +00:00
} ) = > {
2021-01-27 02:33:32 +00:00
let ( bitcoin_wallet , monero_wallet ) =
init_wallets ( config . path , bitcoin_network , monero_network ) . await ? ;
2021-01-18 10:57:17 +00:00
2021-01-19 04:21:40 +00:00
let bob_factory = Builder ::new (
2021-01-18 10:57:17 +00:00
seed ,
db_path ,
2020-12-21 03:43:44 +00:00
swap_id ,
2021-01-20 02:36:38 +00:00
Arc ::new ( bitcoin_wallet ) ,
Arc ::new ( monero_wallet ) ,
2020-12-21 03:43:44 +00:00
alice_addr ,
2021-01-18 10:57:17 +00:00
alice_peer_id ,
2021-01-27 02:33:32 +00:00
execution_params ,
2021-01-18 10:57:17 +00:00
) ;
2021-01-19 04:21:40 +00:00
let ( swap , event_loop ) = bob_factory . build ( ) . await ? ;
2021-01-18 10:57:17 +00:00
tokio ::spawn ( async move { event_loop . run ( ) . await } ) ;
bob ::run ( swap ) . await ? ;
2020-12-15 10:26:02 +00:00
}
2021-02-01 05:10:43 +00:00
Command ::Cancel ( Cancel ::BuyXmr {
swap_id ,
alice_peer_id ,
alice_addr ,
config ,
} ) = > {
// TODO: Optimization: Only init the Bitcoin wallet, Monero wallet unnecessary
let ( bitcoin_wallet , monero_wallet ) =
init_wallets ( config . path , bitcoin_network , monero_network ) . await ? ;
let bob_factory = Builder ::new (
seed ,
db_path ,
swap_id ,
Arc ::new ( bitcoin_wallet ) ,
Arc ::new ( monero_wallet ) ,
alice_addr ,
alice_peer_id ,
execution_params ,
) ;
let ( swap , event_loop ) = bob_factory . build ( ) . await ? ;
tokio ::spawn ( async move { event_loop . run ( ) . await } ) ;
match bob ::cancel ( swap . swap_id , swap . state , swap . bitcoin_wallet , swap . db ) . await ? {
Ok ( ( txid , _ ) ) = > { info! ( " Cancel transaction successfully published with id {} " , txid ) } ,
Err ( CancelError ::CancelTimelockNotExpiredYet ) = > { info! ( " The Cancel Transaction cannot be published yet, because the timelock has not expired. Please try again later. " ) } ,
Err ( CancelError ::CancelTxAlreadyPublished ) = > { info! ( " The Cancel Transaction has already been published. " ) }
}
}
2020-12-21 03:43:44 +00:00
} ;
2020-12-04 05:27:17 +00:00
Ok ( ( ) )
2020-10-15 22:14:39 +00:00
}
2020-12-15 10:26:02 +00:00
2021-01-27 02:33:32 +00:00
async fn init_wallets (
config_path : Option < PathBuf > ,
bitcoin_network : bitcoin ::Network ,
monero_network : monero ::Network ,
2021-01-21 03:00:37 +00:00
) -> Result < ( bitcoin ::Wallet , monero ::Wallet ) > {
2021-01-27 02:33:32 +00:00
let config_path = if let Some ( config_path ) = config_path {
config_path
} else {
default_config_path ( ) ?
} ;
let config = match read_config ( config_path . clone ( ) ) ? {
Ok ( config ) = > config ,
Err ( ConfigNotInitialized { } ) = > {
initial_setup ( config_path . clone ( ) , query_user_for_initial_testnet_config ) ? ;
read_config ( config_path ) ? . expect ( " after initial setup config can be read " )
}
} ;
let bitcoin_wallet = bitcoin ::Wallet ::new (
config . bitcoin . wallet_name . as_str ( ) ,
config . bitcoin . bitcoind_url ,
bitcoin_network ,
)
. await ? ;
2020-12-15 10:26:02 +00:00
let bitcoin_balance = bitcoin_wallet . balance ( ) . await ? ;
info! (
" Connection to Bitcoin wallet succeeded, balance: {} " ,
bitcoin_balance
) ;
2021-01-27 02:33:32 +00:00
let monero_wallet = monero ::Wallet ::new ( config . monero . wallet_rpc_url , monero_network ) ;
2020-12-15 10:26:02 +00:00
let monero_balance = monero_wallet . get_balance ( ) . await ? ;
info! (
" Connection to Monero wallet succeeded, balance: {} " ,
monero_balance
) ;
2021-01-19 03:48:07 +00:00
Ok ( ( bitcoin_wallet , monero_wallet ) )
2020-12-15 10:26:02 +00:00
}