Piac test code coverage report
Current view: top level - src - cli.cpp (source / functions) Hit Total Coverage
Commit: Piac-RELEASE Lines: 215 270 79.6 %
Date: 2022-12-16 13:44:03 Functions: 7 8 87.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 332 791 42.0 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/cli.cpp
       4                 :            :   \copyright 2022-2025 J. Bakosi,
       5                 :            :              All rights reserved. See the LICENSE file for details.
       6                 :            :   \brief     Piac command line interface
       7                 :            : */
       8                 :            : // *****************************************************************************
       9                 :            : 
      10                 :            : #include <thread>
      11                 :            : 
      12                 :            : #include <readline/history.h>
      13                 :            : #include <readline/readline.h>
      14                 :            : #include <getopt.h>
      15                 :            : 
      16                 :            : #include "macro.hpp"
      17                 :            : #include "project_config.hpp"
      18                 :            : #include "string_util.hpp"
      19                 :            : #include "logging_util.hpp"
      20                 :            : #include "zmq_util.hpp"
      21                 :            : #include "monero_util.hpp"
      22                 :            : #include "cli_matrix_thread.hpp"
      23                 :            : #include "cli_message_thread.hpp"
      24                 :            : 
      25                 :            : static std::unique_ptr< monero_wallet_full > g_wallet;
      26                 :            : static std::vector< std::thread > g_threads;
      27                 :            : 
      28                 :         44 : static void graceful_exit() {
      29 [ +  - ][ +  - ]:         88 :   MDEBUG( "graceful exit" );
      30                 :            :   g_wallet.release();
      31         [ +  + ]:         44 :   if (piac::g_matrix_connected) {
      32                 :          3 :     piac::g_matrix_shutdown = true;
      33                 :          3 :     std::cout << "Waiting for matrix thread to quit ...\n";
      34         [ +  + ]:          9 :     for (auto& t : g_threads) t.join();
      35                 :          3 :     g_threads.clear();
      36                 :            :   }
      37                 :         44 : }
      38                 :            : 
      39                 :          0 : [[noreturn]] static void s_signal_handler( int /*signal_value*/ ) {
      40 [ -  - ][ -  - ]:          0 :   MDEBUG( "interrupted" );
      41                 :          0 :   graceful_exit();
      42                 :          0 :   exit( EXIT_SUCCESS );
      43                 :            : }
      44                 :            : 
      45                 :            : #if defined(__clang__)
      46                 :            :   #pragma clang diagnostic push
      47                 :            :   #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
      48                 :            : #endif
      49                 :            : 
      50                 :         44 : static void s_catch_signals() {
      51                 :            :   struct sigaction action;
      52                 :         44 :   action.sa_handler = s_signal_handler;
      53                 :         44 :   action.sa_flags = 0;
      54                 :         44 :   sigemptyset( &action.sa_mask );
      55                 :         44 :   sigaction( SIGINT, &action, nullptr );
      56                 :         44 :   sigaction( SIGTERM, &action, nullptr );
      57                 :         44 : }
      58                 :            : 
      59                 :            : #if defined(__clang__)
      60                 :            :   #pragma clang diagnostic pop
      61                 :            : #endif
      62                 :            : 
      63                 :            : //! Piac declarations and definitions
      64                 :            : namespace piac {
      65                 :            : 
      66                 :            : enum COLOR { RED, GREEN, BLUE, GRAY, YELLOW };
      67                 :            : 
      68                 :            : static std::string
      69         [ -  + ]:         88 : color_string( const std::string &s, COLOR color = GRAY )
      70                 :            : // *****************************************************************************
      71                 :            : //! Insert ASCI color string code to a string
      72                 :            : //! \param[in] s String to append
      73                 :            : //! \param[in] color Color code to insert
      74                 :            : //! \return String postfixed with ASCII color code
      75                 :            : // *****************************************************************************
      76                 :            : {
      77                 :            :   std::string ret;
      78         [ -  + ]:         88 :   if (color == RED) ret = "\033[0;31m";
      79         [ +  + ]:         88 :   if (color == GREEN) ret = "\033[0;32m";
      80         [ -  + ]:         88 :   if (color == BLUE) ret = "\033[0;34m";
      81         [ -  + ]:         88 :   if (color == GRAY) ret = "\033[0;37m";
      82         [ +  + ]:         88 :   if (color == YELLOW) ret = "\033[0;33m";
      83 [ +  - ][ +  - ]:        176 :   return ret + s + "\033[0m";
         [ -  + ][ -  - ]
      84                 :            : }
      85                 :            : 
      86                 :            : static void
      87         [ +  + ]:         36 : load_key( const std::string& filename, std::string& key )
      88                 :            : // *****************************************************************************
      89                 :            : //! Load keys from file
      90                 :            : //! \param[in] filename File to load keys from
      91                 :            : //! \param[in] key String to store key in
      92                 :            : // *****************************************************************************
      93                 :            : {
      94 [ +  + ][ -  + ]:         36 :   if (filename.empty() || not key.empty()) return;
      95                 :         40 :   std::ifstream f( filename );
      96         [ -  + ]:         20 :   if (not f.good()) {
      97         [ -  - ]:          0 :     epee::set_console_color( epee::console_color_red, /*bright=*/ false );
      98                 :          0 :     std::cerr <<  "Cannot open file for reading: " << filename << '\n';
      99         [ -  - ]:          0 :     epee::set_console_color( epee::console_color_default, /*bright=*/ false );
     100                 :          0 :     exit( EXIT_FAILURE );
     101                 :            :   }
     102         [ +  - ]:         20 :   f >> key;
     103                 :            :   assert( key.size() == 40 );
     104         [ +  - ]:         20 :   f.close();
     105 [ +  - ][ +  - ]:         40 :   MINFO( "Read key from file " << filename );
                 [ +  - ]
     106                 :            : }
     107                 :            : 
     108                 :            : static std::string
     109                 :          1 : usage( const std::string& logfile )
     110                 :            : // *****************************************************************************
     111                 :            : //! Return program usage information
     112                 :            : //! \param[in] logfile Logfile name
     113                 :            : //! \return String containing usage information
     114                 :            : // *****************************************************************************
     115                 :            : {
     116 [ +  - ][ +  - ]:          2 :   return "Usage: " + piac::cli_executable() + " [OPTIONS]\n\n"
         [ -  + ][ -  + ]
         [ -  + ][ -  - ]
         [ -  - ][ -  - ]
     117                 :            :          "OPTIONS\n"
     118                 :            :          "  --help\n"
     119                 :            :          "         Show help message.\n\n"
     120                 :            :          "  --log-file <filename.log>\n"
     121 [ +  - ][ +  - ]:          2 :          "         Specify log filename, default: " + logfile + ".\n\n"
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     122                 :            :          "  --log-level <[0-4]>\n"
     123                 :            :          "         Specify log level: 0: minimum, 4: maximum.\n\n"
     124                 :            :          "  --max-log-file-size <size-in-bytes> \n"
     125 [ +  - ][ -  + ]:          2 :          "         Specify maximum log file size in bytes. Default: " +
                 [ -  - ]
     126 [ +  - ][ +  - ]:          4 :          std::to_string( MAX_LOG_FILE_SIZE ) + ". Once the log file\n"
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     127                 :            :          "         grows past that limit, the next log file is created with "
     128                 :            :                   "a UTC timestamp postfix\n"
     129         [ +  - ]:          1 :          "         -YYYY-MM-DD-HH-MM-SS. Set --max-log-file-size 0 to prevent "
     130 [ +  - ][ +  - ]:          4 :                  + piac::cli_executable() + " from managing\n"
         [ -  + ][ -  + ]
         [ -  + ][ -  - ]
         [ -  - ][ -  - ]
     131                 :            :          "         the log files.\n\n"
     132                 :            :          "  --max-log-files <num> \n"
     133 [ +  - ][ -  + ]:          2 :          "         Specify a limit on the number of log files. Default: " +
                 [ -  - ]
     134 [ +  - ][ +  - ]:          4 :          std::to_string( MAX_LOG_FILES ) + ". The oldest log files\n"
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     135                 :            :          "         are removed. In production deployments, you would "
     136                 :            :                   "probably prefer to use\n"
     137                 :            :          "         established solutions like logrotate instead.\n\n"
     138                 :            :          "  --rpc-secure\n"
     139                 :            :          "         Enable secure connection to server.\n\n"
     140                 :            :          "  --rpc-client-public-key-file <filename>\n"
     141                 :            :          "         Load client public key from file. Need to also set "
     142                 :            :                   "--rpc-secure.\n\n"
     143                 :            :          "  --rpc-client-secret-key-file <filename>\n"
     144                 :            :          "         Load client secret key from file. Need to also set "
     145                 :            :                   "--rpc-secure.\n\n"
     146                 :            :          "  --rpc-server-public-key <key>\n"
     147                 :            :          "         Specify server public key. Need to also set "
     148                 :            :                   "--rpc-secure.\n\n"
     149                 :            :          "  --rpc-server-public-key-file <filename>\n"
     150                 :            :          "         Load server public key from file. Neet to also set "
     151                 :            :                   "--rpc-secure. If given,\n"
     152                 :            :          "         --rpc-server-public-key takes precedence.\n\n"
     153                 :            :          "  --matrix-sync-timeout\n"
     154                 :            :          "         Timeout in milliseconds for matrix sync calls. The default "
     155 [ +  - ][ +  - ]:          3 :                  "is " + std::to_string(piac::g_matrix_sync_timeout) + ".\n\n"
         [ -  + ][ -  - ]
     156                 :            :          "  --version\n"
     157         [ +  - ]:          2 :          "         Show version information.\n\n";
     158                 :            : }
     159                 :            : 
     160                 :            : static void
     161                 :        128 : echo_connection( const std::string& info, const std::string& server )
     162                 :            : // *****************************************************************************
     163                 :            : //!  Echo connection information to screen
     164                 :            : //! \param[in] info Introductory info message about connection
     165                 :            : //! \param[in] server Hostname that will be addressed
     166                 :            : // *****************************************************************************
     167                 :            : {
     168                 :        128 :   epee::set_console_color( epee::console_color_yellow, /* bright = */ false );
     169                 :            :   std::cout << info;
     170                 :        128 :   epee::set_console_color( epee::console_color_white, /* bright = */ false );
     171                 :        128 :   std::cout << server << '\n';
     172                 :        128 :   epee::set_console_color( epee::console_color_default, /* bright = */ false );
     173                 :        128 : }
     174                 :            : 
     175                 :            : } // piac::
     176                 :            : 
     177                 :            : int
     178                 :         48 : main( int argc, char **argv )
     179                 :            : // *****************************************************************************
     180                 :            : //! Piac command line interface main function
     181                 :            : //! \param[in] argc Number of command line arguments passed from shell
     182                 :            : //! \param[in] argv List of command line arguments passed from shell
     183                 :            : //! \return Error code to return to shell
     184                 :            : // *****************************************************************************
     185                 :            : {
     186                 :            :   // save command line
     187                 :         96 :   std::vector< std::string > args( argv, argv+argc );
     188         [ +  - ]:         96 :   std::stringstream cmdline;
     189         [ +  + ]:        202 :   for (const auto& a : args) cmdline << a << ' ';
     190                 :            : 
     191 [ +  - ][ +  - ]:         48 :   std::string logfile( piac::cli_executable() + ".log" );
     192         [ +  - ]:         48 :   std::string log_level( "4" );
     193 [ +  - ][ +  - ]:         96 :   std::string version( "piac: " + piac::cli_executable() + " v"
         [ +  - ][ -  + ]
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
                 [ -  - ]
     194 [ +  - ][ +  - ]:        192 :                        + piac::project_version() + "-"
         [ +  - ][ -  + ]
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
                 [ -  - ]
     195 [ +  - ][ +  - ]:         96 :                        + piac::build_type() );
                 [ -  - ]
     196                 :         48 :   std::size_t max_log_file_size = MAX_LOG_FILE_SIZE;
     197         [ +  - ]:         48 :   std::size_t max_log_files = MAX_LOG_FILES;
     198                 :            : 
     199                 :            :   std::string rpc_client_public_key_file;
     200                 :            :   std::string rpc_client_secret_key_file;
     201                 :            :   std::string rpc_server_public_key;
     202                 :            :   std::string rpc_server_public_key_file;
     203                 :            : 
     204                 :            :   // Process command line arguments
     205                 :            :   int c;
     206                 :         48 :   int option_index = 0;
     207                 :         48 :   int rpc_secure = 0;
     208                 :            :   int num_err = 0;
     209                 :            :   const int ARG_HELP                       = 1000;
     210                 :            :   const int ARG_LOG_FILE                   = 1001;
     211                 :            :   const int ARG_LOG_LEVEL                  = 1002;
     212                 :            :   const int ARG_MAX_LOG_FILE_SIZE          = 1003;
     213                 :            :   const int ARG_MAX_LOG_FILES              = 1004;
     214                 :            :   const int ARG_VERSION                    = 1005;
     215                 :            :   const int ARG_RPC_CLIENT_PUBLIC_KEY_FILE = 1006;
     216                 :            :   const int ARG_RPC_CLIENT_SECRET_KEY_FILE = 1007;
     217                 :            :   const int ARG_RPC_SERVER_PUBLIC_KEY      = 1008;
     218                 :            :   const int ARG_RPC_SERVER_PUBLIC_KEY_FILE = 1009;
     219                 :            :   const int ARG_MATRIX_SYNC_TIMEOUT        = 1010;
     220                 :            :   static struct option long_options[] =
     221                 :            :     {
     222                 :            :       { "help", no_argument, nullptr, ARG_HELP },
     223                 :            :       { "log-file", required_argument, nullptr, ARG_LOG_FILE },
     224                 :            :       { "log-level", required_argument, nullptr, ARG_LOG_LEVEL },
     225                 :            :       { "max-log-file-size", required_argument, nullptr, ARG_MAX_LOG_FILE_SIZE },
     226                 :            :       { "max-log-files", required_argument, nullptr, ARG_MAX_LOG_FILES },
     227                 :            :       { "rpc-secure", no_argument, &rpc_secure, 1 },
     228                 :            :       { "rpc-client-public-key-file", required_argument, nullptr,
     229                 :            :         ARG_RPC_CLIENT_PUBLIC_KEY_FILE},
     230                 :            :       { "rpc-client-secret-key-file", required_argument, nullptr,
     231                 :            :         ARG_RPC_CLIENT_SECRET_KEY_FILE},
     232                 :            :       { "rpc-server-public-key", required_argument, nullptr,
     233                 :            :         ARG_RPC_SERVER_PUBLIC_KEY},
     234                 :            :       { "rpc-server-public-key-file",required_argument, nullptr,
     235                 :            :         ARG_RPC_SERVER_PUBLIC_KEY_FILE},
     236                 :            :       { "matrix-sync-timeout",required_argument, nullptr,
     237                 :            :         ARG_MATRIX_SYNC_TIMEOUT},
     238                 :            :       { "version", no_argument, nullptr, ARG_VERSION },
     239                 :            :       { nullptr, 0, nullptr, 0 }
     240 [ +  - ][ +  - ]:         48 :     };
     241         [ +  + ]:        106 :   while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
     242 [ +  + ][ +  - ]:         60 :     switch (c) {
         [ -  - ][ +  + ]
         [ +  + ][ +  + ]
                    [ - ]
     243                 :            :       case ARG_HELP: {
     244         [ +  - ]:          1 :         std::cout << version << "\n\n" << piac::usage( logfile );
     245                 :          1 :         return EXIT_SUCCESS;
     246                 :            :       }
     247                 :            : 
     248                 :         20 :       case ARG_LOG_FILE: {
     249         [ +  - ]:         20 :         logfile = optarg;
     250                 :            :         break;
     251                 :            :       }
     252                 :            : 
     253                 :          0 :       case ARG_LOG_LEVEL: {
     254         [ -  - ]:          0 :         std::stringstream s;
     255         [ -  - ]:          0 :         s << optarg;
     256                 :            :         int level;
     257         [ -  - ]:          0 :         s >> level;
     258         [ -  - ]:          0 :         if (level < 0) level = 0;
     259         [ -  - ]:          0 :         if (level > 4) level = 4;
     260         [ -  - ]:          0 :         log_level = std::to_string( level );
     261                 :            :         break;
     262                 :            :       }
     263                 :            : 
     264                 :          0 :       case ARG_MAX_LOG_FILE_SIZE: {
     265         [ -  - ]:          0 :         std::stringstream s;
     266         [ -  - ]:          0 :         s << optarg;
     267                 :            :         s >> max_log_file_size;
     268                 :            :         break;
     269                 :            :       }
     270                 :            : 
     271                 :          0 :       case ARG_MAX_LOG_FILES: {
     272         [ -  - ]:          0 :         std::stringstream s;
     273         [ -  - ]:          0 :         s << optarg;
     274                 :            :         s >> max_log_files;
     275                 :            :         break;
     276                 :            :       }
     277                 :            : 
     278                 :          5 :       case ARG_RPC_CLIENT_PUBLIC_KEY_FILE: {
     279         [ +  - ]:          5 :         rpc_client_public_key_file = optarg;
     280                 :            :         break;
     281                 :            :       }
     282                 :            : 
     283                 :          5 :       case ARG_RPC_CLIENT_SECRET_KEY_FILE: {
     284         [ +  - ]:          5 :         rpc_client_secret_key_file = optarg;
     285                 :            :         break;
     286                 :            :       }
     287                 :            : 
     288                 :          2 :       case ARG_RPC_SERVER_PUBLIC_KEY: {
     289         [ +  - ]:          2 :         rpc_server_public_key = optarg;
     290                 :            :         break;
     291                 :            :       }
     292                 :            : 
     293                 :         10 :       case ARG_RPC_SERVER_PUBLIC_KEY_FILE: {
     294         [ +  - ]:         10 :         rpc_server_public_key_file = optarg;
     295                 :            :         break;
     296                 :            :       }
     297                 :            : 
     298                 :          3 :       case ARG_MATRIX_SYNC_TIMEOUT: {
     299         [ +  - ]:          6 :         std::stringstream s;
     300         [ +  - ]:          3 :         s << optarg;
     301                 :            :         s >> piac::g_matrix_sync_timeout;
     302                 :            :         break;
     303                 :            :       }
     304                 :            : 
     305                 :            :       case ARG_VERSION: {
     306                 :          1 :         std::cout << version << '\n';
     307                 :          1 :         return EXIT_SUCCESS;
     308                 :            :       }
     309                 :            : 
     310                 :          0 :       case '?': {
     311                 :          0 :         ++num_err;
     312                 :          0 :         break;
     313                 :            :       }
     314                 :            :     }
     315                 :            :   }
     316                 :            : 
     317         [ +  + ]:         46 :   if (optind < argc) {
     318         [ +  - ]:          1 :     printf( "%s: invalid options -- ", argv[0] );
     319 [ +  + ][ +  - ]:          2 :     while (optind < argc) printf( "%s ", argv[optind++] );
     320         [ +  - ]:          1 :     printf( "\n" );
     321                 :            :     return EXIT_FAILURE;
     322                 :            :   }
     323                 :            : 
     324         [ -  + ]:         45 :   if (num_err) {
     325                 :            :     std::cerr << "Erros during parsing command line\n"
     326 [ -  - ][ -  - ]:          0 :               << "Command line: " + cmdline.str() << '\n'
                 [ -  - ]
     327 [ -  - ][ -  - ]:          0 :               << piac::usage( logfile );
                 [ -  - ]
     328                 :          0 :     return EXIT_FAILURE;
     329                 :            :   }
     330                 :            : 
     331         [ +  - ]:         40 :   if ((not rpc_client_public_key_file.empty() ||
     332         [ +  + ]:         40 :        not rpc_client_secret_key_file.empty() ||
     333         [ +  + ]:         38 :        not rpc_server_public_key.empty() ||
     334         [ +  + ]:         45 :        not rpc_server_public_key_file.empty()) &&
     335         [ -  + ]:         12 :        not rpc_secure)
     336                 :            :   {
     337         [ -  - ]:          0 :     std::cerr << "Need --rpc-secure to secure RPC channel.\n";
     338                 :            :     return EXIT_FAILURE;
     339                 :            :   }
     340                 :            : 
     341         [ +  + ]:         13 :   if (rpc_secure &&
     342 [ +  + ][ +  + ]:         56 :       rpc_server_public_key.empty() &&
     343                 :            :       rpc_server_public_key_file.empty())
     344                 :            :   {
     345                 :            :     std::cerr << "Need --rpc-server-public-key or --rpc-server-public-key-file "
     346         [ +  - ]:          1 :                  "to secure RPC channel.\n";
     347                 :            :     return EXIT_FAILURE;
     348                 :            :   }
     349                 :            : 
     350         [ +  - ]:         44 :   epee::set_console_color( epee::console_color_green, /* bright = */ false );
     351                 :         44 :   std::cout << version << '\n';
     352         [ +  - ]:         44 :   epee::set_console_color( epee::console_color_default, /* bright = */ false );
     353                 :            :   std::cout <<
     354                 :            :     "Welcome to piac, where anyone can buy and sell anything privately and\n"
     355                 :            :     "securely using the private digital cash, monero. For more information\n"
     356                 :            :     "on monero, see https://getmonero.org. This is the command line client\n"
     357 [ +  - ][ +  - ]:         88 :     "of piac. It needs to connect to a " + piac::daemon_executable() + " to "
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     358         [ +  - ]:         44 :     "work correctly. Type\n'help' to list the available commands.\n";
     359                 :            : 
     360         [ +  - ]:         44 :   piac::setup_logging( logfile, log_level, /* console_logging = */ false,
     361                 :            :                        max_log_file_size, max_log_files );
     362                 :            : 
     363 [ +  - ][ +  - ]:        176 :   MINFO( "Command line: " + cmdline.str() );
         [ +  - ][ +  - ]
         [ -  + ][ -  - ]
                 [ -  - ]
     364                 :            : 
     365         [ +  - ]:         44 :   std::string piac_host = "localhost:55090";
     366         [ +  - ]:         44 :   std::string monerod_host = "localhost:38089";
     367                 :            : 
     368 [ +  - ][ +  - ]:         88 :   piac::echo_connection( "Will connect to piac daemon at ", piac_host );
         [ +  - ][ -  - ]
     369         [ +  - ]:         44 :   piac::echo_connection( "Wallet connecting to monero daemon at ",
     370 [ +  - ][ +  - ]:         88 :                          monerod_host );
                 [ -  - ]
     371                 :            : 
     372                 :            :   piac::WalletListener listener;
     373                 :            : 
     374 [ +  - ][ +  + ]:         88 :   MLOG_SET_THREAD_NAME( "cli" );
                 [ -  - ]
     375                 :            : 
     376                 :            :   // setup RPC security
     377                 :            :   int rpc_ironhouse = 1;
     378                 :         44 :   zmqpp::curve::keypair rpc_client_keys;
     379         [ +  + ]:         44 :   if (rpc_secure) {
     380         [ +  - ]:         12 :     piac::load_key( rpc_server_public_key_file, rpc_server_public_key );
     381                 :            :     assert( not rpc_server_public_key.empty() );
     382                 :            :     assert( rpc_server_public_key.size() == 40 );
     383         [ +  - ]:         12 :     piac::load_key( rpc_client_public_key_file, rpc_client_keys.public_key );
     384         [ +  - ]:         12 :     piac::load_key( rpc_client_secret_key_file, rpc_client_keys.secret_key );
     385                 :            :     // fallback to stonehouse if needed
     386 [ +  + ][ -  + ]:         12 :     if (rpc_client_keys.secret_key.empty() ||
     387                 :            :         rpc_client_keys.public_key.empty())
     388                 :            :     {
     389                 :            :       rpc_ironhouse = 0;
     390         [ +  - ]:         14 :       rpc_client_keys = zmqpp::curve::generate_keypair();
     391                 :            :     }
     392                 :            :   }
     393                 :            : 
     394                 :            :   // echo RPC security configured
     395         [ +  + ]:         44 :   if (rpc_secure) {
     396         [ +  + ]:         12 :     if (rpc_ironhouse) {  // ironhouse
     397         [ +  - ]:          5 :       epee::set_console_color( epee::console_color_green, /*bright=*/ false );
     398                 :            :       std::string ironhouse( "Connection to piac daemon is secure and "
     399         [ +  - ]:          5 :         "authenticated." );
     400                 :          5 :       std::cout << ironhouse << '\n';
     401         [ +  - ]:          5 :       epee::set_console_color( epee::console_color_white, /*bright=*/false );
     402 [ +  - ][ +  - ]:         10 :       MINFO( ironhouse );
         [ +  - ][ -  - ]
     403                 :            :     } else {              // stonehouse
     404                 :            :       std::string stonehouse( "Connection to piac daemon is secure but not "
     405         [ +  - ]:          7 :         "authenticated." );
     406         [ +  - ]:          7 :       epee::set_console_color( epee::console_color_yellow, /*bright=*/ false );
     407                 :          7 :       std::cout << stonehouse << '\n';
     408         [ +  - ]:          7 :       epee::set_console_color( epee::console_color_white, /*bright=*/false );
     409 [ +  - ][ +  - ]:         14 :       MINFO( stonehouse );
         [ +  - ][ -  - ]
     410                 :            :     }
     411                 :            :   } else {                // grasslands
     412         [ +  - ]:         32 :     epee::set_console_color( epee::console_color_red, /* bright = */ false );
     413         [ +  - ]:         32 :     std::string grasslands( "WARNING: Connection to piac daemon is not secure" );
     414                 :         32 :     std::cout << grasslands << '\n';
     415 [ +  - ][ +  - ]:         64 :     MWARNING( grasslands );
         [ +  - ][ -  - ]
     416                 :            :   }
     417         [ +  - ]:         44 :   epee::set_console_color( epee::console_color_default, /* bright = */false );
     418 [ +  - ][ +  - ]:         88 :   MINFO( "RPC client public key: " << rpc_client_keys.public_key );
                 [ +  - ]
     419                 :            : 
     420                 :            :   std::string matrix_host, matrix_user, matrix_password;
     421                 :            : 
     422                 :            :   // initialize (thread-safe) zmq context used to communicate with daemon
     423         [ +  - ]:         44 :   zmqpp::context ctx_rpc;
     424                 :            : 
     425                 :            :   char* buf;
     426 [ +  - ][ +  - ]:         88 :   std::string prompt = color_string( "piac", piac::GREEN ) +
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     427 [ +  - ][ +  - ]:        132 :                        color_string( "> ", piac::YELLOW );
         [ +  - ][ -  + ]
         [ -  - ][ -  - ]
     428                 :            : 
     429                 :         44 :   s_catch_signals();
     430                 :            : 
     431 [ +  - ][ +  - ]:        170 :   while ((buf = readline( prompt.c_str() ) ) != nullptr) {
     432                 :            : 
     433         [ +  + ]:        170 :     if (!strcmp(buf,"balance")) {
     434                 :            : 
     435         [ +  - ]:          1 :       piac::show_wallet_balance( g_wallet, listener );
     436                 :            : 
     437 [ +  + ][ +  - ]:        169 :     } else if (buf[0]=='d' && buf[1]=='b') {
     438                 :            : 
     439         [ +  - ]:         22 :       piac::send_cmd( buf, ctx_rpc, piac_host, rpc_server_public_key,
     440 [ +  - ][ -  - ]:         44 :                       rpc_client_keys, g_wallet );
     441                 :            : 
     442 [ +  + ][ +  - ]:        147 :     } else if (!strcmp(buf,"exit") || !strcmp(buf,"quit") || buf[0]=='q') {
                 [ +  - ]
     443                 :            : 
     444                 :            :       break;
     445                 :            : 
     446         [ +  + ]:        103 :     } else if (!strcmp(buf,"help")) {
     447                 :            : 
     448                 :            :       std::cout << "COMMANDS\n"
     449                 :            :       "      balance\n"
     450                 :            :       "                Show monero wallet balance and sync status\n\n"
     451                 :            :       "      db <command>\n"
     452                 :            :       "                Send database command to piac daemon. Example db commands:\n"
     453                 :            :       "                > db query cat - search for the word 'cat'\n"
     454                 :            :       "                > db query race -condition - search for 'race' but not 'condition'\n"
     455                 :            :       "                > db add json <path-to-json-db-entry>\n"
     456                 :            :       "                > db rm <hash> - remove document\n"
     457                 :            :       "                > db list - list all documents\n"
     458                 :            :       "                > db list hash - list all document hashes\n"
     459                 :            :       "                > db list numdoc - list number of documents\n"
     460                 :            :       "                > db list numusr - list number of users in db\n\n"
     461                 :            :       "      exit, quit, q\n"
     462                 :            :       "                Exit\n\n"
     463                 :            :       "      help\n"
     464                 :            :       "                This help message\n\n"
     465                 :            :       "      keys\n"
     466                 :            :       "                Show monero wallet keys of current user\n\n"
     467                 :            :       "      matrix <host>[:<port>] <username> <password>]\n"
     468                 :            :       "                Specify matrix server login information to connect to. The <host>\n"
     469                 :            :       "                argument specifies a hostname or an IPv4 address in standard dot\n"
     470                 :            :       "                notation. The optional <port> argument is an integer specifying a\n"
     471                 :            :       "                port. If unspecified, the default port is 443. The <host>[:<port>]\n"
     472                 :            :       "                argument must be followed by <username> and <password>. Use\n"
     473                 :            :       "                'matrix \"\"' to clear the setting and disable communication via the\n"
     474                 :            :       "                built-in matrix client. Note that connecting to a matrix server also\n"
     475                 :            :       "                requires an active user id, see also 'new' or 'user'.\n\n"
     476                 :            :       "      msg <user> <message>\n"
     477                 :            :       "                Send a message to user, where <user> is a matrix user id and message\n"
     478                 :            :       "                is either a single word or a doubly-quoted multi-word message.\n\n"
     479                 :            :       "      monerod [<host>[:<port>]]\n"
     480                 :            :       "                Specify monero node to connect to. The <host> argument specifies\n"
     481                 :            :       "                a hostname or an IPv4 address in standard dot notation. The\n"
     482                 :            :       "                optional <port> argument is an integer specifying a port. The\n"
     483 [ +  - ][ -  + ]:          2 :       "                default is " + monerod_host + ". Without an argument, show current\n"
         [ -  + ][ -  - ]
                 [ -  - ]
     484                 :            :       "                setting. Use 'monerod \"\"' to clear the setting and use an\n"
     485                 :            :       "                offline wallet.\n\n"
     486                 :            :       "      new\n"
     487                 :            :       "                Create new user identity. This will generate a new monero wallet\n"
     488                 :            :       "                which will be used as a user id when creating an ad or paying for\n"
     489                 :            :       "                an item. This wallet can be used just like any other monero wallet.\n"
     490                 :            :       "                If you want to use your existing monero wallet, see 'user'.\n\n"
     491                 :            :       "      peers\n"
     492                 :            :       "                List server peers\n\n"
     493                 :            :       "      server [<host>[:<port>]] [<public-key>]\n"
     494                 :            :       "                Specify server to send commands to. The <host> argument specifies\n"
     495                 :            :       "                a hostname or an IPv4 address in standard dot notation. The\n"
     496                 :            :       "                optional <port> argument is an integer specifying a port. The\n"
     497 [ +  - ][ +  - ]:          2 :       "                default is " + piac_host + ". The optional public-key is the\n"
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     498                 :            :       "                server's public key to use for authenticated and secure\n"
     499                 :            :       "                connections. Without an argument, show current setting. Use\n"
     500                 :            :       "                'server \"\"' to clear the setting and to not communicate with\n"
     501                 :            :       "                the peer-to-peer piac network.\n\n"
     502                 :            :       "      user [<mnemonic>]\n"
     503                 :            :       "                Show active monero wallet mnemonic seed (user id) if no mnemonic is\n"
     504                 :            :       "                given. Switch to mnemonic if given.\n\n"
     505                 :            :       "      version\n"
     506 [ +  - ][ +  - ]:          4 :       "                Display " + piac::cli_executable() + " version\n";
         [ +  - ][ +  - ]
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     507                 :            : 
     508         [ +  + ]:        102 :     } else if (!strcmp(buf,"keys")) {
     509                 :            : 
     510         [ +  - ]:          2 :       piac::show_wallet_keys( g_wallet );
     511                 :            : 
     512 [ +  + ][ +  + ]:        100 :     } else if (buf[0]=='m' && buf[1]=='s' && buf[2]=='g') {
                 [ +  - ]
     513                 :            : 
     514         [ -  + ]:          1 :       if (not g_wallet) {
     515         [ -  - ]:          0 :         std::cerr << "Need active user id (wallet). See 'new' or 'user'.\n";
     516                 :            :       }
     517         [ +  - ]:          1 :       std::string b( buf );
     518         [ +  - ]:          2 :       auto t = piac::tokenize( b );
     519         [ +  - ]:          1 :       if (t.size() == 3) {
     520                 :            :         auto target_user = t[1];
     521                 :            :         auto msg = t[2];
     522         [ +  - ]:          1 :         piac::matrix_message( matrix_user, target_user, msg );
     523                 :            :       } else {
     524         [ -  - ]:          0 :         std::cerr << "Need exactly 3 tokens.\n";
     525                 :          1 :       }
     526                 :            : 
     527 [ +  + ][ +  + ]:         99 :     } else if (buf[0]=='m' && buf[1]=='a' && buf[2]=='t' && buf[3]=='r' &&
         [ +  - ][ +  - ]
     528 [ +  - ][ +  - ]:          3 :                buf[4]=='i' && buf[5]=='x') {
     529                 :            : 
     530         [ -  + ]:          3 :       if (not g_wallet) {
     531         [ -  - ]:          0 :         std::cerr << "Need active user id (wallet). See 'new' or 'user'.\n";
     532                 :            :       }
     533         [ +  - ]:          3 :       std::string b( buf );
     534         [ +  - ]:          6 :       auto t = piac::tokenize( b );
     535         [ -  + ]:          3 :       if (t.size() == 1) {
     536         [ -  - ]:          0 :         if (piac::g_matrix_connected) {
     537         [ -  - ]:          0 :           piac::echo_connection( "Connected to matrix server as ",
     538 [ -  - ][ -  - ]:          0 :                                  '@' + matrix_user + ':' + matrix_host );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     539                 :            :         } else {
     540         [ -  - ]:          0 :           std::cout << "Offline\n";
     541                 :            :         }
     542         [ -  + ]:          3 :       } else if (t.size() == 2) {
     543                 :            :         matrix_host = t[1];
     544         [ -  - ]:          0 :         if (matrix_host == "disconnect") {
     545         [ -  - ]:          0 :           if (piac::g_matrix_connected) {
     546                 :          0 :             piac::g_matrix_shutdown = true;
     547         [ -  - ]:          0 :             std::cout << "Waiting for matrix thread to quit...\n";
     548 [ -  - ][ -  - ]:          0 :             for (auto& h : g_threads) h.join();
     549                 :          0 :             g_threads.clear();
     550         [ -  - ]:          0 :             std::cout << "Disconnected\n";
     551                 :            :           }
     552         [ -  - ]:          0 :         } else if (matrix_host == "\"\"") {
     553                 :            :           matrix_host.clear();
     554         [ -  - ]:          0 :           std::cout << "Offline\n";
     555                 :            :         }
     556         [ +  - ]:          3 :       } else if (t.size() == 4) {
     557                 :            :         matrix_host = t[1];
     558                 :            :         matrix_user = t[2];
     559                 :            :         matrix_password = t[3];
     560         [ +  - ]:          3 :         piac::echo_connection( "Connecting to matrix server as ",
     561 [ +  - ][ +  - ]:          9 :                                '@' + matrix_user + ':' + matrix_host );
         [ +  - ][ +  - ]
         [ +  - ][ -  + ]
         [ -  + ][ +  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
     562                 :            :         g_threads.emplace_back( piac::matrix_thread, matrix_host, matrix_user,
     563 [ +  - ][ +  - ]:          3 :           matrix_password, g_wallet->get_private_spend_key() );
     564         [ +  - ]:          3 :         g_threads.emplace_back( piac::message_thread );
     565                 :            :       } else {
     566                 :            :         std::cout << "The command 'matrix' must be followed by 3 arguments "
     567         [ -  - ]:          0 :                      "separated by spaces. See 'help'.\n";
     568                 :          3 :       }
     569                 :            : 
     570 [ +  + ][ +  - ]:         96 :     } else if (buf[0]=='m' && buf[1]=='o' && buf[2]=='n' && buf[3]=='e'&&
         [ +  - ][ +  - ]
     571 [ +  - ][ +  - ]:         14 :                buf[4]=='r' && buf[5]=='o' && buf[6]=='d') {
                 [ +  - ]
     572                 :            : 
     573         [ +  - ]:         14 :       std::string b( buf );
     574         [ +  - ]:         28 :       auto t = piac::tokenize( b );
     575         [ +  - ]:         14 :       epee::set_console_color( epee::console_color_yellow, /*bright=*/ false );
     576         [ -  + ]:         14 :       if (t.size() == 1) {
     577         [ -  - ]:          0 :         if (monerod_host.empty()) {
     578         [ -  - ]:          0 :           std::cout << "Wallet offline\n";
     579                 :            :         } else {
     580         [ -  - ]:          0 :           piac::echo_connection( "Wallet connecting to monero daemon at ",
     581         [ -  - ]:          0 :                                  monerod_host );
     582                 :            :         }
     583                 :            :       } else {
     584                 :            :         monerod_host = t[1];
     585         [ +  - ]:         14 :         if (monerod_host == "\"\"") {
     586                 :            :           monerod_host.clear();
     587         [ +  - ]:         14 :           std::cout << "Wallet will not connect to monero daemon\n";
     588                 :            :         } else {
     589         [ -  - ]:          0 :           piac::echo_connection( "Wallet connecting to monero daemon at ",
     590         [ -  - ]:          0 :                                  monerod_host );
     591                 :            :         }
     592                 :            :       }
     593         [ +  - ]:         28 :       epee::set_console_color( epee::console_color_default, /*bright=*/ false );
     594                 :            : 
     595         [ +  + ]:         82 :     } else if (!strcmp(buf,"new")) {
     596                 :            : 
     597         [ +  - ]:          2 :       g_wallet = piac::create_wallet( monerod_host, listener );
     598                 :            : 
     599         [ +  + ]:         81 :     } else if (!strcmp(buf,"peers")) {
     600                 :            : 
     601         [ +  - ]:         21 :       piac::send_cmd( "peers", ctx_rpc, piac_host, rpc_server_public_key,
     602 [ +  - ][ -  - ]:         42 :                       rpc_client_keys, g_wallet );
     603                 :            : 
     604 [ +  + ][ +  + ]:         60 :     } else if (buf[0]=='s' && buf[1]=='e' && buf[2]=='r' && buf[3]=='v'&&
         [ +  - ][ +  - ]
     605 [ +  - ][ +  - ]:         37 :                buf[4]=='e' && buf[5]=='r') {
     606                 :            : 
     607         [ +  - ]:         37 :       std::string b( buf );
     608         [ +  - ]:         74 :       auto t = piac::tokenize( b );
     609         [ +  - ]:         37 :       epee::set_console_color( epee::console_color_yellow, /*bright=*/ false );
     610         [ -  + ]:         37 :       if (t.size() == 1) {
     611         [ -  - ]:          0 :         if (piac_host.empty()) {
     612         [ -  - ]:          0 :           std::cout << "Offline\n";
     613                 :            :         } else {
     614 [ -  - ][ -  - ]:          0 :          piac::echo_connection( "Will connect to piac daemon at ", piac_host );
     615                 :            :         }
     616                 :            :       } else {
     617                 :            :         piac_host = t[1];
     618         [ -  + ]:         37 :         if (piac_host == "\"\"") {
     619                 :            :           piac_host.clear();
     620         [ -  - ]:          0 :           std::cout << "Offline\n";
     621                 :            :         } else {
     622 [ +  - ][ +  - ]:         74 :           piac::echo_connection( "Will connect to piac daemon at ", piac_host );
                 [ -  + ]
     623         [ -  + ]:         37 :           if (t.size() > 2) {
     624                 :            :             std::cout << ", using public key: " << t[2];
     625                 :            :             rpc_server_public_key = t[2];
     626                 :            :           }
     627                 :         37 :           std::cout << '\n';
     628                 :            :         }
     629                 :            :       }
     630         [ +  - ]:         74 :       epee::set_console_color( epee::console_color_default, /*bright=*/ false );
     631                 :            : 
     632 [ +  + ][ +  - ]:         23 :     } else if (buf[0]=='s' && buf[1]=='l' && buf[2]=='e' && buf[3]=='e' &&
         [ +  - ][ +  - ]
     633         [ +  - ]:          4 :                buf[4]=='p')
     634                 :            :     {
     635                 :            : 
     636         [ +  - ]:          4 :       std::string sec( buf );
     637         [ +  - ]:          4 :       sec.erase( 0, 6 );
     638                 :            : 
     639                 :          4 :       auto start = std::chrono::high_resolution_clock::now();
     640         [ +  - ]:          4 :       std::this_thread::sleep_for( std::chrono::seconds( std::atoi(sec.c_str()) ) );
     641                 :          4 :       auto end = std::chrono::high_resolution_clock::now();
     642                 :            :       std::chrono::duration< double, std::milli > elapsed = end - start;
     643 [ +  - ][ +  - ]:         12 :       MDEBUG( "Waited " << elapsed.count() << " ms" );
         [ +  - ][ -  - ]
     644                 :            : 
     645 [ +  + ][ +  - ]:         19 :     } else if (buf[0]=='u' && buf[1]=='s' && buf[2]=='e' && buf[3]=='r') {
         [ +  - ][ +  - ]
     646                 :            : 
     647         [ +  - ]:         17 :       std::string mnemonic( buf );
     648         [ +  - ]:         17 :       mnemonic.erase( 0, 5 );
     649         [ +  + ]:         17 :       if (mnemonic.empty()) {
     650         [ +  - ]:          2 :         piac::show_user( g_wallet );
     651 [ +  - ][ +  + ]:         15 :       } else if (piac::wordcount(mnemonic) != 25) {
     652         [ +  - ]:          1 :         std::cout << "Need 25 words\n";
     653                 :            :       } else {
     654         [ +  - ]:         28 :         g_wallet = piac::switch_user( mnemonic, monerod_host, listener );
     655                 :         17 :       }
     656                 :            : 
     657         [ +  + ]:          2 :     } else if (!strcmp(buf,"version")) {
     658                 :            : 
     659                 :          1 :       std::cout << version << '\n';
     660                 :            : 
     661                 :            :     } else {
     662                 :            : 
     663         [ +  - ]:          1 :       std::cout << "unknown cmd\n";
     664                 :            : 
     665                 :            :     }
     666                 :            : 
     667 [ +  - ][ +  - ]:        126 :     if (strlen( buf ) > 0) add_history( buf );
     668                 :            : 
     669                 :            :     // readline malloc's a new buffer every time
     670                 :        126 :     if (buf) free( buf );
     671                 :            :   }
     672                 :            : 
     673         [ +  - ]:         44 :   graceful_exit();
     674                 :            :   return EXIT_SUCCESS;
     675                 :            : }

Generated by: LCOV version 1.14