text:
/*
 * Copyright (c) 2017 Cryptonomex, Inc., and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <algorithm>
#include <cctype>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <list>

#include <boost/version.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/replace.hpp>

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/range/algorithm/unique.hpp>
#include <boost/range/algorithm/sort.hpp>

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>

#include <fc/git_revision.hpp>
#include <fc/io/fstream.hpp>
#include <fc/io/json.hpp>
#include <fc/io/stdio.hpp>
#include <fc/network/http/websocket.hpp>
#include <fc/rpc/cli.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/thread/mutex.hpp>
#include <fc/thread/scoped_lock.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/aes.hpp>

#include <graphene/app/api.hpp>
#include <graphene/app/util.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/protocol/fee_schedule.hpp>
#include <graphene/protocol/pts_address.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/utilities/git_revision.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/utilities/words.hpp>
#include <graphene/wallet/wallet.hpp>
#include <graphene/wallet/api_documentation.hpp>
#include "wallet_api_impl.hpp"
#include <graphene/debug_witness/debug_api.hpp>

#include "operation_printer.hpp"
#include <graphene/wallet/reflect_util.hpp>

#define BRAIN_KEY_WORD_COUNT 16
#define RANGE_PROOF_MANTISSA 49 // Minimum mantissa bits to "hide" in the range proof.
                                // If this number is set too low, then for large value
                                // commitments the length of the range proof will hint
                                // strongly at the value amount that is being hidden.

namespace graphene { namespace wallet {
   fc::sha256 signed_message::digest()const
   {
      fc::stringstream to_sign;
      to_sign << message << '\n';
      to_sign << "account=" << meta.account << '\n';
      to_sign << "memokey=" << std::string( meta.memo_key ) << '\n';
      to_sign << "block=" << meta.block << '\n';
      to_sign << "timestamp=" << meta.time;

      return fc::sha256::hash( to_sign.str() );
   }
   vector<brain_key_info> utility::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys)
   {
      // Safety-check
      FC_ASSERT( number_of_desired_keys >= 1 );

      // Create as many derived owner keys as requested
      vector<brain_key_info> results;
      brain_key = graphene::wallet::detail::normalize_brain_key(brain_key);
      for (int i = 0; i < number_of_desired_keys; ++i) {
        fc::ecc::private_key priv_key = graphene::wallet::detail::derive_private_key( brain_key, i );

        brain_key_info result;
        result.brain_priv_key = brain_key;
        result.wif_priv_key = key_to_wif( priv_key );
        result.pub_key = priv_key.get_public_key();

        results.push_back(result);
      }

      return results;
   }

   brain_key_info utility::suggest_brain_key()
   {
      brain_key_info result;
      // create a private key for secure entropy
      fc::sha256 sha_entropy1 = fc::ecc::private_key::generate().get_secret();
      fc::sha256 sha_entropy2 = fc::ecc::private_key::generate().get_secret();
      fc::bigint entropy1(sha_entropy1.data(), sha_entropy1.data_size());
      fc::bigint entropy2(sha_entropy2.data(), sha_entropy2.data_size());
      fc::bigint entropy(entropy1);
      entropy <<= 8 * sha_entropy1.data_size();
      entropy += entropy2;
      string brain_key = "";

      for (int i = 0; i < BRAIN_KEY_WORD_COUNT; i++)
      {
         fc::bigint choice = entropy % graphene::words::word_list_size;
         entropy /= graphene::words::word_list_size;
         if (i > 0)
            brain_key += " ";
         brain_key += graphene::words::word_list[choice.to_int64()];
      }

      brain_key = detail::normalize_brain_key(brain_key);
      fc::ecc::private_key priv_key = detail::derive_private_key(brain_key, 0);
      result.brain_priv_key = brain_key;
      result.wif_priv_key = key_to_wif(priv_key);
      result.pub_key = priv_key.get_public_key();
      return result;
   }
}}

namespace graphene { namespace wallet {

wallet_api::wallet_api(const wallet_data& initial_data, fc::api<login_api> rapi)
   : my(new detail::wallet_api_impl(*this, initial_data, rapi))
{
}

wallet_api::~wallet_api()
{
}

bool wallet_api::copy_wallet_file(string destination_filename)
{
   return my->copy_wallet_file(destination_filename);
}

optional<signed_block_with_info> wallet_api::get_block(uint32_t num)
{
   return my->_remote_db->get_block(num);
}

uint64_t wallet_api::get_account_count() const
{
   return my->_remote_db->get_account_count();
}

vector<account_object> wallet_api::list_my_accounts()
{
   return vector<account_object>(my->_wallet.my_accounts.begin(), my->_wallet.my_accounts.end());
}

map<string,account_id_type> wallet_api::list_accounts(const string& lowerbound, uint32_t limit)
{
   return my->_remote_db->lookup_accounts(lowerbound, limit, {});
}

vector<asset> wallet_api::list_account_balances(const string& id)
{
   return my->_remote_db->get_account_balances(id, flat_set<asset_id_type>());
}

vector<extended_asset_object> wallet_api::list_assets(const string& lowerbound, uint32_t limit)const
{
   return my->_remote_db->list_assets( lowerbound, limit );
}

uint64_t wallet_api::get_asset_count()const
{
   return my->_remote_db->get_asset_count();
}

signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol,
         string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size,
         const uint32_t claim_period_seconds, const std::string& memo, bool broadcast)
{
   return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size,
         claim_period_seconds, memo, broadcast);
}

fc::optional<fc::variant> wallet_api::get_htlc(std::string htlc_id) const
{
   fc::optional<htlc_object> optional_obj = my->get_htlc(htlc_id);
   if ( optional_obj.valid() )
   {
      const htlc_object& obj = *optional_obj;
      // convert to formatted variant
      fc::mutable_variant_object transfer;
      const auto& from = my->get_account( obj.transfer.from );
      transfer["from"] = from.name;
      const auto& to = my->get_account( obj.transfer.to );
      transfer["to"] = to.name;
      const auto& asset = my->get_asset( obj.transfer.asset_id );
      transfer["asset"] = asset.symbol;
      transfer["amount"] = graphene::app::uint128_amount_to_string( obj.transfer.amount.value, asset.precision );
      if (obj.memo.valid())
         transfer["memo"] = my->read_memo( *obj.memo );
      class htlc_hash_to_variant_visitor
      {
         public:
         typedef fc::mutable_variant_object result_type;

         result_type operator()(const fc::ripemd160& obj)const
         { return convert("RIPEMD160", obj.str()); }
         result_type operator()(const fc::sha1& obj)const
         { return convert("SHA1", obj.str()); }
         result_type operator()(const fc::sha256& obj)const
         { return convert("SHA256", obj.str()); }
         result_type operator()(const fc::hash160& obj)const
         { return convert("HASH160", obj.str()); }
         private:
         result_type convert(const std::string& type, const std::string& hash)const
         {
            fc::mutable_variant_object ret_val;
            ret_val["hash_algo"] = type;
            ret_val["preimage_hash"] = hash;
            return ret_val;
         }
      };
      static htlc_hash_to_variant_visitor hash_visitor;
      fc::mutable_variant_object htlc_lock = obj.conditions.hash_lock.preimage_hash.visit(hash_visitor);
      htlc_lock["preimage_size"] = obj.conditions.hash_lock.preimage_size;
      fc::mutable_variant_object time_lock;
      time_lock["expiration"] = obj.conditions.time_lock.expiration;
      time_lock["time_left"] = fc::get_approximate_relative_time_string(obj.conditions.time_lock.expiration);
      fc::mutable_variant_object conditions;
      conditions["htlc_lock"] = htlc_lock;
      conditions["time_lock"] = time_lock;
      fc::mutable_variant_object result;
      result["transfer"] = transfer;
      result["conditions"] = conditions;
      return fc::optional<fc::variant>(result);
   }
   return fc::optional<fc::variant>();
}

signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage,
      bool broadcast)
{

   return my->htlc_redeem(htlc_id, issuer, std::vector<char>(preimage.begin(), preimage.end()), broadcast);
}

signed_transaction wallet_api::htlc_extend ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add,
      bool broadcast)
{
   return my->htlc_extend(htlc_id, issuer, seconds_to_add, broadcast);
}

vector<operation_detail> wallet_api::get_account_history(string name, int limit)const
{
   vector<operation_detail> result;

   while( limit > 0 )
   {
      bool skip_first_row = false;
      operation_history_id_type start;
      if( result.size() )
      {
         start = result.back().op.id;
         if( start == operation_history_id_type() ) // no more data
            break;
         start = start + (-1);
         if( start == operation_history_id_type() ) // will return most recent history if
                                                    // directly call remote API with this
         {
            start = start + 1;
            skip_first_row = true;
         }
      }

      int page_limit = skip_first_row ? std::min( 100, limit + 1 ) : std::min( 100, limit );

      vector<operation_history_object> current = my->_remote_hist->get_account_history(
            name,
            operation_history_id_type(),
            page_limit,
            start );
      bool first_row = true;
      for( auto& o : current )
      {
         if( first_row )
         {
            first_row = false;
            if( skip_first_row )
            {
               continue;
            }
         }
         std::stringstream ss;
         auto memo = o.op.visit(detail::operation_printer(ss, *my, o));
         result.push_back( operation_detail{ memo, ss.str(), o } );
      }

      if( int(current.size()) < page_limit )
         break;

      limit -= current.size();
      if( skip_first_row )
         ++limit;
   }

   return result;
}

vector<operation_detail> wallet_api::get_relative_account_history(
      string name,
      uint32_t stop,
      int limit,
      uint32_t start)const
{
   vector<operation_detail> result;
   auto account_id = get_account(name).get_id();

   const account_object& account = my->get_account(account_id);
   const account_statistics_object& stats = my->get_object(account.statistics);

   if(start == 0)
       start = stats.total_ops;
   else
      start = std::min<uint32_t>(start, stats.total_ops);

   while( limit > 0 )
   {
      vector <operation_history_object> current = my->_remote_hist->get_relative_account_history(
            name,
            stop,
            std::min<uint32_t>(100, limit),
            start);
      for (auto &o : current) {
         std::stringstream ss;
         auto memo = o.op.visit(detail::operation_printer(ss, *my, o));
         result.push_back(operation_detail{memo, ss.str(), o});
      }
      if (current.size() < std::min<uint32_t>(100, limit))
         break;
      limit -= current.size();
      start -= 100;
      if( start == 0 ) break;
   }
   return result;
}

account_history_operation_detail wallet_api::get_account_history_by_operations(
      string name,
      vector<uint16_t> operation_types,
      uint32_t start,
      int limit)
{
    account_history_operation_detail result;
    auto account_id = get_account(name).get_id();

    const auto& account = my->get_account(account_id);
    const auto& stats = my->get_object(account.statistics);

    // sequence of account_transaction_history_object start with 1
    start = start == 0 ? 1 : start;

    if (start <= stats.removed_ops) {
        start = stats.removed_ops;
        result.total_count =stats.removed_ops;
    }

    while (limit > 0 && start <= stats.total_ops) {
        uint32_t min_limit = std::min<uint32_t> (100, limit);
        auto current = my->_remote_hist->get_account_history_by_operations(name, operation_types, start, min_limit);
        for (auto& obj : current.operation_history_objs) {
            std::stringstream ss;
            auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj));

            transaction_id_type transaction_id;
            auto block = get_block(obj.block_num);
            if (block.valid() && obj.trx_in_block < block->transaction_ids.size()) {
                transaction_id = block->transaction_ids[obj.trx_in_block];
            }
            result.details.push_back(operation_detail_ex{memo, ss.str(), obj, transaction_id});
        }
        result.result_count += current.operation_history_objs.size();
        result.total_count += current.total_count;

        start += current.total_count > 0 ? current.total_count : min_limit;
        limit -= current.operation_history_objs.size();
    }

    return result;
}

full_account wallet_api::get_full_account( const string& name_or_id)
{
    return my->_remote_db->get_full_accounts({name_or_id}, false)[name_or_id];
}

vector<bucket_object> wallet_api::get_market_history(
      string symbol1,
      string symbol2,
      uint32_t bucket,
      fc::time_point_sec start,
      fc::time_point_sec end )const
{
   return my->_remote_hist->get_market_history( symbol1, symbol2, bucket, start, end );
}

vector<limit_order_object> wallet_api::get_account_limit_orders(
      const string& name_or_id,
      const string &base,
      const string &quote,
      uint32_t limit,
      optional<limit_order_id_type> ostart_id,
      optional<price> ostart_price)
{
   return my->_remote_db->get_account_limit_orders(name_or_id, base, quote, limit, ostart_id, ostart_price);
}

vector<limit_order_object> wallet_api::get_limit_orders(std::string a, std::string b, uint32_t limit)const
{
   return my->_remote_db->get_limit_orders(a, b, limit);
}

vector<call_order_object> wallet_api::get_call_orders(std::string a, uint32_t limit)const
{
   return my->_remote_db->get_call_orders(a, limit);
}

vector<force_settlement_object> wallet_api::get_settle_orders(std::string a, uint32_t limit)const
{
   return my->_remote_db->get_settle_orders(a, limit);
}

vector<collateral_bid_object> wallet_api::get_collateral_bids(std::string asset, uint32_t limit, uint32_t start)const
{
   return my->_remote_db->get_collateral_bids(asset, limit, start);
}

brain_key_info wallet_api::suggest_brain_key()const
{
   return graphene::wallet::utility::suggest_brain_key();
}

vector<brain_key_info> wallet_api::derive_owner_keys_from_brain_key(
      string brain_key,
      int number_of_desired_keys) const
{
   return graphene::wallet::utility::derive_owner_keys_from_brain_key(brain_key, number_of_desired_keys);
}

bool wallet_api::is_public_key_registered(string public_key) const
{
   bool is_known = my->_remote_db->is_public_key_registered(public_key);
   return is_known;
}

string wallet_api::serialize_transaction( signed_transaction tx )const
{
   return fc::to_hex(fc::raw::pack(tx));
}

variant wallet_api::get_object( object_id_type id ) const
{
   return my->_remote_db->get_objects({id}, {});
}

string wallet_api::get_wallet_filename() const
{
   return my->get_wallet_filename();
}

transaction_handle_type wallet_api::begin_builder_transaction()
{
   return my->begin_builder_transaction();
}

void wallet_api::add_operation_to_builder_transaction(
      transaction_handle_type transaction_handle,
      const operation& op)
{
   my->add_operation_to_builder_transaction(transaction_handle, op);
}

void wallet_api::replace_operation_in_builder_transaction(
      transaction_handle_type handle,
      unsigned operation_index,
      const operation& new_op)
{
   my->replace_operation_in_builder_transaction(handle, operation_index, new_op);
}

asset wallet_api::set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset)
{
   return my->set_fees_on_builder_transaction(handle, fee_asset);
}

transaction wallet_api::preview_builder_transaction(transaction_handle_type handle)
{
   return my->preview_builder_transaction(handle);
}

signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast)
{
   return my->sign_builder_transaction(transaction_handle, broadcast);
}

signed_transaction wallet_api::sign_builder_transaction2(transaction_handle_type transaction_handle,
                                                        const vector<public_key_type>& explicit_keys,
                                                        bool broadcast)
{
   return my->sign_builder_transaction2(transaction_handle, explicit_keys, broadcast);
}

pair<transaction_id_type,signed_transaction> wallet_api::broadcast_transaction(signed_transaction tx)
{
    return my->broadcast_transaction(tx);
}

signed_transaction wallet_api::propose_builder_transaction(
      transaction_handle_type handle,
      time_point_sec expiration,
      uint32_t review_period_seconds,
      bool broadcast)
{
   return my->propose_builder_transaction(handle, expiration, review_period_seconds, broadcast);
}

signed_transaction wallet_api::propose_builder_transaction2(
      transaction_handle_type handle,
      string account_name_or_id,
      time_point_sec expiration,
      uint32_t review_period_seconds,
      bool broadcast)
{
   return my->propose_builder_transaction2(handle, account_name_or_id, expiration, review_period_seconds, broadcast);
}

void wallet_api::remove_builder_transaction(transaction_handle_type handle)
{
   return my->remove_builder_transaction(handle);
}

account_object wallet_api::get_account(string account_name_or_id) const
{
   return my->get_account(account_name_or_id);
}

extended_asset_object wallet_api::get_asset(string asset_name_or_id) const
{
   auto found_asset = my->find_asset(asset_name_or_id);
   FC_ASSERT( found_asset, "Unable to find asset '${a}'", ("a",asset_name_or_id) );
   return *found_asset;
}

asset_bitasset_data_object wallet_api::get_bitasset_data(string asset_name_or_id) const
{
   auto asset = get_asset(asset_name_or_id);
   FC_ASSERT(asset.is_market_issued() && asset.bitasset_data_id);
   return my->get_object(*asset.bitasset_data_id);
}

account_id_type wallet_api::get_account_id(string account_name_or_id) const
{
   return my->get_account_id(account_name_or_id);
}

asset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const
{
   return my->get_asset_id(asset_symbol_or_id);
}

bool wallet_api::import_key(string account_name_or_id, string wif_key)
{
   FC_ASSERT(!is_locked());
   // backup wallet
   fc::optional<fc::ecc::private_key> optional_private_key = wif_to_key(wif_key);
   if (!optional_private_key)
      FC_THROW("Invalid private key");
   string shorthash = detail::address_to_shorthash(optional_private_key->get_public_key());
   copy_wallet_file( "before-import-key-" + shorthash );

   if( my->import_key(account_name_or_id, wif_key) )
   {
      save_wallet_file();
      copy_wallet_file( "after-import-key-" + shorthash );
      return true;
   }
   return false;
}

map<string, bool> wallet_api::import_accounts( string filename, string password )
{
   FC_ASSERT( !is_locked() );
   FC_ASSERT( fc::exists( filename ) );

   const auto imported_keys = fc::json::from_file<exported_keys>( filename );

   const auto password_hash = fc::sha512::hash( password );
   FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum );

   map<string, bool> result;
   for( const auto& item : imported_keys.account_keys )
   {
       const auto import_this_account = [ & ]() -> bool
       {
           try
           {
               const account_object account = get_account( item.account_name );
               const auto& owner_keys = account.owner.get_keys();
               const auto& active_keys = account.active.get_keys();

               for( const auto& public_key : item.public_keys )
               {
                   if( std::find( owner_keys.begin(), owner_keys.end(), public_key ) != owner_keys.end() )
                       return true;

                   if( std::find( active_keys.begin(), active_keys.end(), public_key ) != active_keys.end() )
                       return true;
               }
           }
           catch( ... )
           {
           }

           return false;
       };

       const auto should_proceed = import_this_account();
       result[ item.account_name ] = should_proceed;

       if( should_proceed )
       {
           uint32_t import_successes = 0;
           uint32_t import_failures = 0;
           // TODO: First check that all private keys match public keys
           for( const auto& encrypted_key : item.encrypted_private_keys )
           {
               try
               {
                  const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key );
                  const auto private_key = fc::raw::unpack<private_key_type>( plain_text );

                  import_key( item.account_name, string( graphene::utilities::key_to_wif( private_key ) ) );
                  ++import_successes;
               }
               catch( const fc::exception& e )
               {
                  elog( "Couldn't import key due to exception ${e}", ("e", e.to_detail_string()) );
                  ++import_failures;
               }
           }
           ilog( "successfully imported ${n} keys for account ${name}",
                 ("n", import_successes)("name", item.account_name) );
           if( import_failures > 0 )
              elog( "failed to import ${n} keys for account ${name}",
                    ("n", import_failures)("name", item.account_name) );
       }
   }

   return result;
}

bool wallet_api::import_account_keys(
      string filename,
      string password,
      string src_account_name,
      string dest_account_name )
{
   FC_ASSERT( !is_locked() );
   FC_ASSERT( fc::exists( filename ) );

   bool is_my_account = false;
   const auto accounts = list_my_accounts();
   for( const auto& account : accounts )
   {
       if( account.name == dest_account_name )
       {
           is_my_account = true;
           break;
       }
   }
   FC_ASSERT( is_my_account );

   const auto imported_keys = fc::json::from_file<exported_keys>( filename );

   const auto password_hash = fc::sha512::hash( password );
   FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum );

   bool found_account = false;
   for( const auto& item : imported_keys.account_keys )
   {
       if( item.account_name != src_account_name )
           continue;

       found_account = true;

       for( const auto& encrypted_key : item.encrypted_private_keys )
       {
           const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key );
           const auto private_key = fc::raw::unpack<private_key_type>( plain_text );

           my->import_key( dest_account_name, string( graphene::utilities::key_to_wif( private_key ) ) );
       }

       return true;
   }
   save_wallet_file();

   FC_ASSERT( found_account );

   return false;
}

string wallet_api::normalize_brain_key(string s) const
{
   return detail::normalize_brain_key( s );
}

variant wallet_api::info()
{
   return my->info();
}

variant_object wallet_api::about() const
{
    return my->about();
}

fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_string, int sequence_number) const
{
   return detail::derive_private_key( prefix_string, sequence_number );
}

signed_transaction wallet_api::register_account(string name,
                                                public_key_type owner_pubkey,
                                                public_key_type active_pubkey,
                                                string  registrar_account,
                                                string  referrer_account,
                                                uint32_t referrer_percent,
                                                bool broadcast)
{
   return my->register_account( name, owner_pubkey, active_pubkey,
                                registrar_account, referrer_account, referrer_percent, broadcast );
}
signed_transaction wallet_api::create_account_with_brain_key(string brain_key, string account_name,
                                                             string registrar_account, string referrer_account,
                                                             bool broadcast /* = false */)
{
   return my->create_account_with_brain_key(
            brain_key, account_name, registrar_account,
            referrer_account, broadcast
            );
}
signed_transaction wallet_api::issue_asset(string to_account, string amount, string symbol,
                                           string memo, bool broadcast)
{
   return my->issue_asset(to_account, amount, symbol, memo, broadcast);
}

signed_transaction wallet_api::transfer(string from, string to, string amount,
                                        string asset_symbol, string memo, bool broadcast /* = false */)
{
   return my->transfer(from, to, amount, asset_symbol, memo, broadcast);
}
signed_transaction wallet_api::create_asset(string issuer,
                                            string symbol,
                                            uint8_t precision,
                                            asset_options common,
                                            fc::optional<bitasset_options> bitasset_opts,
                                            bool broadcast)

{
   return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast);
}

signed_transaction wallet_api::update_asset(string symbol,
                                            optional<string> new_issuer,
                                            asset_options new_options,
                                            bool broadcast /* = false */)
{
   return my->update_asset(symbol, new_issuer, new_options, broadcast);
}

signed_transaction wallet_api::update_asset_issuer(string symbol,
                                            string new_issuer,
                                            bool broadcast /* = false */)
{
   return my->update_asset_issuer(symbol, new_issuer, broadcast);
}

signed_transaction wallet_api::update_bitasset(string symbol,
                                               bitasset_options new_options,
                                               bool broadcast /* = false */)
{
   return my->update_bitasset(symbol, new_options, broadcast);
}

signed_transaction wallet_api::update_asset_feed_producers(string symbol,
                                                           flat_set<string> new_feed_producers,
                                                           bool broadcast /* = false */)
{
   return my->update_asset_feed_producers(symbol, new_feed_producers, broadcast);
}

signed_transaction wallet_api::publish_asset_feed(string publishing_account,
                                                  string symbol,
                                                  price_feed feed,
                                                  bool broadcast /* = false */)
{
   return my->publish_asset_feed(publishing_account, symbol, feed, broadcast);
}

signed_transaction wallet_api::fund_asset_fee_pool(string from,
                                                   string symbol,
                                                   string amount,
                                                   bool broadcast /* = false */)
{
   return my->fund_asset_fee_pool(from, symbol, amount, broadcast);
}

signed_transaction wallet_api::claim_asset_fee_pool(string symbol,
                                                    string amount,
                                                    bool broadcast /* = false */)
{
   return my->claim_asset_fee_pool(symbol, amount, broadcast);
}

signed_transaction wallet_api::reserve_asset(string from,
                                          string amount,
                                          string symbol,
                                          bool broadcast /* = false */)
{
   return my->reserve_asset(from, amount, symbol, broadcast);
}

signed_transaction wallet_api::global_settle_asset(string symbol,
                                                   price settle_price,
                                                   bool broadcast /* = false */)
{
   return my->global_settle_asset(symbol, settle_price, broadcast);
}

signed_transaction wallet_api::settle_asset(string account_to_settle,
                                            string amount_to_settle,
                                            string symbol,
                                            bool broadcast /* = false */)
{
   return my->settle_asset(account_to_settle, amount_to_settle, symbol, broadcast);
}

signed_transaction wallet_api::bid_collateral(string bidder_name,
                                              string debt_amount, string debt_symbol,
                                              string additional_collateral,
                                              bool broadcast )
{
   return my->bid_collateral(bidder_name, debt_amount, debt_symbol, additional_collateral, broadcast);
}

signed_transaction wallet_api::whitelist_account(string authorizing_account,
                                                 string account_to_list,
                                                 account_whitelist_operation::account_listing new_listing_status,
                                                 bool broadcast /* = false */)
{
   return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast);
}

signed_transaction wallet_api::create_committee_member(string owner_account, string url,
                                               bool broadcast /* = false */)
{
   return my->create_committee_member(owner_account, url, broadcast);
}

map<string,witness_id_type> wallet_api::list_witnesses(const string& lowerbound, uint32_t limit)
{
   return my->_remote_db->lookup_witness_accounts(lowerbound, limit);
}

map<string,committee_member_id_type> wallet_api::list_committee_members(const string& lowerbound, uint32_t limit)
{
   return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit);
}

witness_object wallet_api::get_witness(string owner_account)
{
   return my->get_witness(owner_account);
}

committee_member_object wallet_api::get_committee_member(string owner_account)
{
   return my->get_committee_member(owner_account);
}

signed_transaction wallet_api::create_witness(string owner_account,
                                              string url,
                                              bool broadcast /* = false */)
{
   return my->create_witness(owner_account, url, broadcast);
}

signed_transaction wallet_api::create_worker(
   string owner_account,
   time_point_sec work_begin_date,
   time_point_sec work_end_date,
   share_type daily_pay,
   string name,
   string url,
   variant worker_settings,
   bool broadcast /* = false */)
{
   return my->create_worker( owner_account, work_begin_date, work_end_date,
      daily_pay, name, url, worker_settings, broadcast );
}

signed_transaction wallet_api::update_worker_votes(
   string owner_account,
   worker_vote_delta delta,
   bool broadcast /* = false */)
{
   return my->update_worker_votes( owner_account, delta, broadcast );
}

signed_transaction wallet_api::update_witness(
   string witness_name,
   string url,
   string block_signing_key,
   bool broadcast /* = false */)
{
   return my->update_witness(witness_name, url, block_signing_key, broadcast);
}

vector< vesting_balance_object_with_info > wallet_api::get_vesting_balances( string account_name )
{
   return my->get_vesting_balances( account_name );
}

signed_transaction wallet_api::withdraw_vesting(
   string witness_name,
   string amount,
   string asset_symbol,
   bool broadcast /* = false */)
{
   return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast );
}

signed_transaction wallet_api::vote_for_committee_member(string voting_account,
                                                 string witness,
                                                 bool approve,
                                                 bool broadcast /* = false */)
{
   return my->vote_for_committee_member(voting_account, witness, approve, broadcast);
}

signed_transaction wallet_api::vote_for_witness(string voting_account,
                                                string witness,
                                                bool approve,
                                                bool broadcast /* = false */)
{
   return my->vote_for_witness(voting_account, witness, approve, broadcast);
}

signed_transaction wallet_api::set_voting_proxy(string account_to_modify,
                                                optional<string> voting_account,
                                                bool broadcast /* = false */)
{
   return my->set_voting_proxy(account_to_modify, voting_account, broadcast);
}

signed_transaction wallet_api::set_desired_witness_and_committee_member_count(string account_to_modify,
                                                                      uint16_t desired_number_of_witnesses,
                                                                      uint16_t desired_number_of_committee_members,
                                                                      bool broadcast /* = false */)
{
   return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses,
                                                     desired_number_of_committee_members, broadcast);
}

void wallet_api::set_wallet_filename(string wallet_filename)
{
   my->_wallet_filename = wallet_filename;
}

signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broadcast /* = false */)
{ try {
   return my->sign_transaction( tx, broadcast);
} FC_CAPTURE_AND_RETHROW( (tx) ) }

signed_transaction wallet_api::sign_transaction2(signed_transaction tx, const vector<public_key_type>& signing_keys,
                                                 bool broadcast /* = false */)
{ try {
   return my->sign_transaction2( tx, signing_keys, broadcast);
} FC_CAPTURE_AND_RETHROW( (tx) ) }

flat_set<public_key_type> wallet_api::get_transaction_signers(const signed_transaction &tx) const
{ try {
   return my->get_transaction_signers(tx);
} FC_CAPTURE_AND_RETHROW( (tx) ) }

vector<flat_set<account_id_type>> wallet_api::get_key_references(const vector<public_key_type> &keys) const
{ try {
   return my->get_key_references(keys);
} FC_CAPTURE_AND_RETHROW( (keys) ) }

operation wallet_api::get_prototype_operation(string operation_name)
{
   return my->get_prototype_operation( operation_name );
}

void wallet_api::dbg_make_uia(string creator, string symbol)
{
   FC_ASSERT(!is_locked());
   my->dbg_make_uia(creator, symbol);
}

void wallet_api::dbg_make_mia(string creator, string symbol)
{
   FC_ASSERT(!is_locked());
   my->dbg_make_mia(creator, symbol);
}

void wallet_api::dbg_push_blocks( std::string src_filename, uint32_t count )
{
   my->dbg_push_blocks( src_filename, count );
}

void wallet_api::dbg_generate_blocks( std::string debug_wif_key, uint32_t count )
{
   my->dbg_generate_blocks( debug_wif_key, count );
}

void wallet_api::dbg_stream_json_objects( const std::string& filename )
{
   my->dbg_stream_json_objects( filename );
}

void wallet_api::dbg_update_object( fc::variant_object update )
{
   my->dbg_update_object( update );
}

void wallet_api::network_add_nodes( const vector<string>& nodes )
{
   my->network_add_nodes( nodes );
}

vector< variant > wallet_api::network_get_connected_peers()
{
   return my->network_get_connected_peers();
}

void wallet_api::flood_network(string prefix, uint32_t number_of_transactions)
{
   FC_ASSERT(!is_locked());
   my->flood_network(prefix, number_of_transactions);
}

signed_transaction wallet_api::propose_parameter_change(
   const string& proposing_account,
   fc::time_point_sec expiration_time,
   const variant_object& changed_values,
   bool broadcast /* = false */
   )
{
   return my->propose_parameter_change( proposing_account, expiration_time, changed_values, broadcast );
}

signed_transaction wallet_api::propose_fee_change(
   const string& proposing_account,
   fc::time_point_sec expiration_time,
   const variant_object& changed_fees,
   bool broadcast /* = false */
   )
{
   return my->propose_fee_change( proposing_account, expiration_time, changed_fees, broadcast );
}

signed_transaction wallet_api::approve_proposal(
   const string& fee_paying_account,
   const string& proposal_id,
   const approval_delta& delta,
   bool broadcast /* = false */
   )
{
   return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast );
}

global_property_object wallet_api::get_global_properties() const
{
   return my->get_global_properties();
}

dynamic_global_property_object wallet_api::get_dynamic_global_properties() const
{
   return my->get_dynamic_global_properties();
}

signed_transaction wallet_api::add_transaction_signature( signed_transaction tx,
                                                          bool broadcast )
{
   return my->add_transaction_signature( tx, broadcast );
}

string wallet_api::help()const
{
   std::vector<std::string> method_names = my->method_documentation.get_method_names();
   std::stringstream ss;
   for (const std::string method_name : method_names)
   {
      try
      {
         ss << my->method_documentation.get_brief_description(method_name);
      }
      catch (const fc::key_not_found_exception&)
      {
         ss << method_name << " (no help available)\n";
      }
   }
   return ss.str();
}

string wallet_api::gethelp(const string& method)const
{
   fc::api<wallet_api> tmp;
   std::stringstream ss;
   ss << "\n";

   std::string doxygenHelpString = my->method_documentation.get_detailed_description(method);
   if (!doxygenHelpString.empty())
      ss << doxygenHelpString << "\n";

   if( method == "import_key" )
   {
      ss << "usage: import_key ACCOUNT_NAME_OR_ID  WIF_PRIVATE_KEY\n\n";
      ss << "example: import_key \"1.3.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n";
      ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n";
   }
   else if( method == "transfer" )
   {
      ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n";
      ss << "example: transfer \"1.3.11\" \"1.3.4\" 1000.03 CORE \"memo\" true\n";
      ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n";
   }
   else if( method == "create_account_with_brain_key" )
   {
      ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n";
      ss << "example: create_account_with_brain_key "
         << "\"my really long brain key\" \"newaccount\" \"1.3.11\" \"1.3.11\" true\n";
      ss << "example: create_account_with_brain_key "
         << "\"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n";
      ss << "\n";
      ss << "This method should be used if you would like the wallet to generate new keys derived from the "
         << "brain key.\n";
      ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY.  "
         << "Use\n";
      ss << "register_account if you already know the keys you know the public keys that you would like to "
         << "register.\n";

   }
   else if( method == "register_account" )
   {
      ss << "usage: register_account ACCOUNT_NAME OWNER_PUBLIC_KEY ACTIVE_PUBLIC_KEY REGISTRAR "
         << "REFERRER REFERRER_PERCENT BROADCAST\n\n";
      ss << "example: register_account \"newaccount\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" "
         << "\"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"1.3.11\" \"1.3.11\" 50 true\n";
      ss << "\n";
      ss << "Use this method to register an account for which you do not know the private keys.";
   }
   else if( method == "create_asset" )
   {
      ss << "usage: ISSUER SYMBOL PRECISION_DIGITS OPTIONS BITASSET_OPTIONS BROADCAST\n\n";
      ss << "PRECISION_DIGITS: the number of digits after the decimal point\n\n";
      ss << "Example value of OPTIONS: \n";
      ss << fc::json::to_pretty_string( graphene::chain::asset_options() );
      ss << "\nExample value of BITASSET_OPTIONS: \n";
      ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() );
      ss << "\nBITASSET_OPTIONS may be null\n";
   }
   else if (doxygenHelpString.empty())
      ss << "No help defined for method " << method << "\n";

   return ss.str();
}

bool wallet_api::load_wallet_file( string wallet_filename )
{
   return my->load_wallet_file( wallet_filename );
}

void wallet_api::quit()
{
   my->quit();
}

void wallet_api::save_wallet_file( string wallet_filename )
{
   my->save_wallet_file( wallet_filename );
}

std::map<string,std::function<string(fc::variant,const fc::variants&)> >
wallet_api::get_result_formatters() const
{
   return my->get_result_formatters();
}

bool wallet_api::is_locked()const
{
   return my->is_locked();
}
bool wallet_api::is_new()const
{
   return my->_wallet.cipher_keys.size() == 0;
}

void wallet_api::encrypt_keys()
{
   my->encrypt_keys();
}

void wallet_api::lock()
{ try {
   FC_ASSERT( !is_locked() );
   encrypt_keys();
   for( auto key : my->_keys )
      key.second = key_to_wif(fc::ecc::private_key());
   my->_keys.clear();
   my->_checksum = fc::sha512();
   my->self.lock_changed(true);
} FC_CAPTURE_AND_RETHROW() }

void wallet_api::unlock(string password)
{ try {
   FC_ASSERT(password.size() > 0);
   auto pw = fc::sha512::hash(password.c_str(), password.size());
   vector<char> decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys);
   auto pk = fc::raw::unpack<plain_keys>(decrypted);
   FC_ASSERT(pk.checksum == pw);
   my->_keys = std::move(pk.keys);
   my->_checksum = pk.checksum;
   my->self.lock_changed(false);
} FC_CAPTURE_AND_RETHROW() }

void wallet_api::set_password( string password )
{
   if( !is_new() )
      FC_ASSERT( !is_locked(), "The wallet must be unlocked before the password can be set" );
   my->_checksum = fc::sha512::hash( password.c_str(), password.size() );
   lock();
}

vector< signed_transaction > wallet_api::import_balance(
      string name_or_id,
      const vector<string>& wif_keys,
      bool broadcast )
{
   return my->import_balance( name_or_id, wif_keys, broadcast );
}

map<public_key_type, string> wallet_api::dump_private_keys()
{
   FC_ASSERT(!is_locked());
   return my->_keys;
}

signed_transaction wallet_api::upgrade_account( string name, bool broadcast )
{
   return my->upgrade_account(name,broadcast);
}

signed_transaction wallet_api::sell_asset(string seller_account,
                                          string amount_to_sell,
                                          string symbol_to_sell,
                                          string min_to_receive,
                                          string symbol_to_receive,
                                          uint32_t expiration,
                                          bool   fill_or_kill,
                                          bool   broadcast)
{
   return my->sell_asset(seller_account, amount_to_sell, symbol_to_sell, min_to_receive,
                         symbol_to_receive, expiration, fill_or_kill, broadcast);
}

signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell,
                                                string asset_symbol, string amount_of_collateral, bool broadcast)
{
   FC_ASSERT(!is_locked());
   return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast);
}

signed_transaction wallet_api::borrow_asset_ext( string seller_name, string amount_to_sell,
                                                 string asset_symbol, string amount_of_collateral,
                                                 call_order_update_operation::extensions_type extensions,
                                                 bool broadcast)
{
   FC_ASSERT(!is_locked());
   return my->borrow_asset_ext(seller_name, amount_to_sell, asset_symbol,
                               amount_of_collateral, extensions, broadcast);
}

signed_transaction wallet_api::cancel_order(object_id_type order_id, bool broadcast)
{
   FC_ASSERT(!is_locked());
   return my->cancel_order(order_id, broadcast);
}

memo_data wallet_api::sign_memo(string from, string to, string memo)
{
   FC_ASSERT(!is_locked());
   return my->sign_memo(from, to, memo);
}

string wallet_api::read_memo(const memo_data& memo)
{
   FC_ASSERT(!is_locked());
   return my->read_memo(memo);
}

signed_message wallet_api::sign_message(string signer, string message)
{
   FC_ASSERT(!is_locked());
   return my->sign_message(signer, message);
}

bool wallet_api::verify_message( string message, string account, int block, const string& time, compact_signature sig )
{
   return my->verify_message( message, account, block, time, sig );
}

/** Verify a message signed with sign_message
 *
 * @param message the signed_message structure containing message, meta data and signature
 * @return true if signature matches
 */
bool wallet_api::verify_signed_message( signed_message message )
{
   return my->verify_signed_message( message );
}

/** Verify a message signed with sign_message, in its encapsulated form.
 *
 * @param message the complete encapsulated message string including separators and line feeds
 * @return true if signature matches
 */
bool wallet_api::verify_encapsulated_message( string message )
{
   return my->verify_encapsulated_message( message );
}

string wallet_api::get_key_label( public_key_type key )const
{
   auto key_itr   = my->_wallet.labeled_keys.get<by_key>().find(key);
   if( key_itr != my->_wallet.labeled_keys.get<by_key>().end() )
      return key_itr->label;
   return string();
}

string wallet_api::get_private_key( public_key_type pubkey )const
{
   return key_to_wif( my->get_private_key( pubkey ) );
}

public_key_type  wallet_api::get_public_key( string label )const
{
   try { return fc::variant(label, 1).as<public_key_type>( 1 ); } catch ( ... ){}

   auto key_itr   = my->_wallet.labeled_keys.get<by_label>().find(label);
   if( key_itr != my->_wallet.labeled_keys.get<by_label>().end() )
      return key_itr->key;
   return public_key_type();
}

bool               wallet_api::set_key_label( public_key_type key, string label )
{
   auto result = my->_wallet.labeled_keys.insert( key_label{label,key} );
   if( result.second  ) return true;

   auto key_itr   = my->_wallet.labeled_keys.get<by_key>().find(key);
   auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);
   if( label_itr == my->_wallet.labeled_keys.get<by_label>().end() )
   {
      if( key_itr != my->_wallet.labeled_keys.get<by_key>().end() )
         return my->_wallet.labeled_keys.get<by_key>().modify( key_itr, [&]( key_label& obj ){ obj.label = label; } );
   }
   return false;
}
map<string,public_key_type> wallet_api::get_blind_accounts()const
{
   map<string,public_key_type> result;
   for( const auto& item : my->_wallet.labeled_keys )
      result[item.label] = item.key;
   return result;
}
map<string,public_key_type> wallet_api::get_my_blind_accounts()const
{
   FC_ASSERT( !is_locked() );
   map<string,public_key_type> result;
   for( const auto& item : my->_wallet.labeled_keys )
   {
      if( my->_keys.find(item.key) != my->_keys.end() )
         result[item.label] = item.key;
   }
   return result;
}

public_key_type    wallet_api::create_blind_account( string label, string brain_key  )
{
   FC_ASSERT( !is_locked() );

   auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);
   if( label_itr !=  my->_wallet.labeled_keys.get<by_label>().end() )
      FC_ASSERT( !"Key with label already exists" );
   brain_key = fc::trim_and_normalize_spaces( brain_key );
   auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() );
   auto priv_key = fc::ecc::private_key::regenerate( secret );
   public_key_type pub_key  = priv_key.get_public_key();

   FC_ASSERT( set_key_label( pub_key, label ) );

   my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key );

   save_wallet_file();
   return pub_key;
}

vector<asset>   wallet_api::get_blind_balances( string key_or_label )
{
   vector<asset> result;
   map<asset_id_type, share_type> balances;

   vector<commitment_type> used;

   auto pub_key = get_public_key( key_or_label );
   auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();
   auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false)  );
   auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true)  );
   while( start != end )
   {
      if( !start->used  )
      {
         auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} );
         if( answer.size() )
            balances[start->amount.asset_id] += start->amount.amount;
         else
            used.push_back( start->commitment() );
      }
      ++start;
   }
   for( const auto& u : used )
   {
      auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );
      my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );
   }
   for( auto item : balances )
      result.push_back( asset( item.second, item.first ) );
   return result;
}

blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label,
                                                    string to_account_id_or_name,
                                                    string amount_in,
                                                    string symbol,
                                                    bool broadcast )
{ try {
   transfer_from_blind_operation from_blind;

   auto fees  = my->_remote_db->get_global_properties().parameters.get_current_fees();
   fc::optional<asset_object> asset_obj = get_asset(symbol);
   FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol));
   auto amount = asset_obj->amount_from_string(amount_in);

   from_blind.fee  = fees.calculate_fee( from_blind, asset_obj->options.core_exchange_rate );

   auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount );

   auto conf = blind_transfer_help( from_blind_account_key_or_label,
                               from_blind_account_key_or_label,
                               blind_in, symbol, false, true/*to_temp*/ );
   FC_ASSERT( conf.outputs.size() > 0 );

   auto to_account = my->get_account( to_account_id_or_name );
   from_blind.to = to_account.id;
   from_blind.amount = amount;
   from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor;
   from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } );
   from_blind.fee  = fees.calculate_fee( from_blind, asset_obj->options.core_exchange_rate );

   idump( (from_blind) );
   conf.trx.operations.push_back(from_blind);
   ilog( "about to validate" );
   conf.trx.validate();

   ilog( "about to broadcast" );
   conf.trx = sign_transaction( conf.trx, broadcast );

   if( broadcast && conf.outputs.size() == 2 ) {

       // Save the change
       blind_confirmation::output conf_output;
       blind_confirmation::output change_output = conf.outputs[0];

       // The wallet must have a private key for confirmation.to, this is used to decrypt the memo
       public_key_type from_key = get_public_key(from_blind_account_key_or_label);
       conf_output.confirmation.to = from_key;
       conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key;
       conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo;
       conf_output.confirmation_receipt = conf_output.confirmation;
       //try {
       receive_blind_transfer( conf_output.confirmation_receipt,
                               from_blind_account_key_or_label,
                               "@"+to_account.name );
       //} catch ( ... ){}
   }

   return conf;
} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) }

blind_confirmation wallet_api::blind_transfer( string from_key_or_label,
                                               string to_key_or_label,
                                               string amount_in,
                                               string symbol,
                                               bool broadcast )
{
   return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false );
}
blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
                                               string to_key_or_label,
                                               string amount_in,
                                               string symbol,
                                               bool broadcast,
                                               bool to_temp )
{
   blind_confirmation confirm;
   try {

   FC_ASSERT( !is_locked() );
   public_key_type from_key = get_public_key(from_key_or_label);
   public_key_type to_key   = get_public_key(to_key_or_label);

   fc::optional<asset_object> asset_obj = get_asset(symbol);
   FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol));

   blind_transfer_operation blind_tr;
   blind_tr.outputs.resize(2);

   auto fees  = my->_remote_db->get_global_properties().parameters.get_current_fees();

   auto amount = asset_obj->amount_from_string(amount_in);

   asset total_amount = asset_obj->amount(0);

   vector<fc::sha256> blinding_factors;

   //auto from_priv_key = my->get_private_key( from_key );

   blind_tr.fee  = fees.calculate_fee( blind_tr, asset_obj->options.core_exchange_rate );

   vector<commitment_type> used;

   auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();
   auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false)  );
   auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true)  );
   while( start != end )
   {
      auto result = my->_remote_db->get_blinded_balances( {start->commitment() } );
      if( result.size() == 0 )
      {
         used.push_back( start->commitment() );
      }
      else
      {
         blind_tr.inputs.push_back({start->commitment(), start->control_authority});
         blinding_factors.push_back( start->data.blinding_factor );
         total_amount += start->amount;

         if( total_amount >= amount + blind_tr.fee )
            break;
      }
      ++start;
   }
   for( const auto& u : used )
   {
      auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );
      my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );
   }

   FC_ASSERT( total_amount >= amount+blind_tr.fee,
              "Insufficient Balance",
              ("available",total_amount)("amount",amount)("fee",blind_tr.fee) );

   auto one_time_key = fc::ecc::private_key::generate();
   auto secret       = one_time_key.get_shared_secret( to_key );
   auto child        = fc::sha256::hash( secret );
   auto nonce        = fc::sha256::hash( one_time_key.get_secret() );
   auto blind_factor = fc::sha256::hash( child );

   auto from_secret  = one_time_key.get_shared_secret( from_key );
   auto from_child   = fc::sha256::hash( from_secret );
   auto from_nonce   = fc::sha256::hash( nonce );

   auto change = total_amount - amount - blind_tr.fee;
   fc::sha256 change_blind_factor;
   fc::sha256 to_blind_factor;
   if( change.amount > 0 )
   {
      idump(("to_blind_factor")(blind_factor) );
      blinding_factors.push_back( blind_factor );
      change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 );
      wdump(("change_blind_factor")(change_blind_factor) );
   }
   else // change == 0
   {
      blind_tr.outputs.resize(1);
      blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );
      idump(("to_sum_blind_factor")(blind_factor) );
      blinding_factors.push_back( blind_factor );
      idump(("nochange to_blind_factor")(blind_factor) );
   }
   fc::ecc::public_key from_pub_key = from_key;
   fc::ecc::public_key to_pub_key = to_key;

   blind_output to_out;
   to_out.owner       = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );
   to_out.commitment  = fc::ecc::blind( blind_factor, amount.amount.value );
   idump(("to_out.blind")(blind_factor)(to_out.commitment) );

   if( blind_tr.outputs.size() > 1 )
   {
      to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce,
                                                      0, RANGE_PROOF_MANTISSA, amount.amount.value );

      blind_output change_out;
      change_out.owner       = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 );
      change_out.commitment  = fc::ecc::blind( change_blind_factor, change.amount.value );
      change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce,
                                                          0, RANGE_PROOF_MANTISSA, change.amount.value );
      blind_tr.outputs[1] = change_out;

      blind_confirmation::output conf_output;
      conf_output.label = from_key_or_label;
      conf_output.pub_key = from_key;
      conf_output.decrypted_memo.from = from_key;
      conf_output.decrypted_memo.amount = change;
      conf_output.decrypted_memo.blinding_factor = change_blind_factor;
      conf_output.decrypted_memo.commitment = change_out.commitment;
      conf_output.decrypted_memo.check   = from_secret._hash[0].value();
      conf_output.confirmation.one_time_key = one_time_key.get_public_key();
      conf_output.confirmation.to = from_key;
      conf_output.confirmation.encrypted_memo =
            fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) );
      conf_output.auth = change_out.owner;
      conf_output.confirmation_receipt = conf_output.confirmation;

      confirm.outputs.push_back( conf_output );
   }
   blind_tr.outputs[0] = to_out;

   blind_confirmation::output conf_output;
   conf_output.label = to_key_or_label;
   conf_output.pub_key = to_key;
   conf_output.decrypted_memo.from = from_key;
   conf_output.decrypted_memo.amount = amount;
   conf_output.decrypted_memo.blinding_factor = blind_factor;
   conf_output.decrypted_memo.commitment = to_out.commitment;
   conf_output.decrypted_memo.check   = secret._hash[0].value();
   conf_output.confirmation.one_time_key = one_time_key.get_public_key();
   conf_output.confirmation.to = to_key;
   conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
   conf_output.auth = to_out.owner;
   conf_output.confirmation_receipt = conf_output.confirmation;

   confirm.outputs.push_back( conf_output );

   /** commitments must be in sorted order */
   std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(),
              [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
   std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(),
              [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } );

   confirm.trx.operations.emplace_back( std::move(blind_tr) );
   ilog( "validate before" );
   confirm.trx.validate();
   confirm.trx = sign_transaction(confirm.trx, broadcast);

   if( broadcast )
   {
      for( const auto& out : confirm.outputs )
      {
         try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){}
      }
   }

   return confirm;
} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) }

/*
 *  Transfers a public balance from @from to one or more blinded balances using a
 *  stealth transfer.
 */
blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name,
                                                  string asset_symbol,
                                                  /* map from key or label to amount */
                                                  vector<pair<string, string>> to_amounts,
                                                  bool broadcast )
{ try {
   FC_ASSERT( !is_locked() );
   idump((to_amounts));

   blind_confirmation confirm;
   account_object from_account = my->get_account(from_account_id_or_name);

   fc::optional<asset_object> asset_obj = get_asset(asset_symbol);
   FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol));

   transfer_to_blind_operation bop;
   bop.from   = from_account.id;

   vector<fc::sha256> blinding_factors;

   asset total_amount = asset_obj->amount(0);

   for( auto item : to_amounts )
   {
      auto one_time_key = fc::ecc::private_key::generate();
      auto to_key       = get_public_key( item.first );
      auto secret       = one_time_key.get_shared_secret( to_key );
      auto child        = fc::sha256::hash( secret );
      auto nonce        = fc::sha256::hash( one_time_key.get_secret() );
      auto blind_factor = fc::sha256::hash( child );

      blinding_factors.push_back( blind_factor );

      auto amount = asset_obj->amount_from_string(item.second);
      total_amount += amount;

      fc::ecc::public_key to_pub_key = to_key;
      blind_output out;
      out.owner       = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );
      out.commitment  = fc::ecc::blind( blind_factor, amount.amount.value );
      if( to_amounts.size() > 1 )
         out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce,
                                                      0, RANGE_PROOF_MANTISSA, amount.amount.value );

      blind_confirmation::output conf_output;
      conf_output.label = item.first;
      conf_output.pub_key = to_key;
      conf_output.decrypted_memo.amount = amount;
      conf_output.decrypted_memo.blinding_factor = blind_factor;
      conf_output.decrypted_memo.commitment = out.commitment;
      conf_output.decrypted_memo.check   = secret._hash[0].value();
      conf_output.confirmation.one_time_key = one_time_key.get_public_key();
      conf_output.confirmation.to = to_key;
      conf_output.confirmation.encrypted_memo =
            fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
      conf_output.confirmation_receipt = conf_output.confirmation;

      confirm.outputs.push_back( conf_output );

      bop.outputs.push_back(out);
   }
   bop.amount          = total_amount;
   bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );

   /** commitments must be in sorted order */
   std::sort( bop.outputs.begin(), bop.outputs.end(),
              [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );

   confirm.trx.operations.push_back( bop );
   my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.get_current_fees());
   confirm.trx.validate();
   confirm.trx = sign_transaction(confirm.trx, broadcast);

   if( broadcast )
   {
      for( const auto& out : confirm.outputs )
      {
         try {
            receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name );
         } catch ( ... ){}
      }
   }

   return confirm;
} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) }

blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo )
{
   FC_ASSERT( !is_locked() );
   stealth_confirmation conf(confirmation_receipt);
   FC_ASSERT( conf.to );

   blind_receipt result;
   result.conf = conf;

   auto to_priv_key_itr = my->_keys.find( *conf.to );
   FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) );

   auto to_priv_key = wif_to_key( to_priv_key_itr->second );
   FC_ASSERT( to_priv_key );

   auto secret       = to_priv_key->get_shared_secret( conf.one_time_key );
   auto child        = fc::sha256::hash( secret );

   auto child_priv_key = to_priv_key->child( child );
   //auto blind_factor = fc::sha256::hash( child );

   auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo );
   auto memo = fc::raw::unpack<stealth_confirmation::memo_data>( plain_memo );

   result.to_key   = *conf.to;
   result.to_label = get_key_label( result.to_key );
   if( memo.from )
   {
      result.from_key = *memo.from;
      result.from_label = get_key_label( result.from_key );
      if( result.from_label == string() )
      {
         result.from_label = opt_from;
         set_key_label( result.from_key, result.from_label );
      }
   }
   else
   {
      result.from_label = opt_from;
   }
   result.amount = memo.amount;
   result.memo = opt_memo;

   // confirm the amount matches the commitment (verify the blinding factor)
   auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value );
   FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) );

   blind_balance bal;
   bal.amount = memo.amount;
   bal.to     = *conf.to;
   if( memo.from ) bal.from   = *memo.from;
   bal.one_time_key = conf.one_time_key;
   bal.blinding_factor = memo.blinding_factor;
   bal.commitment = memo.commitment;
   bal.used = false;

   auto child_pubkey = child_priv_key.get_public_key();
   auto owner = authority(1, public_key_type(child_pubkey), 1);
   result.control_authority = owner;
   result.data = memo;

   auto child_key_itr = owner.key_auths.find( child_pubkey );
   if( child_key_itr != owner.key_auths.end() )
      my->_keys[child_key_itr->first] = key_to_wif( child_priv_key );

   // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal );

   result.date = fc::time_point::now();
   my->_wallet.blind_receipts.insert( result );
   my->_keys[child_pubkey] = key_to_wif( child_priv_key );

   save_wallet_file();

   return result;
}

vector<blind_receipt> wallet_api::blind_history( string key_or_account )
{
   vector<blind_receipt> result;
   auto pub_key = get_public_key( key_or_account );

   if( pub_key == public_key_type() )
      return vector<blind_receipt>();

   for( auto& r : my->_wallet.blind_receipts )
   {
      if( r.from_key == pub_key || r.to_key == pub_key )
         result.push_back( r );
   }
   std::sort( result.begin(), result.end(),
              [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } );
   return result;
}

order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit )
{
   return( my->_remote_db->get_order_book( base, quote, limit ) );
}

// custom operations
signed_transaction wallet_api::account_store_map(string account, string catalog, bool remove,
      flat_map<string, optional<string>> key_values, bool broadcast)
{
   return my->account_store_map(account, catalog, remove, key_values, broadcast);
}

vector<account_storage_object> wallet_api::get_account_storage(string account, string catalog)
{ try {
   return my->_custom_operations->get_storage_info(account, catalog);
} FC_CAPTURE_AND_RETHROW( (account)(catalog) ) }

signed_block_with_info::signed_block_with_info( const signed_block& block )
   : signed_block( block )
{
   block_id = id();
   signing_key = signee();
   transaction_ids.reserve( transactions.size() );
   for( const processed_transaction& tx : transactions )
      transaction_ids.push_back( tx.id() );
}

vesting_balance_object_with_info::vesting_balance_object_with_info(
      const vesting_balance_object& vbo,
      fc::time_point_sec now )
   : vesting_balance_object( vbo )
{
   allowed_withdraw = get_allowed_withdraw( now );
   allowed_withdraw_time = now;
}

} } // graphene::wallet

namespace fc {
   void to_variant( const account_multi_index_type& accts, variant& vo, uint32_t max_depth )
   {
      to_variant( std::vector<account_object>(accts.begin(), accts.end()), vo, max_depth );
   }

   void from_variant( const variant& var, account_multi_index_type& vo, uint32_t max_depth )
   {
      const std::vector<account_object>& v = var.as<std::vector<account_object>>( max_depth );
      vo = account_multi_index_type(v.begin(), v.end());
   }
}

max_stars_repo_name: delioncoin/bitshares-core

max_issues_repo_name: delioncoin/bitshares-core

max_forks_repo_name: delioncoin/bitshares-core


text:
#ifndef PARMCB_MPI_SVA_TREES_HPP_
#define PARMCB_MPI_SVA_TREES_HPP_

//    Copyright (C) Dimitrios Michail 2019 - 2021.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          https://www.boost.org/LICENSE_1_0.txt)

#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/tuple/detail/tuple_basic.hpp>

#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <boost/mpi/collectives.hpp>
#include <boost/mpi/timer.hpp>

#include <cstddef>
#include <functional>
#include <iostream>
#include <iterator>
#include <limits>
#include <set>
#include <vector>
#include <cmath>

#include <parmcb/forestindex.hpp>
#include <parmcb/spvecgf2.hpp>
#include <parmcb/detail/fvs.hpp>
#include <parmcb/detail/cycles.hpp>
#include <parmcb/util.hpp>
#include <parmcb/mpi/sptrees.hpp>

namespace parmcb {

    template<class Graph, class WeightMap, class CycleOutputIterator, class CyclesBuilder, bool ParallelUsingTBB>
    typename boost::property_traits<WeightMap>::value_type _mcb_sva_trees_mpi(const Graph &g, WeightMap weight_map,
            CycleOutputIterator out, boost::mpi::communicator &world) {

        typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
        typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
        typedef typename boost::property_traits<WeightMap>::value_type WeightType;

        boost::mpi::timer total_timer;

        /*
         * Index the graph
         */
        ForestIndex<Graph> forest_index(g);
        auto csd = forest_index.cycle_space_dimension();
        if (world.rank() == 0) {
            std::cout << "Cycle space dimension: " << csd << std::endl;
        }

        /*
         * Initialize support vectors
         */
        std::vector<SpVecGF2<std::size_t>> support;
        for (std::size_t k = 0; k < csd; k++) {
            support.emplace_back(k);
        }

        /**
         * Scatter candidate cycles
         */
        std::vector<SerializableCandidateCycle<Graph>> candidate_cycles;
        if (world.rank() == 0) {
            /*
             * Compute all vertex-edge candidate pairs
             */
            std::vector<parmcb::SPTree<Graph, WeightMap>> trees;
            std::vector<parmcb::CandidateCycle<Graph, WeightMap>> cycles;
            CyclesBuilder cycles_builder;
            cycles_builder(g, weight_map, trees, cycles);
            std::cout << "Total candidate cycles: " << cycles.size() << std::endl;

            std::vector<SerializableCandidateCycle<Graph>> all_candidate_cycles;
            parmcb::CandidateCycleToSerializableConverter<Graph, WeightMap> converter(trees, forest_index);
            for (const auto &c : cycles) {
                all_candidate_cycles.push_back(converter(c));
            }

            /*
             * Divide them to processes
             */
            std::size_t stride = ceil((double) all_candidate_cycles.size() / world.size());
            std::vector<std::vector<SerializableCandidateCycle<Graph>>> chunks;
            for (int p = 0; p < world.size(); p++) {
                std::size_t istart = p * stride;
                std::size_t iend = istart + stride;
                std::vector<SerializableCandidateCycle<Graph>> chunk;
                std::size_t total = all_candidate_cycles.size();
                for (std::size_t i = istart; i < iend && i < total; i++) {
                    chunk.push_back(all_candidate_cycles[i]);
                }
                chunks.push_back(chunk);
            }
            boost::mpi::scatter(world, chunks, candidate_cycles, 0);
        } else {
            boost::mpi::scatter(world, std::vector<std::vector<SerializableCandidateCycle<Graph>>> { },
                    candidate_cycles, 0);
        }

        std::cout << "Rank " << world.rank() << " received " << candidate_cycles.size() << " candidates" << std::endl;

        /*
         * Group local candidate cycles per vertex
         */
        std::map<Vertex, std::vector<Edge>> perVertexCandidates;
        for (SerializableCandidateCycle<Graph> t : candidate_cycles) {
            Vertex s = t.v;
            Edge e = forest_index(t.e);
            if (perVertexCandidates.find(s) == perVertexCandidates.end()) {
                perVertexCandidates.insert(std::make_pair(s, std::vector<Edge> { }));
            }
            perVertexCandidates[s].push_back(e);
        }

        /*
         * Build trees for local candidate cycles
         */
        std::vector<parmcb::SPTree<Graph, WeightMap>> trees;
        std::vector<parmcb::CandidateCycle<Graph, WeightMap>> cycles;
        for (auto const &p : perVertexCandidates) {
            SPTree<Graph, WeightMap> tree(trees.size(), g, boost::get(boost::vertex_index, g), weight_map, p.first);
            trees.push_back(tree);
            std::vector<CandidateCycle<Graph, WeightMap>> tree_cycles = tree.create_candidate_cycles(p.second.begin(),
                    p.second.end());
            cycles.insert(cycles.end(), tree_cycles.begin(), tree_cycles.end());
        }
        std::cout << "Total candidate cycles: " << cycles.size() << std::endl;
        const bool sorted_cycles = true;
        if (sorted_cycles) {
            // sort
            std::cout << "Sorting cycles" << std::endl;
            std::sort(cycles.begin(), cycles.end(), [](const auto &a, const auto &b) {
                return a.weight() < b.weight();
            });
        }
        ShortestOddCycleLookup<Graph, WeightMap, ParallelUsingTBB> cycle_lookup(g, weight_map, trees, cycles,
                sorted_cycles);

        /*
         * Main loop
         */
        WeightType mcb_weight = WeightType();
        for (std::size_t k = 0; k < csd; k++) {
            if (k % 250 == 0) {
                std::cout << "Rank " << world.rank() << " at cycle " << k << std::endl;
            }

            // broadcast support vector
            if (world.rank() == 0) {
                boost::mpi::broadcast(world, support[k], 0);
            } else {
                SpVecGF2<std::size_t> received;
                boost::mpi::broadcast(world, received, 0);
                support[k] = received;
            }

            //std::cout << "Rank " << world.rank() << " has support = " << support[k] << std::endl;

            std::set<Edge> signed_edges;
            convert_edges(support[k], std::inserter(signed_edges, signed_edges.end()), forest_index);

            std::tuple<std::set<Edge>, WeightType, bool> best_local_cycle = cycle_lookup(signed_edges);

            std::vector<typename ForestIndex<Graph>::size_type> best_local_cycle_as_indices;
            convert_edges(std::get<0>(best_local_cycle),
                    std::inserter(best_local_cycle_as_indices, best_local_cycle_as_indices.end()), forest_index);
            SerializableMinOddCycle<Graph, WeightMap> local_min_odd_cycle(best_local_cycle_as_indices,
                    std::get<1>(best_local_cycle), std::get<2>(best_local_cycle));
            SerializableMinOddCycle<Graph, WeightMap> global_min_odd_cycle;

            boost::mpi::reduce(world, local_min_odd_cycle, global_min_odd_cycle,
                    SerializableMinOddCycleMinOp<Graph, WeightMap>(), 0);

            if (world.rank() == 0) {
                std::set<std::size_t> cyclek;
                for (std::size_t e : global_min_odd_cycle.edges) {
                    cyclek.insert(e);
                }
                for (std::size_t l = k + 1; l < csd; l++) {
                    if (support[l] * cyclek == 1) {
                        support[l] += support[k];
                    }
                }

                std::list<Edge> cyclek_edgelist;
                convert_edges(global_min_odd_cycle.edges, std::inserter(cyclek_edgelist, cyclek_edgelist.end()),
                        forest_index);
                mcb_weight += global_min_odd_cycle.weight;
                *out++ = cyclek_edgelist;
            }
        }

        if (world.rank() == 0) {
            std::cout << "Total time: " << total_timer.elapsed() << " (sec)" << std::endl;
        }

        return mcb_weight;

    }

    template<class Graph, class WeightMap, class CycleOutputIterator>
    typename boost::property_traits<WeightMap>::value_type mcb_sva_fvs_trees_mpi(const Graph &g, WeightMap weight_map,
            CycleOutputIterator out, boost::mpi::communicator &world) {
        return _mcb_sva_trees_mpi<Graph, WeightMap, CycleOutputIterator,
                parmcb::detail::FVSCyclesBuilder<Graph, WeightMap>, false>(g, weight_map, out, world);
    }

    template<class Graph, class WeightMap, class CycleOutputIterator>
    typename boost::property_traits<WeightMap>::value_type mcb_sva_fvs_trees_tbb_mpi(const Graph &g,
            WeightMap weight_map, CycleOutputIterator out, boost::mpi::communicator &world) {
        return _mcb_sva_trees_mpi<Graph, WeightMap, CycleOutputIterator,
                parmcb::detail::FVSCyclesBuilder<Graph, WeightMap>, true>(g, weight_map, out, world);
    }

    template<class Graph, class WeightMap, class CycleOutputIterator>
    typename boost::property_traits<WeightMap>::value_type mcb_sva_iso_trees_mpi(const Graph &g, WeightMap weight_map,
            CycleOutputIterator out, boost::mpi::communicator &world) {
        return _mcb_sva_trees_mpi<Graph, WeightMap, CycleOutputIterator,
                parmcb::detail::ISOCyclesBuilder<Graph, WeightMap>, false>(g, weight_map, out, world);
    }

    template<class Graph, class WeightMap, class CycleOutputIterator>
    typename boost::property_traits<WeightMap>::value_type mcb_sva_iso_trees_tbb_mpi(const Graph &g,
            WeightMap weight_map, CycleOutputIterator out, boost::mpi::communicator &world) {
        return _mcb_sva_trees_mpi<Graph, WeightMap, CycleOutputIterator,
                parmcb::detail::ISOCyclesBuilder<Graph, WeightMap>, true>(g, weight_map, out, world);
    }

} // namespace mcb

#endif

ext: hpp

max_stars_repo_name: d-michail/parmcb

max_issues_repo_name: d-michail/parmcb

max_forks_repo_name: d-michail/parmcb


text: #include <boost/units/base_dimension.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/**
 * @file    VectorUtils.hpp
 * @author  [Yi-Mu "Enoch" Chen](https://github.com/yimuchen)
 * @brief   Template functions for common vector operations.
 */

#ifndef USERUTILS_COMMON_STLUTILS_VECTORUTILS_HPP
#define USERUTILS_COMMON_STLUTILS_VECTORUTILS_HPP
#include <algorithm>
#include <fstream>
#include <set>
#include <sstream>
#include <string>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

namespace usr {

/**
 * @defgroup VecUtils Vector enhancements
 * @brief    std::vector enhancement functions.
 * @ingroup  Common
 * @details  A collection of STL recipes for performing vector operations, many
 * of which consists of full range parsing of vectors. Another important factor
 * is the variadic template interface for creating vectors, something which
 * will enable the use of variadic template interface for other function.
 * @{
 */
template<typename T>
std::vector<T> RemoveDuplicate( const std::vector<T>& dupvec );

template<typename OBJTYPE, typename ARGTYPE, typename ... VARTYPE>
std::vector<OBJTYPE> MakeVector( const ARGTYPE& first, const VARTYPE& ... arg );

template<typename OBJTYPE, typename ARGTYPE>
std::vector<OBJTYPE> MakeVector( const ARGTYPE& first );

template<typename OBJTYPE>
void ClearValue( std::vector<OBJTYPE>& vec, const OBJTYPE& x );

template<typename OBJTYPE>
void ClearIf( std::vector<OBJTYPE>& vec, bool ( * func )( const OBJTYPE& ) );

template<typename OBJTYPE>
bool FindValue( std::vector<OBJTYPE>& vec, const OBJTYPE& x );

template<typename OBJTYPE=std::string>
std::vector<OBJTYPE> ListFromFile( const std::string& file,
                                   const std::string& delim = "\n" );

/** @} */

/*-----------------------------------------------------------------------------
 *  Template Implementations
   --------------------------------------------------------------------------*/
/**
 * @brief Removing duplicates from a vector while maintaining vector order
 * regardless of whether the vector is sorted or not.
 */
template<typename T>
std::vector<T>
RemoveDuplicate( const std::vector<T>& dupvec )
{
  std::set<T> unique_set;
  std::vector<T> ans;

  for( const auto& item : dupvec ){
    if( unique_set.insert( item ).second ){
      ans.push_back( item );
    }
  }

  return ans;
}

/**
 * @brief Constructing a vector of same-typed inputs of arbitrary number of
 *        function inputs
 *
 * Basically, this functions allow for the function call like:
 * `MakeVector<int>(12,3,4,5,6,7,8,9)`, which is turn could enable functions
 * to be declared as:
 * ```cpp
 * f( int reqired, TYPES... args){ return f(required, MakeVector(args...)); }
 * ```
 * allowing the user to input arbitrary many arguments for a given function,
 * particularly useful if your functions has many defaulted arguments that
 * the user might not want to keep track of ordering.
 */
template<typename OBJTYPE, typename ARGTYPE, typename ... VARTYPE>
std::vector<OBJTYPE>
MakeVector( const ARGTYPE& first, const VARTYPE& ... arg )
{
  std::vector<OBJTYPE> ans = MakeVector<OBJTYPE>( arg ... );
  ans.insert( ans.begin(), OBJTYPE( first ) );
  return ans;
}

/**
 * @brief Starting point for the recursive call for the variadic interfaces.
 */
template<typename OBJTYPE, typename ARGTYPE>
std::vector<OBJTYPE>
MakeVector( const ARGTYPE& first )
{
  return std::vector<OBJTYPE>( 1, OBJTYPE( first ) );
}

/**
 * @brief removing element in a vector if element is equivalent to x
 */
template<typename OBJTYPE>
void
ClearValue( std::vector<OBJTYPE>& vec, const OBJTYPE& x )
{
  vec.erase( std::remove( vec.begin(), vec.end(), x ), vec.end() );
}

/**
 * @brief removing element in a vector if element matches some function
 */
template<typename OBJTYPE>
void
ClearIf( std::vector<OBJTYPE>& vec, bool ( * func )( const OBJTYPE& ) )
{
  vec.erase( std::remove_if( vec.begin(), vec.end(), func ), vec.end() );
}

/**
 * @brief Checking if any of the elements in the vector is equivalent to x
 */
template<typename OBJTYPE>
bool
FindValue( const std::vector<OBJTYPE>& vec, const OBJTYPE& x )
{
  return std::find( vec.begin(), vec.end(), x ) != vec.end();
}

/**
 * @brief Checking if any of the elements in the vector is equivalent to x
 */
template<typename OBJTYPE>
bool
FindValue( std::vector<OBJTYPE>& vec, const OBJTYPE& x )
{
  return std::find( vec.begin(), vec.end(), x ) != vec.end();
}

/**
 * @brief Get the Maximum element value in a vector
 */
template<typename OBJTYPE>
OBJTYPE
GetMaximum( const std::vector<OBJTYPE>& vec )
{
  return *std::max_element( vec.begin(), vec.end() );
}

/**
 * @brief Get the minimum element value in a vector
 */
template<typename OBJTYPE>
OBJTYPE
GetMinimum( const std::vector<OBJTYPE>& vec )
{
  return *std::min_element( vec.begin(), vec.end() );
}

/**
 * @brief Getting a list of objects from a file.
 *
 * Given a file path, the function return the contents of file broken by
 * delimiters (be default the delimiter character is '\n').
 */
template<typename OBJTYPE>
std::vector<OBJTYPE>
ListFromFile( const std::string& file, const std::string& delimiter )
{
  std::ifstream f( file );
  std::stringstream buffer;
  std::string content;
  std::vector<std::string> tokens;
  std::vector<OBJTYPE> ans;

  // Getting the full contents of the file
  buffer << f.rdbuf();
  content = buffer.str();

  // Boost algorithm for string splitting
  boost::split( tokens, content, boost::is_any_of( delimiter ) );

  // std algorithm for removing empty strings.
  tokens.erase( std::remove( tokens.begin(), tokens.end(), "" ), tokens.end() );

  for( const auto& t : tokens ){
    ans.push_back( boost::lexical_cast<OBJTYPE>( t ) );
  }

  return ans;
}

}/* usr */

#endif/* end of include guard: USERUTILS_COMMON_VECTORUTILS */

max_stars_repo_name: yimuchen/UserUtils

max_issues_repo_name: yimuchen/UserUtils

max_forks_repo_name: yimuchen/UserUtils


text:
// This file is part of the dune-stuff project:
//   https://github.com/wwu-numerik/dune-stuff
// Copyright holders: Rene Milk, Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

/**
   *  \file logging.hh
   *  \brief  logging
   **/
#ifndef DUNE_STUFF_COMMON_LOGGING_HH
#define DUNE_STUFF_COMMON_LOGGING_HH

#include <map>
#include <string>
#include <mutex>

#include <dune/stuff/common/disable_warnings.hh>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <dune/stuff/common/reenable_warnings.hh>

#include <dune/stuff/common/logstreams.hh>

namespace Dune {
namespace Stuff {
namespace Common {

class Logging;
//! global Logging instance
inline Logging& Logger();

/** \brief handles all logging
  **/
class Logging
{
private:
  Logging();
  //! cleanup stream and flag containers
  void deinit();

public:
  ~Logging();

  /** \brief setup loglevel, logfilename
     *  \param logflags any OR'd combination of flags
     *  \param logfile filename for log, can contain paths, but creation will fail if dir is non-existant
     **/
  void create(int logflags = (LOG_FILE | LOG_CONSOLE | LOG_ERROR), const std::string logfile = "dune_stuff_log",
              const std::string datadir = "data", const std::string _logdir = std::string("log"));

  //! \attention This will probably not do wht we want it to!
  void setPrefix(std::string prefix);
  void setStreamFlags(int streamID, int flags);
  int getStreamFlags(int streamID) const;

  /** \name forwarded Log functions
     * \{
     */
  template <class T>
  void log(T c, int streamID)
  {
    getStream(streamID) << c;
  } // Log

  /** \}
     */

  LogStream& getStream(int streamId);
  LogStream& error() { return getStream(LOG_ERROR); }
  LogStream& info() { return getStream(LOG_INFO); }
  LogStream& debug() { return getStream(LOG_DEBUG); }
  LogStream& devnull() { return emptyLogStream_; }

  //! flush all active streams
  void flush();
  //! creates a new LogStream with given id
  int addStream(int flags);

  //! re-enable all logging below given priority level
  void resume(LogStream::PriorityType prio = LogStream::default_suspend_priority);
  //! (temporarily) disable all logging below given priority level
  void suspend(LogStream::PriorityType prio = LogStream::default_suspend_priority);

  struct SuspendLocal
  {
    LogStream::PriorityType prio_;
    SuspendLocal(LogStream::PriorityType prio = LogStream::default_suspend_priority) : prio_(prio)
    {
      Logger().suspend(prio_);
    }

    ~SuspendLocal() { Logger().resume(prio_); }
  };

  struct ResumeLocal
  {
    LogStream::PriorityType prio_;
    ResumeLocal(LogStream::PriorityType prio = LogStream::default_suspend_priority) : prio_(prio)
    {
      Logger().resume(prio_);
    }

    ~ResumeLocal() { Logger().suspend(prio_); }
  };

private:
  boost::filesystem::path filename_;
  boost::filesystem::path filenameWoTime_;
  boost::filesystem::ofstream logfile_;
  typedef std::map<int, int> FlagMap;
  FlagMap flagmap_;
  typedef std::map<int, std::unique_ptr<LogStream>> StreamMap;
  StreamMap streammap_;
  typedef std::vector<int> IdVec;
  IdVec streamIDs_;
  int logflags_;
  EmptyLogStream emptyLogStream_;

  friend Logging& Logger();
  // satisfy stricter warnings wrt copying
  Logging(const Logging&) = delete;
  Logging& operator=(const Logging&) = delete;
};

inline Logging& Logger()
{
  static Logging log;
  return log;
}

} // namespace Common
} // namespace Stuff
} // namespace Dune

#define DSC_LOG Dune::Stuff::Common::Logger()
#define DSC_LOG_INFO DSC_LOG.info()
#define DSC_LOG_DEBUG DSC_LOG.debug()
#define DSC_LOG_ERROR DSC_LOG.error()
#define DSC_LOG_DEVNULL DSC_LOG.devnull()

#define DSC_LOG_INFO_0 (Dune::MPIHelper::getCollectiveCommunication().rank() == 0 ? DSC_LOG.info() : DSC_LOG.devnull())
#define DSC_LOG_DEBUG_0                                                                                \
  (Dune::MPIHelper::getCollectiveCommunication().rank() == 0 ? DSC_LOG.debug() : DSC_LOG.devnull())
#define DSC_LOG_ERROR_0                                                                                \
  (Dune::MPIHelper::getCollectiveCommunication().rank() == 0 ? DSC_LOG.error() : DSC_LOG.devnull())

#endif // DUNE_STUFF_COMMON_LOGGING_HH

ext: hh

max_stars_repo_name: ftalbrecht/dune-stuff-simplified

max_issues_repo_name: ftalbrecht/dune-stuff-simplified

max_forks_repo_name: ftalbrecht/dune-stuff-simplified


text:
//  Copyright 2013 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_MP_MATH_SETUP_HPP
#define BOOST_MP_MATH_SETUP_HPP

#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif

#define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
#undef BOOST_MATH_SMALL_CONSTANT
#define BOOST_MATH_SMALL_CONSTANT(x) x

#if !defined(TEST_MPF_50) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR_50) && \
    !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT)
#define TEST_MPF_50
#define TEST_MPFR_50
#define TEST_CPP_DEC_FLOAT
#define TEST_FLOAT128
#define TEST_CPP_BIN_FLOAT

#ifdef _MSC_VER
#pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
#endif
#ifdef __GNUC__
#pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
#endif

#endif

#if defined(TEST_MPF_50)
#include <nil/crypto3/multiprecision/gmp.hpp>
#include <nil/crypto3/multiprecision/debug_adaptor.hpp>

#define MPF_TESTS /*test(number<gmp_float<18> >(), "number<gmp_float<18> >");*/ \
    mpf_float::default_precision(20);                                           \
    test(mpf_float(), "number<gmp_float<0> > (20 digit precision)");            \
    mpf_float::default_precision(35);                                           \
    test(mpf_float(), "number<gmp_float<0> > (35 digit precision)");            \
    test(number<gmp_float<30>>(), "number<gmp_float<30> >");                    \
    test(number<gmp_float<35>>(), "number<gmp_float<35> >");                    \
    /* there should be at least one test with expression templates off: */      \
    test(number<gmp_float<35>, et_off>(), "number<gmp_float<35>, et_off>");
#define MPF_SMALL_TESTS /*test(number<gmp_float<18> >(), "number<gmp_float<18> >");*/ \
    test(number<gmp_float<30>>(), "number<gmp_float<30> >");                          \
    test(number<gmp_float<35>>(), "number<gmp_float<35> >");                          \
    /* there should be at least one test with expression templates off: */            \
    test(number<gmp_float<35>, et_off>(), "number<gmp_float<35>, et_off>");           \
    mpf_float::default_precision(20);                                                 \
    test(mpf_float(), "number<gmp_float<0> > (20 digit precision)");                  \
    mpf_float::default_precision(35);                                                 \
    test(mpf_float(), "number<gmp_float<0> > (35 digit precision)");

typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::gmp_float<18>> test_type_1;
typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::gmp_float<30>> test_type_2;
typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::gmp_float<35>> test_type_3;
typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::gmp_float<35>,
                                             nil::crypto3::multiprecision::et_off>
    test_type_4;
typedef nil::crypto3::multiprecision::mpf_float test_type_5;

#else

#define MPF_TESTS
#define MPF_SMALL_TESTS

#endif

#if defined(TEST_MPFR_50)
#include <nil/crypto3/multiprecision/mpfr.hpp>

#define MPFR_TESTS                                                                                \
    test(number<mpfr_float_backend<18>>(), "number<mpfr_float_backend<18> >");                                       \
    test(number<mpfr_float_backend<30>>(), "number<mpfr_float_backend<30> >");                                       \
    test(number<mpfr_float_backend<35>>(), "number<mpfr_float_backend<35> >");                                       \
    /* Test variable precision at 2 different precisions - checks our ability to handle dynamic changes in precision \
     */                                                                                \
    mpfr_float::default_precision(20);                                                                               \
    test(mpfr_float(), "number<mpfr_float_backend<0> > (20-digit precision)");                                       \
    mpfr_float::default_precision(35);                                                                               \
    test(mpfr_float(), "number<mpfr_float_backend<0> > (35-digit precision)");

typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::mpfr_float_backend<18>> test_type_1;
typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::mpfr_float_backend<30>> test_type_2;
typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::mpfr_float_backend<35>> test_type_3;
typedef nil::crypto3::multiprecision::mpfr_float test_type_4;

#else

#define MPFR_TESTS

#endif

#ifdef TEST_BACKEND
#include <nil/crypto3/multiprecision/concepts/mp_number_archetypes.hpp>
#endif
#ifdef TEST_CPP_DEC_FLOAT
#include <nil/crypto3/multiprecision/cpp_dec_float.hpp>

#define CPP_DEC_FLOAT_TESTS                                            \
    test(number<cpp_dec_float<18>>(), "number<cpp_dec_float<18> >");   \
    test(number<cpp_dec_float<30>>(), "number<cpp_dec_float<30> >");   \
    test(number<cpp_dec_float<35, long long, std::allocator<char>>>(), \
         "number<cpp_dec_float<35, long long, std::allocator<char> > >");

typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<18>> test_type_1;
typedef nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<30>> test_type_2;
typedef nil::crypto3::multiprecision::number<
    nil::crypto3::multiprecision::cpp_dec_float<35, long long, std::allocator<char>>>
    test_type_3;

#else

#define CPP_DEC_FLOAT_TESTS

#endif

#ifdef TEST_CPP_BIN_FLOAT
#include <nil/crypto3/multiprecision/cpp_bin_float.hpp>
#include <nil/crypto3/multiprecision/debug_adaptor.hpp>

//#define CPP_BIN_FLOAT_TESTS test(number<debug_adaptor<cpp_bin_float_quad::backend_type>, et_off>(),
//"cpp_bin_float_quad");
#define CPP_BIN_FLOAT_TESTS test(cpp_bin_float_quad(), "cpp_bin_float_quad");

// typedef
// nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::debug_adaptor<nil::crypto3::multiprecision::cpp_bin_float_quad::backend_type>,
// nil::crypto3::multiprecision::et_off> test_type_1;
typedef nil::crypto3::multiprecision::cpp_bin_float_quad test_type_1;

#else

#define CPP_BIN_FLOAT_TESTS

#endif

#ifdef TEST_FLOAT128
#include <nil/crypto3/multiprecision/float128.hpp>

#define FLOAT128_TESTS test(float128(), "float128");

typedef nil::crypto3::multiprecision::float128 test_type_1;

#else

#define FLOAT128_TESTS

#endif

#ifndef BOOST_MATH_TEST_TYPE
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#endif

#define ALL_TESTS       \
    MPF_TESTS           \
    MPFR_TESTS          \
    CPP_DEC_FLOAT_TESTS \
    FLOAT128_TESTS      \
    CPP_BIN_FLOAT_TESTS

#define ALL_SMALL_TESTS \
    MPF_SMALL_TESTS     \
    MPFR_TESTS          \
    CPP_DEC_FLOAT_TESTS \
    FLOAT128_TESTS      \
    CPP_BIN_FLOAT_TESTS

#endif