2015-06-08 15:50:35 +00:00
|
|
|
/*
|
2015-06-19 23:26:29 +00:00
|
|
|
* Copyright (c) 2015, Cryptonomex, Incn
|
2015-06-08 15:50:35 +00:00
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
|
|
|
|
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
|
* are permitted until September 8, 2015, provided that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
|
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cctype>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <iterator>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <list>
|
|
|
|
|
|
|
|
|
|
#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/aes.hpp>
|
|
|
|
|
#include <fc/crypto/hex.hpp>
|
|
|
|
|
#include <fc/thread/mutex.hpp>
|
|
|
|
|
|
|
|
|
|
#include <graphene/app/api.hpp>
|
|
|
|
|
#include <graphene/chain/address.hpp>
|
|
|
|
|
#include <graphene/chain/asset_object.hpp>
|
|
|
|
|
#include <graphene/utilities/key_conversion.hpp>
|
|
|
|
|
#include <graphene/wallet/wallet.hpp>
|
2015-06-26 21:47:03 +00:00
|
|
|
#include <graphene/wallet/api_documentation.hpp>
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
#ifndef WIN32
|
2015-06-26 21:47:03 +00:00
|
|
|
# include <sys/types.h>
|
|
|
|
|
# include <sys/stat.h>
|
2015-06-08 15:50:35 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace graphene { namespace wallet {
|
|
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
|
|
// BLOCK TRX OP VOP
|
|
|
|
|
struct operation_printer
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
ostream& out;
|
|
|
|
|
const wallet_api_impl& wallet;
|
|
|
|
|
operation_result result;
|
|
|
|
|
|
|
|
|
|
void fee(const asset& a) const;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() )
|
|
|
|
|
: out(out),
|
|
|
|
|
wallet(wallet),
|
|
|
|
|
result(r)
|
|
|
|
|
{}
|
|
|
|
|
typedef void result_type;
|
|
|
|
|
template<typename T>
|
|
|
|
|
void operator()(const T& op)const
|
|
|
|
|
{
|
|
|
|
|
balance_accumulator acc;
|
|
|
|
|
op.get_balance_delta( acc, result );
|
|
|
|
|
string op_name = fc::get_typename<T>::name();
|
|
|
|
|
if( op_name.find_last_of(':') != string::npos )
|
|
|
|
|
op_name.erase(0, op_name.find_last_of(':')+1);
|
|
|
|
|
out << op_name <<" ";
|
|
|
|
|
out << "balance delta: " << fc::json::to_string(acc.balance) <<" ";
|
|
|
|
|
out << fc::json::to_string(op.fee_payer()) << " fee: " << fc::json::to_string(op.fee);
|
|
|
|
|
}
|
|
|
|
|
void operator()(const transfer_operation& op)const;
|
|
|
|
|
void operator()(const account_create_operation& op)const;
|
|
|
|
|
void operator()(const account_update_operation& op)const;
|
|
|
|
|
void operator()(const key_create_operation& op)const;
|
|
|
|
|
void operator()(const asset_create_operation& op)const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template< class T >
|
|
|
|
|
optional< T > maybe_id( const string& name_or_id )
|
|
|
|
|
{
|
|
|
|
|
if( std::isdigit( name_or_id.front() ) )
|
|
|
|
|
return fc::variant(name_or_id).as<T>();
|
|
|
|
|
return optional<T>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string address_to_shorthash( const address& addr )
|
|
|
|
|
{
|
|
|
|
|
uint32_t x = addr.addr._hash[0];
|
|
|
|
|
static const char hd[] = "0123456789abcdef";
|
|
|
|
|
string result;
|
|
|
|
|
|
|
|
|
|
result += hd[(x >> 0x1c) & 0x0f];
|
|
|
|
|
result += hd[(x >> 0x18) & 0x0f];
|
|
|
|
|
result += hd[(x >> 0x14) & 0x0f];
|
|
|
|
|
result += hd[(x >> 0x10) & 0x0f];
|
|
|
|
|
result += hd[(x >> 0x0c) & 0x0f];
|
|
|
|
|
result += hd[(x >> 0x08) & 0x0f];
|
|
|
|
|
result += hd[(x >> 0x04) & 0x0f];
|
|
|
|
|
result += hd[(x ) & 0x0f];
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fc::ecc::private_key derive_private_key(
|
|
|
|
|
const std::string& prefix_string,
|
|
|
|
|
int sequence_number
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
std::string sequence_string = std::to_string(sequence_number);
|
|
|
|
|
fc::sha512 h = fc::sha512::hash(prefix_string + " " + sequence_string);
|
|
|
|
|
fc::ecc::private_key derived_key = fc::ecc::private_key::regenerate(fc::sha256::hash(h));
|
|
|
|
|
return derived_key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string normalize_brain_key( string s )
|
|
|
|
|
{
|
|
|
|
|
size_t i = 0, n = s.length();
|
|
|
|
|
std::string result;
|
|
|
|
|
char c;
|
|
|
|
|
result.reserve( n );
|
|
|
|
|
|
|
|
|
|
bool preceded_by_whitespace = false;
|
|
|
|
|
bool non_empty = false;
|
|
|
|
|
while( i < n )
|
|
|
|
|
{
|
|
|
|
|
c = s[i++];
|
|
|
|
|
switch( c )
|
|
|
|
|
{
|
|
|
|
|
case ' ': case '\t': case '\r': case '\n': case '\v': case '\f':
|
|
|
|
|
preceded_by_whitespace = true;
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
case 'a': c = 'A'; break;
|
|
|
|
|
case 'b': c = 'B'; break;
|
|
|
|
|
case 'c': c = 'C'; break;
|
|
|
|
|
case 'd': c = 'D'; break;
|
|
|
|
|
case 'e': c = 'E'; break;
|
|
|
|
|
case 'f': c = 'F'; break;
|
|
|
|
|
case 'g': c = 'G'; break;
|
|
|
|
|
case 'h': c = 'H'; break;
|
|
|
|
|
case 'i': c = 'I'; break;
|
|
|
|
|
case 'j': c = 'J'; break;
|
|
|
|
|
case 'k': c = 'K'; break;
|
|
|
|
|
case 'l': c = 'L'; break;
|
|
|
|
|
case 'm': c = 'M'; break;
|
|
|
|
|
case 'n': c = 'N'; break;
|
|
|
|
|
case 'o': c = 'O'; break;
|
|
|
|
|
case 'p': c = 'P'; break;
|
|
|
|
|
case 'q': c = 'Q'; break;
|
|
|
|
|
case 'r': c = 'R'; break;
|
|
|
|
|
case 's': c = 'S'; break;
|
|
|
|
|
case 't': c = 'T'; break;
|
|
|
|
|
case 'u': c = 'U'; break;
|
|
|
|
|
case 'v': c = 'V'; break;
|
|
|
|
|
case 'w': c = 'W'; break;
|
|
|
|
|
case 'x': c = 'X'; break;
|
|
|
|
|
case 'y': c = 'Y'; break;
|
|
|
|
|
case 'z': c = 'Z'; break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( preceded_by_whitespace && non_empty )
|
|
|
|
|
result.push_back(' ');
|
|
|
|
|
result.push_back(c);
|
|
|
|
|
preceded_by_whitespace = false;
|
|
|
|
|
non_empty = true;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
class wallet_api_impl
|
|
|
|
|
{
|
2015-06-26 21:47:03 +00:00
|
|
|
public:
|
|
|
|
|
api_documentation method_documentation;
|
|
|
|
|
private:
|
2015-06-08 15:50:35 +00:00
|
|
|
void claim_registered_account(const account_object& account)
|
|
|
|
|
{
|
|
|
|
|
auto it = _wallet.pending_account_registrations.find( account.name );
|
|
|
|
|
FC_ASSERT( it != _wallet.pending_account_registrations.end() );
|
|
|
|
|
if( !import_key( account.name, it->second ) )
|
|
|
|
|
{
|
|
|
|
|
// somebody else beat our pending registration, there is
|
|
|
|
|
// nothing we can do except log it and move on
|
|
|
|
|
elog( "account ${name} registered by someone else first!",
|
|
|
|
|
("name", account.name) );
|
|
|
|
|
// might as well remove it from pending regs,
|
|
|
|
|
// because there is now no way this registration
|
|
|
|
|
// can become valid (even in the extremely rare
|
|
|
|
|
// possibility of migrating to a fork where the
|
|
|
|
|
// name is available, the user can always
|
|
|
|
|
// manually re-register)
|
|
|
|
|
}
|
|
|
|
|
_wallet.pending_account_registrations.erase( it );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fc::mutex _resync_mutex;
|
|
|
|
|
void resync()
|
|
|
|
|
{
|
|
|
|
|
_resync_mutex.lock();
|
|
|
|
|
// this method is used to update wallet_data annotations
|
|
|
|
|
// e.g. wallet has been restarted and was not notified
|
|
|
|
|
// of events while it was down
|
|
|
|
|
//
|
|
|
|
|
// everything that is done "incremental style" when a push
|
|
|
|
|
// notification is received, should also be done here
|
|
|
|
|
// "batch style" by querying the blockchain
|
|
|
|
|
|
|
|
|
|
if( _wallet.pending_account_registrations.size() > 0 )
|
|
|
|
|
{
|
|
|
|
|
std::vector<string> v_names;
|
|
|
|
|
v_names.reserve( _wallet.pending_account_registrations.size() );
|
|
|
|
|
std::transform(_wallet.pending_account_registrations.begin(), _wallet.pending_account_registrations.end(),
|
|
|
|
|
std::back_inserter(v_names), [](const std::pair<string, string>& p) {
|
|
|
|
|
return p.first;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
std::vector< fc::optional< graphene::chain::account_object >>
|
|
|
|
|
v_accounts = _remote_db->lookup_account_names( v_names );
|
|
|
|
|
|
|
|
|
|
for( fc::optional< graphene::chain::account_object > opt_account : v_accounts )
|
|
|
|
|
{
|
|
|
|
|
if( ! opt_account.valid() )
|
|
|
|
|
continue;
|
|
|
|
|
claim_registered_account(*opt_account);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_resync_mutex.unlock();
|
|
|
|
|
}
|
|
|
|
|
void enable_umask_protection()
|
|
|
|
|
{
|
|
|
|
|
#ifdef __unix__
|
|
|
|
|
_old_umask = umask( S_IRWXG | S_IRWXO );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
void disable_umask_protection()
|
|
|
|
|
{
|
|
|
|
|
#ifdef __unix__
|
|
|
|
|
umask( _old_umask );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
map<transaction_handle_type, signed_transaction> _builder_transactions;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
wallet_api& self;
|
|
|
|
|
wallet_api_impl( wallet_api& s, fc::api<login_api> rapi )
|
|
|
|
|
: self(s),
|
|
|
|
|
_remote_api(rapi),
|
|
|
|
|
_remote_db(rapi->database()),
|
|
|
|
|
_remote_net(rapi->network()),
|
|
|
|
|
_remote_hist(rapi->history())
|
|
|
|
|
{
|
|
|
|
|
_remote_db->subscribe_to_objects( [=]( const fc::variant& obj )
|
|
|
|
|
{
|
|
|
|
|
fc::async([this]{resync();}, "Resync after block");
|
|
|
|
|
}, {dynamic_global_property_id_type()} );
|
|
|
|
|
}
|
|
|
|
|
virtual ~wallet_api_impl()
|
|
|
|
|
{
|
2015-06-25 21:34:01 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_remote_db->cancel_all_subscriptions();
|
|
|
|
|
}
|
|
|
|
|
catch (const fc::exception& e)
|
|
|
|
|
{
|
|
|
|
|
// Right now the wallet_api has no way of knowing if the connection to the
|
|
|
|
|
// witness has already disconnected (via the witness node exiting first).
|
|
|
|
|
// If it has exited, cancel_all_subscriptsions() will throw and there's
|
|
|
|
|
// nothing we can do about it.
|
|
|
|
|
// dlog("Caught exception ${e} while canceling database subscriptions", ("e", e));
|
|
|
|
|
}
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void encrypt_keys()
|
|
|
|
|
{
|
|
|
|
|
plain_keys data;
|
|
|
|
|
data.keys = _keys;
|
|
|
|
|
data.checksum = _checksum;
|
|
|
|
|
auto plain_txt = fc::raw::pack(data);
|
|
|
|
|
_wallet.cipher_keys = fc::aes_encrypt( data.checksum, plain_txt );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool copy_wallet_file( string destination_filename )
|
|
|
|
|
{
|
|
|
|
|
fc::path src_path = get_wallet_filename();
|
|
|
|
|
if( !fc::exists( src_path ) )
|
|
|
|
|
return false;
|
|
|
|
|
fc::path dest_path = destination_filename + _wallet_filename_extension;
|
|
|
|
|
int suffix = 0;
|
|
|
|
|
while( fc::exists(dest_path) )
|
|
|
|
|
{
|
|
|
|
|
++suffix;
|
|
|
|
|
dest_path = destination_filename + "-" + to_string( suffix ) + _wallet_filename_extension;
|
|
|
|
|
}
|
|
|
|
|
wlog( "backing up wallet ${src} to ${dest}",
|
|
|
|
|
("src", src_path)
|
|
|
|
|
("dest", dest_path) );
|
|
|
|
|
|
|
|
|
|
fc::path dest_parent = fc::absolute(dest_path).parent_path();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
enable_umask_protection();
|
|
|
|
|
if( !fc::exists( dest_parent ) )
|
|
|
|
|
fc::create_directories( dest_parent );
|
|
|
|
|
fc::copy( src_path, dest_path );
|
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
|
{
|
|
|
|
|
disable_umask_protection();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool is_locked()const
|
|
|
|
|
{
|
|
|
|
|
return _checksum == fc::sha512();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
T get_object(object_id<T::space_id, T::type_id, T> id)const
|
|
|
|
|
{
|
|
|
|
|
auto ob = _remote_db->get_objects({id}).front();
|
|
|
|
|
return ob.template as<T>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
variant info() const
|
|
|
|
|
{
|
|
|
|
|
auto global_props = get_global_properties();
|
|
|
|
|
auto dynamic_props = get_dynamic_global_properties();
|
|
|
|
|
fc::mutable_variant_object result;
|
|
|
|
|
result["head_block_num"] = dynamic_props.head_block_number;
|
|
|
|
|
result["head_block_id"] = dynamic_props.head_block_id;
|
|
|
|
|
result["head_block_age"] = fc::get_approximate_relative_time_string(dynamic_props.time,
|
|
|
|
|
time_point_sec(time_point::now()),
|
|
|
|
|
" old");
|
|
|
|
|
result["next_maintenance_time"] = fc::get_approximate_relative_time_string(dynamic_props.next_maintenance_time);
|
|
|
|
|
result["chain_id"] = global_props.chain_id;
|
|
|
|
|
result["active_witnesses"] = global_props.active_witnesses;
|
|
|
|
|
result["active_delegates"] = global_props.active_delegates;
|
|
|
|
|
result["entropy"] = dynamic_props.random;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
global_property_object get_global_properties() const
|
|
|
|
|
{
|
|
|
|
|
return _remote_db->get_global_properties();
|
|
|
|
|
}
|
|
|
|
|
dynamic_global_property_object get_dynamic_global_properties() const
|
|
|
|
|
{
|
|
|
|
|
return _remote_db->get_dynamic_global_properties();
|
|
|
|
|
}
|
|
|
|
|
account_object get_account(account_id_type id) const
|
|
|
|
|
{
|
|
|
|
|
if( _wallet.my_accounts.get<by_id>().count(id) )
|
|
|
|
|
return *_wallet.my_accounts.get<by_id>().find(id);
|
|
|
|
|
auto rec = _remote_db->get_accounts({id}).front();
|
|
|
|
|
FC_ASSERT(rec);
|
|
|
|
|
return *rec;
|
|
|
|
|
}
|
|
|
|
|
account_object get_account(string account_name_or_id) const
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT( account_name_or_id.size() > 0 );
|
|
|
|
|
|
|
|
|
|
if( auto id = maybe_id<account_id_type>(account_name_or_id) )
|
|
|
|
|
{
|
|
|
|
|
// It's an ID
|
|
|
|
|
return get_account(*id);
|
|
|
|
|
} else {
|
|
|
|
|
// It's a name
|
|
|
|
|
if( _wallet.my_accounts.get<by_name>().count(account_name_or_id) )
|
|
|
|
|
return *_wallet.my_accounts.get<by_name>().find(account_name_or_id);
|
|
|
|
|
auto rec = _remote_db->lookup_account_names({account_name_or_id}).front();
|
|
|
|
|
FC_ASSERT( rec && rec->name == account_name_or_id );
|
|
|
|
|
return *rec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
account_id_type get_account_id(string account_name_or_id) const
|
|
|
|
|
{
|
|
|
|
|
return get_account(account_name_or_id).get_id();
|
|
|
|
|
}
|
|
|
|
|
optional<asset_object> find_asset(asset_id_type id)const
|
|
|
|
|
{
|
|
|
|
|
auto rec = _remote_db->get_assets({id}).front();
|
|
|
|
|
if( rec )
|
|
|
|
|
_asset_cache[id] = *rec;
|
|
|
|
|
return rec;
|
|
|
|
|
}
|
|
|
|
|
optional<asset_object> find_asset(string asset_symbol_or_id)const
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT( asset_symbol_or_id.size() > 0 );
|
|
|
|
|
|
|
|
|
|
if( auto id = maybe_id<asset_id_type>(asset_symbol_or_id) )
|
|
|
|
|
{
|
|
|
|
|
// It's an ID
|
|
|
|
|
return find_asset(*id);
|
|
|
|
|
} else {
|
|
|
|
|
// It's a symbol
|
|
|
|
|
auto rec = _remote_db->lookup_asset_symbols({asset_symbol_or_id}).front();
|
|
|
|
|
if( rec )
|
|
|
|
|
{
|
|
|
|
|
if( rec->symbol != asset_symbol_or_id )
|
|
|
|
|
return optional<asset_object>();
|
|
|
|
|
|
|
|
|
|
_asset_cache[rec->get_id()] = *rec;
|
|
|
|
|
}
|
|
|
|
|
return rec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
asset_object get_asset(asset_id_type id)const
|
|
|
|
|
{
|
|
|
|
|
auto opt = find_asset(id);
|
|
|
|
|
FC_ASSERT(opt);
|
|
|
|
|
return *opt;
|
|
|
|
|
}
|
|
|
|
|
asset_object get_asset(string asset_symbol_or_id)const
|
|
|
|
|
{
|
|
|
|
|
auto opt = find_asset(asset_symbol_or_id);
|
|
|
|
|
FC_ASSERT(opt);
|
|
|
|
|
return *opt;
|
|
|
|
|
}
|
|
|
|
|
asset_id_type get_asset_id(string asset_symbol_or_id) const
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT( asset_symbol_or_id.size() > 0 );
|
|
|
|
|
vector<optional<asset_object>> opt_asset;
|
|
|
|
|
if( std::isdigit( asset_symbol_or_id.front() ) )
|
|
|
|
|
return fc::variant(asset_symbol_or_id).as<asset_id_type>();
|
|
|
|
|
opt_asset = _remote_db->lookup_asset_symbols( {asset_symbol_or_id} );
|
|
|
|
|
FC_ASSERT( (opt_asset.size() > 0) && (opt_asset[0].valid()) );
|
|
|
|
|
return opt_asset[0]->id;
|
|
|
|
|
}
|
|
|
|
|
string get_wallet_filename() const
|
|
|
|
|
{
|
|
|
|
|
return _wallet_filename;
|
|
|
|
|
}
|
|
|
|
|
fc::ecc::private_key get_private_key(key_id_type id)const
|
|
|
|
|
{
|
|
|
|
|
auto it = _keys.find(id);
|
|
|
|
|
FC_ASSERT( it != _keys.end() );
|
|
|
|
|
|
|
|
|
|
fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );
|
|
|
|
|
FC_ASSERT( privkey );
|
|
|
|
|
return *privkey;
|
|
|
|
|
}
|
|
|
|
|
fc::ecc::public_key get_public_key(key_id_type id)const
|
|
|
|
|
{
|
|
|
|
|
vector<optional<key_object>> keys = _remote_db->get_keys( {id} );
|
|
|
|
|
FC_ASSERT( keys.size() == 1 );
|
|
|
|
|
FC_ASSERT( keys[0].valid() );
|
|
|
|
|
return keys[0]->key();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool import_key(string account_name_or_id, string wif_key)
|
|
|
|
|
{
|
|
|
|
|
auto opt_priv_key = wif_to_key(wif_key);
|
|
|
|
|
FC_ASSERT( opt_priv_key.valid() );
|
|
|
|
|
graphene::chain::address wif_key_address = graphene::chain::address(
|
|
|
|
|
opt_priv_key->get_public_key() );
|
|
|
|
|
|
|
|
|
|
auto acnt = get_account( account_name_or_id );
|
|
|
|
|
|
|
|
|
|
flat_set<key_id_type> keys;
|
|
|
|
|
for( auto item : acnt.active.auths )
|
|
|
|
|
{
|
|
|
|
|
if( item.first.type() == key_object_type )
|
|
|
|
|
keys.insert( item.first );
|
|
|
|
|
}
|
|
|
|
|
for( auto item : acnt.owner.auths )
|
|
|
|
|
{
|
|
|
|
|
if( item.first.type() == key_object_type )
|
|
|
|
|
keys.insert( item.first );
|
|
|
|
|
}
|
|
|
|
|
auto opt_keys = _remote_db->get_keys( vector<key_id_type>(keys.begin(),keys.end()) );
|
|
|
|
|
for( const fc::optional<key_object>& opt_key : opt_keys )
|
|
|
|
|
{
|
|
|
|
|
// the requested key ID's should all exist because they are
|
|
|
|
|
// keys for an account
|
|
|
|
|
FC_ASSERT( opt_key.valid() );
|
|
|
|
|
// we do this check by address because key objects on the
|
|
|
|
|
// blockchain may not contain a key (i.e. are simply an address)
|
|
|
|
|
if( opt_key->key_address() == wif_key_address )
|
|
|
|
|
{
|
|
|
|
|
_keys[ opt_key->id ] = wif_key;
|
|
|
|
|
if( _wallet.update_account(acnt) )
|
|
|
|
|
_remote_db->subscribe_to_objects([this](const fc::variant& v) {
|
|
|
|
|
_wallet.update_account(v.as<account_object>());
|
|
|
|
|
}, {acnt.id});
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ilog( "key not for account ${name}", ("name",account_name_or_id) );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bool load_wallet_file(string wallet_filename = "")
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// TODO: Merge imported wallet with existing wallet,
|
|
|
|
|
// instead of replacing it
|
|
|
|
|
//
|
|
|
|
|
if( wallet_filename == "" )
|
|
|
|
|
wallet_filename = _wallet_filename;
|
|
|
|
|
|
|
|
|
|
if( ! fc::exists( wallet_filename ) )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if( !_wallet.my_accounts.empty() )
|
|
|
|
|
_remote_db->unsubscribe_from_objects(_wallet.my_account_ids());
|
|
|
|
|
_wallet = fc::json::from_file( wallet_filename ).as< wallet_data >();
|
|
|
|
|
if( !_wallet.my_accounts.empty() )
|
|
|
|
|
_remote_db->subscribe_to_objects([this](const fc::variant& v) {
|
|
|
|
|
_wallet.update_account(v.as<account_object>());
|
|
|
|
|
}, _wallet.my_account_ids());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
void save_wallet_file(string wallet_filename = "")
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Serialize in memory, then save to disk
|
|
|
|
|
//
|
|
|
|
|
// This approach lessens the risk of a partially written wallet
|
|
|
|
|
// if exceptions are thrown in serialization
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
encrypt_keys();
|
|
|
|
|
|
|
|
|
|
if( wallet_filename == "" )
|
|
|
|
|
wallet_filename = _wallet_filename;
|
|
|
|
|
|
|
|
|
|
wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) );
|
|
|
|
|
|
|
|
|
|
string data = fc::json::to_pretty_string( _wallet );
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
enable_umask_protection();
|
|
|
|
|
//
|
|
|
|
|
// Parentheses on the following declaration fails to compile,
|
|
|
|
|
// due to the Most Vexing Parse. Thanks, C++
|
|
|
|
|
//
|
|
|
|
|
// http://en.wikipedia.org/wiki/Most_vexing_parse
|
|
|
|
|
//
|
|
|
|
|
fc::ofstream outfile{ fc::path( wallet_filename ) };
|
|
|
|
|
outfile.write( data.c_str(), data.length() );
|
|
|
|
|
outfile.flush();
|
|
|
|
|
outfile.close();
|
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
|
{
|
|
|
|
|
disable_umask_protection();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transaction_handle_type begin_builder_transaction()
|
|
|
|
|
{
|
|
|
|
|
int trx_handle = _builder_transactions.empty()? 0
|
|
|
|
|
: (--_builder_transactions.end())->first + 1;
|
|
|
|
|
_builder_transactions[trx_handle];
|
|
|
|
|
return trx_handle;
|
|
|
|
|
}
|
|
|
|
|
void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(_builder_transactions.count(transaction_handle));
|
|
|
|
|
_builder_transactions[transaction_handle].operations.emplace_back(op);
|
|
|
|
|
}
|
|
|
|
|
void replace_operation_in_builder_transaction(transaction_handle_type handle,
|
|
|
|
|
int operation_index,
|
|
|
|
|
const operation& new_op)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(_builder_transactions.count(handle));
|
|
|
|
|
signed_transaction& trx = _builder_transactions[handle];
|
|
|
|
|
FC_ASSERT(operation_index >= 0 && operation_index < trx.operations.size());
|
|
|
|
|
trx.operations[operation_index] = new_op;
|
|
|
|
|
}
|
|
|
|
|
asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(_builder_transactions.count(handle));
|
|
|
|
|
|
|
|
|
|
auto fee_asset_obj = get_asset(fee_asset);
|
|
|
|
|
asset total_fee = fee_asset_obj.amount(0);
|
|
|
|
|
if( fee_asset_obj.get_id() != asset_id_type() )
|
|
|
|
|
{
|
|
|
|
|
_builder_transactions[handle].visit(
|
|
|
|
|
operation_set_fee(_remote_db->get_global_properties().parameters.current_fees,
|
|
|
|
|
fee_asset_obj.options.core_exchange_rate,
|
|
|
|
|
&total_fee.amount)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
FC_ASSERT((total_fee * fee_asset_obj.options.core_exchange_rate).amount <=
|
|
|
|
|
get_object<asset_dynamic_data_object>(fee_asset_obj.dynamic_asset_data_id).fee_pool,
|
|
|
|
|
"Cannot pay fees in ${asset}, as this asset's fee pool is insufficiently funded.",
|
|
|
|
|
("asset", fee_asset_obj.symbol));
|
|
|
|
|
} else {
|
|
|
|
|
_builder_transactions[handle].visit(
|
|
|
|
|
operation_set_fee(_remote_db->get_global_properties().parameters.current_fees,
|
|
|
|
|
price::unit_price(),
|
|
|
|
|
&total_fee.amount)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total_fee;
|
|
|
|
|
}
|
|
|
|
|
transaction preview_builder_transaction(transaction_handle_type handle)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(_builder_transactions.count(handle));
|
|
|
|
|
return _builder_transactions[handle];
|
|
|
|
|
}
|
|
|
|
|
signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(_builder_transactions.count(transaction_handle));
|
|
|
|
|
|
|
|
|
|
return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast);
|
|
|
|
|
}
|
|
|
|
|
signed_transaction propose_builder_transaction(transaction_handle_type handle,
|
|
|
|
|
time_point_sec expiration = time_point::now() + fc::minutes(1),
|
|
|
|
|
uint32_t review_period_seconds = 0, bool broadcast = true)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(_builder_transactions.count(handle));
|
|
|
|
|
proposal_create_operation op;
|
|
|
|
|
op.expiration_time = expiration;
|
|
|
|
|
signed_transaction& trx = _builder_transactions[handle];
|
|
|
|
|
std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops),
|
|
|
|
|
[](const operation& op) -> op_wrapper { return op; });
|
|
|
|
|
if( review_period_seconds )
|
|
|
|
|
op.review_period_seconds = review_period_seconds;
|
|
|
|
|
op.fee = op.calculate_fee(_remote_db->get_global_properties().parameters.current_fees);
|
|
|
|
|
trx.operations = {op};
|
|
|
|
|
|
|
|
|
|
return trx = sign_transaction(trx, broadcast);
|
|
|
|
|
}
|
|
|
|
|
void remove_builder_transaction(transaction_handle_type handle)
|
|
|
|
|
{
|
|
|
|
|
_builder_transactions.erase(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction register_account(string name,
|
|
|
|
|
public_key_type owner,
|
|
|
|
|
public_key_type active,
|
|
|
|
|
string registrar_account,
|
|
|
|
|
string referrer_account,
|
|
|
|
|
uint8_t referrer_percent,
|
|
|
|
|
bool broadcast = false)
|
|
|
|
|
{ try {
|
|
|
|
|
FC_ASSERT( !self.is_locked() );
|
|
|
|
|
FC_ASSERT( is_valid_name(name) );
|
|
|
|
|
account_create_operation account_create_op;
|
|
|
|
|
|
|
|
|
|
// TODO: process when pay_from_account is ID
|
|
|
|
|
|
|
|
|
|
account_object registrar_account_object =
|
|
|
|
|
this->get_account( registrar_account );
|
2015-06-09 20:46:00 +00:00
|
|
|
FC_ASSERT( registrar_account_object.is_lifetime_member() );
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
account_id_type registrar_account_id = registrar_account_object.id;
|
|
|
|
|
|
|
|
|
|
account_object referrer_account_object =
|
|
|
|
|
this->get_account( referrer_account );
|
|
|
|
|
account_create_op.referrer = referrer_account_object.id;
|
|
|
|
|
account_create_op.referrer_percent = referrer_percent;
|
|
|
|
|
|
|
|
|
|
// get pay_from_account_id
|
|
|
|
|
key_create_operation owner_key_create_op;
|
|
|
|
|
owner_key_create_op.fee_paying_account = registrar_account_id;
|
|
|
|
|
owner_key_create_op.key_data = owner;
|
|
|
|
|
|
|
|
|
|
key_create_operation active_key_create_op;
|
|
|
|
|
active_key_create_op.fee_paying_account = registrar_account_id;
|
|
|
|
|
active_key_create_op.key_data = active;
|
|
|
|
|
|
|
|
|
|
// key_create_op.calculate_fee(db.current_fee_schedule());
|
|
|
|
|
|
|
|
|
|
// TODO: Check if keys already exist!!!
|
|
|
|
|
|
|
|
|
|
relative_key_id_type owner_rkid(0);
|
|
|
|
|
relative_key_id_type active_rkid(1);
|
|
|
|
|
|
|
|
|
|
account_create_op.registrar = registrar_account_id;
|
|
|
|
|
account_create_op.name = name;
|
|
|
|
|
account_create_op.owner = authority(1, owner_rkid, 1);
|
|
|
|
|
account_create_op.active = authority(1, active_rkid, 1);
|
2015-06-16 18:42:02 +00:00
|
|
|
account_create_op.options.memo_key = active_rkid;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
|
|
|
|
|
tx.operations.push_back( owner_key_create_op );
|
|
|
|
|
tx.operations.push_back( active_key_create_op );
|
|
|
|
|
tx.operations.push_back( account_create_op );
|
|
|
|
|
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
|
|
|
|
|
vector<key_id_type> paying_keys = registrar_account_object.active.get_keys();
|
|
|
|
|
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
for( key_id_type& key : paying_keys )
|
|
|
|
|
{
|
|
|
|
|
auto it = _keys.find(key);
|
|
|
|
|
if( it != _keys.end() )
|
|
|
|
|
{
|
|
|
|
|
fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );
|
|
|
|
|
if( !privkey.valid() )
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT( false, "Malformed private key in _keys" );
|
|
|
|
|
}
|
|
|
|
|
tx.sign( key, *privkey );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( broadcast )
|
|
|
|
|
_remote_net->broadcast_transaction( tx );
|
|
|
|
|
return tx;
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)(referrer_account)(referrer_percent)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction upgrade_account(string name, bool broadcast)
|
|
|
|
|
{ try {
|
|
|
|
|
FC_ASSERT( !self.is_locked() );
|
|
|
|
|
account_object account_obj = get_account(name);
|
2015-06-09 20:46:00 +00:00
|
|
|
FC_ASSERT( !account_obj.is_lifetime_member() );
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
signed_transaction tx;
|
2015-06-10 18:17:13 +00:00
|
|
|
account_upgrade_operation op;
|
|
|
|
|
op.account_to_upgrade = account_obj.get_id();
|
|
|
|
|
op.upgrade_to_lifetime_member = true;
|
|
|
|
|
tx.operations = {op};
|
2015-06-08 15:50:35 +00:00
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (name) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction create_account_with_private_key(fc::ecc::private_key owner_privkey,
|
|
|
|
|
string account_name,
|
|
|
|
|
string registrar_account,
|
|
|
|
|
string referrer_account,
|
|
|
|
|
bool broadcast = false,
|
|
|
|
|
bool save_wallet = true)
|
|
|
|
|
{ try {
|
|
|
|
|
fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), 0);
|
|
|
|
|
|
|
|
|
|
graphene::chain::public_key_type owner_pubkey = owner_privkey.get_public_key();
|
|
|
|
|
graphene::chain::public_key_type active_pubkey = active_privkey.get_public_key();
|
|
|
|
|
|
|
|
|
|
account_create_operation account_create_op;
|
|
|
|
|
|
|
|
|
|
// TODO: process when pay_from_account is ID
|
|
|
|
|
|
|
|
|
|
account_object registrar_account_object =
|
|
|
|
|
this->get_account( registrar_account );
|
|
|
|
|
|
|
|
|
|
account_id_type registrar_account_id = registrar_account_object.id;
|
|
|
|
|
|
|
|
|
|
account_object referrer_account_object =
|
|
|
|
|
this->get_account( referrer_account );
|
|
|
|
|
account_create_op.referrer = referrer_account_object.id;
|
2015-06-09 20:46:00 +00:00
|
|
|
account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
// get pay_from_account_id
|
|
|
|
|
key_create_operation owner_key_create_op;
|
|
|
|
|
owner_key_create_op.fee_paying_account = registrar_account_id;
|
|
|
|
|
owner_key_create_op.key_data = owner_pubkey;
|
|
|
|
|
|
|
|
|
|
key_create_operation active_key_create_op;
|
|
|
|
|
active_key_create_op.fee_paying_account = registrar_account_id;
|
|
|
|
|
active_key_create_op.key_data = active_pubkey;
|
|
|
|
|
|
|
|
|
|
// key_create_op.calculate_fee(db.current_fee_schedule());
|
|
|
|
|
|
|
|
|
|
// TODO: Check if keys already exist!!!
|
|
|
|
|
|
|
|
|
|
relative_key_id_type owner_rkid(0);
|
|
|
|
|
relative_key_id_type active_rkid(1);
|
|
|
|
|
|
|
|
|
|
account_create_op.registrar = registrar_account_id;
|
|
|
|
|
account_create_op.name = account_name;
|
|
|
|
|
account_create_op.owner = authority(1, owner_rkid, 1);
|
|
|
|
|
account_create_op.active = authority(1, active_rkid, 1);
|
2015-06-16 18:42:02 +00:00
|
|
|
account_create_op.options.memo_key = active_rkid;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
// current_fee_schedule()
|
|
|
|
|
// find_account(pay_from_account)
|
|
|
|
|
|
|
|
|
|
// account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule());
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
|
|
|
|
|
tx.operations.push_back( owner_key_create_op );
|
|
|
|
|
tx.operations.push_back( active_key_create_op );
|
|
|
|
|
tx.operations.push_back( account_create_op );
|
|
|
|
|
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
|
|
|
|
|
vector<key_id_type> paying_keys = registrar_account_object.active.get_keys();
|
|
|
|
|
|
|
|
|
|
tx.set_expiration(get_dynamic_global_properties().head_block_id);
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
for( key_id_type& key : paying_keys )
|
|
|
|
|
{
|
|
|
|
|
auto it = _keys.find(key);
|
|
|
|
|
if( it != _keys.end() )
|
|
|
|
|
{
|
|
|
|
|
fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );
|
|
|
|
|
FC_ASSERT( privkey.valid(), "Malformed private key in _keys" );
|
|
|
|
|
tx.sign( key, *privkey );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we do not insert owner_privkey here because
|
|
|
|
|
// it is intended to only be used for key recovery
|
|
|
|
|
_wallet.pending_account_registrations[ account_name ] = key_to_wif( active_privkey );
|
|
|
|
|
if( save_wallet )
|
|
|
|
|
save_wallet_file();
|
|
|
|
|
if( broadcast )
|
|
|
|
|
_remote_net->broadcast_transaction( tx );
|
|
|
|
|
return tx;
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction create_account_with_brain_key(string brain_key,
|
|
|
|
|
string account_name,
|
|
|
|
|
string registrar_account,
|
|
|
|
|
string referrer_account,
|
|
|
|
|
bool broadcast = false)
|
|
|
|
|
{ try {
|
|
|
|
|
FC_ASSERT( !self.is_locked() );
|
|
|
|
|
string normalized_brain_key = normalize_brain_key( brain_key );
|
|
|
|
|
// TODO: scan blockchain for accounts that exist with same brain key
|
|
|
|
|
fc::ecc::private_key owner_privkey = derive_private_key( normalized_brain_key, 0 );
|
|
|
|
|
return create_account_with_private_key(owner_privkey, account_name, registrar_account, referrer_account, broadcast);
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account) ) }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
signed_transaction create_asset(string issuer,
|
|
|
|
|
string symbol,
|
|
|
|
|
uint8_t precision,
|
|
|
|
|
asset_object::asset_options common,
|
|
|
|
|
fc::optional<asset_object::bitasset_options> bitasset_opts,
|
|
|
|
|
bool broadcast = false)
|
|
|
|
|
{ try {
|
|
|
|
|
account_object issuer_account = get_account( issuer );
|
|
|
|
|
FC_ASSERT(!find_asset(symbol).valid(), "Asset with that symbol already exists!");
|
|
|
|
|
|
|
|
|
|
asset_create_operation create_op;
|
|
|
|
|
create_op.issuer = issuer_account.id;
|
|
|
|
|
create_op.symbol = symbol;
|
|
|
|
|
create_op.precision = precision;
|
|
|
|
|
create_op.common_options = common;
|
|
|
|
|
create_op.bitasset_options = bitasset_opts;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( create_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) }
|
|
|
|
|
|
2015-06-19 23:26:29 +00:00
|
|
|
signed_transaction update_asset(string symbol,
|
|
|
|
|
optional<string> new_issuer,
|
|
|
|
|
asset_object::asset_options new_options,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
optional<asset_object> asset_to_update = find_asset(symbol);
|
|
|
|
|
if (!asset_to_update)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
optional<account_id_type> new_issuer_account_id;
|
|
|
|
|
if (new_issuer)
|
|
|
|
|
{
|
|
|
|
|
account_object new_issuer_account = get_account(*new_issuer);
|
|
|
|
|
new_issuer_account_id = new_issuer_account.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asset_update_operation update_op;
|
|
|
|
|
update_op.issuer = asset_to_update->issuer;
|
|
|
|
|
update_op.asset_to_update = asset_to_update->id;
|
|
|
|
|
update_op.new_issuer = new_issuer_account_id;
|
|
|
|
|
update_op.new_options = new_options;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( update_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (symbol)(new_issuer)(new_options)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction update_bitasset(string symbol,
|
|
|
|
|
asset_object::bitasset_options new_options,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
optional<asset_object> asset_to_update = find_asset(symbol);
|
|
|
|
|
if (!asset_to_update)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
|
|
|
|
|
asset_update_bitasset_operation update_op;
|
|
|
|
|
update_op.issuer = asset_to_update->issuer;
|
|
|
|
|
update_op.asset_to_update = asset_to_update->id;
|
|
|
|
|
update_op.new_options = new_options;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( update_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (symbol)(new_options)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction update_asset_feed_producers(string symbol,
|
|
|
|
|
flat_set<string> new_feed_producers,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
optional<asset_object> asset_to_update = find_asset(symbol);
|
|
|
|
|
if (!asset_to_update)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
|
|
|
|
|
asset_update_feed_producers_operation update_op;
|
|
|
|
|
update_op.issuer = asset_to_update->issuer;
|
|
|
|
|
update_op.asset_to_update = asset_to_update->id;
|
|
|
|
|
update_op.new_feed_producers.reserve(new_feed_producers.size());
|
|
|
|
|
std::transform(new_feed_producers.begin(), new_feed_producers.end(),
|
|
|
|
|
std::inserter(update_op.new_feed_producers, update_op.new_feed_producers.end()),
|
|
|
|
|
[this](const std::string& account_name_or_id){ return get_account_id(account_name_or_id); });
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( update_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (symbol)(new_feed_producers)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction publish_asset_feed(string publishing_account,
|
|
|
|
|
string symbol,
|
|
|
|
|
price_feed feed,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
optional<asset_object> asset_to_update = find_asset(symbol);
|
|
|
|
|
if (!asset_to_update)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
|
|
|
|
|
asset_publish_feed_operation publish_op;
|
|
|
|
|
publish_op.publisher = get_account_id(publishing_account);
|
|
|
|
|
publish_op.asset_id = asset_to_update->id;
|
|
|
|
|
publish_op.feed = feed;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( publish_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction fund_asset_fee_pool(string from,
|
|
|
|
|
string symbol,
|
|
|
|
|
string amount,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
account_object from_account = get_account(from);
|
|
|
|
|
optional<asset_object> asset_to_fund = find_asset(symbol);
|
|
|
|
|
if (!asset_to_fund)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
asset_object core_asset = get_asset(asset_id_type());
|
|
|
|
|
|
|
|
|
|
asset_fund_fee_pool_operation fund_op;
|
|
|
|
|
fund_op.from_account = from_account.id;
|
|
|
|
|
fund_op.asset_id = asset_to_fund->id;
|
|
|
|
|
fund_op.amount = core_asset.amount_from_string(amount).amount;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( fund_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction burn_asset(string from,
|
|
|
|
|
string amount,
|
|
|
|
|
string symbol,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
account_object from_account = get_account(from);
|
|
|
|
|
optional<asset_object> asset_to_burn = find_asset(symbol);
|
|
|
|
|
if (!asset_to_burn)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
|
|
|
|
|
asset_burn_operation burn_op;
|
|
|
|
|
burn_op.payer = from_account.id;
|
|
|
|
|
burn_op.amount_to_burn = asset_to_burn->amount_from_string(amount);
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( burn_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction global_settle_asset(string symbol,
|
|
|
|
|
price settle_price,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
optional<asset_object> asset_to_settle = find_asset(symbol);
|
|
|
|
|
if (!asset_to_settle)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
|
|
|
|
|
asset_global_settle_operation settle_op;
|
|
|
|
|
settle_op.issuer = asset_to_settle->issuer;
|
|
|
|
|
settle_op.asset_to_settle = asset_to_settle->id;
|
|
|
|
|
settle_op.settle_price = settle_price;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( settle_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (symbol)(settle_price)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction settle_asset(string account_to_settle,
|
|
|
|
|
string amount_to_settle,
|
|
|
|
|
string symbol,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
optional<asset_object> asset_to_settle = find_asset(symbol);
|
|
|
|
|
if (!asset_to_settle)
|
|
|
|
|
FC_THROW("No asset with that symbol exists!");
|
|
|
|
|
|
|
|
|
|
asset_settle_operation settle_op;
|
|
|
|
|
settle_op.account = get_account_id(account_to_settle);
|
|
|
|
|
settle_op.amount = asset_to_settle->amount_from_string(amount_to_settle);
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( settle_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) }
|
|
|
|
|
|
2015-06-20 22:33:58 +00:00
|
|
|
signed_transaction whitelist_account(string authorizing_account,
|
|
|
|
|
string account_to_list,
|
|
|
|
|
account_whitelist_operation::account_listing new_listing_status,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
account_whitelist_operation whitelist_op;
|
|
|
|
|
whitelist_op.authorizing_account = get_account_id(authorizing_account);
|
|
|
|
|
whitelist_op.account_to_list = get_account_id(account_to_list);
|
|
|
|
|
whitelist_op.new_listing = new_listing_status;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( whitelist_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) }
|
2015-06-22 22:02:51 +00:00
|
|
|
|
2015-06-22 23:37:18 +00:00
|
|
|
signed_transaction create_delegate(string owner_account,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
|
|
|
|
|
delegate_create_operation delegate_create_op;
|
|
|
|
|
delegate_create_op.delegate_account = get_account_id(owner_account);
|
|
|
|
|
if (_remote_db->get_delegate_by_account(delegate_create_op.delegate_account))
|
|
|
|
|
FC_THROW("Account ${owner_account} is already a delegate", ("owner_account", owner_account));
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( delegate_create_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction create_witness(string owner_account,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
|
|
|
|
|
witness_create_operation witness_create_op;
|
|
|
|
|
witness_create_op.witness_account = get_account_id(owner_account);
|
|
|
|
|
if (_remote_db->get_witness_by_account(witness_create_op.witness_account))
|
|
|
|
|
FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account));
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( witness_create_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }
|
|
|
|
|
|
2015-06-22 22:02:51 +00:00
|
|
|
signed_transaction vote_for_delegate(string voting_account,
|
|
|
|
|
string delegate,
|
|
|
|
|
bool approve,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
account_object voting_account_object = get_account(voting_account);
|
|
|
|
|
account_id_type delegate_owner_account_id = get_account_id(delegate);
|
|
|
|
|
fc::optional<delegate_object> delegate_obj = _remote_db->get_delegate_by_account(delegate_owner_account_id);
|
|
|
|
|
if (!delegate_obj)
|
|
|
|
|
FC_THROW("Account ${delegate} is not registered as a delegate", ("delegate", delegate));
|
|
|
|
|
if (approve)
|
|
|
|
|
{
|
|
|
|
|
auto insert_result = voting_account_object.options.votes.insert(delegate_obj->vote_id);
|
|
|
|
|
if (!insert_result.second)
|
|
|
|
|
FC_THROW("Account ${account} was already voting for delegate ${delegate}", ("account", voting_account)("delegate", delegate));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned votes_removed = voting_account_object.options.votes.erase(delegate_obj->vote_id);
|
|
|
|
|
if (!votes_removed)
|
|
|
|
|
FC_THROW("Account ${account} is already not voting for delegate ${delegate}", ("account", voting_account)("delegate", delegate));
|
|
|
|
|
}
|
|
|
|
|
account_update_operation account_update_op;
|
|
|
|
|
account_update_op.account = voting_account_object.id;
|
|
|
|
|
account_update_op.new_options = voting_account_object.options;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( account_update_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (voting_account)(delegate)(approve)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction vote_for_witness(string voting_account,
|
|
|
|
|
string witness,
|
|
|
|
|
bool approve,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
account_object voting_account_object = get_account(voting_account);
|
|
|
|
|
account_id_type witness_owner_account_id = get_account_id(witness);
|
|
|
|
|
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
|
|
|
|
|
if (!witness_obj)
|
|
|
|
|
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
|
|
|
|
|
if (approve)
|
|
|
|
|
{
|
|
|
|
|
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
|
|
|
|
|
if (!insert_result.second)
|
|
|
|
|
FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id);
|
|
|
|
|
if (!votes_removed)
|
|
|
|
|
FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness));
|
|
|
|
|
}
|
|
|
|
|
account_update_operation account_update_op;
|
|
|
|
|
account_update_op.account = voting_account_object.id;
|
|
|
|
|
account_update_op.new_options = voting_account_object.options;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( account_update_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
2015-06-20 22:33:58 +00:00
|
|
|
|
2015-06-22 22:02:51 +00:00
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) }
|
|
|
|
|
|
2015-06-22 23:37:18 +00:00
|
|
|
signed_transaction set_voting_proxy(string account_to_modify,
|
|
|
|
|
optional<string> voting_account,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{ try {
|
|
|
|
|
account_object account_object_to_modify = get_account(account_to_modify);
|
|
|
|
|
if (voting_account)
|
|
|
|
|
{
|
|
|
|
|
account_id_type new_voting_account_id = get_account_id(*voting_account);
|
|
|
|
|
if (account_object_to_modify.options.voting_account == new_voting_account_id)
|
|
|
|
|
FC_THROW("Voting proxy for ${account} is already set to ${voter}", ("account", account_to_modify)("voter", *voting_account));
|
|
|
|
|
account_object_to_modify.options.voting_account = new_voting_account_id;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (account_object_to_modify.options.voting_account == account_id_type())
|
|
|
|
|
FC_THROW("Account ${account} is already voting for itself", ("account", account_to_modify));
|
|
|
|
|
account_object_to_modify.options.voting_account = account_id_type();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
account_update_operation account_update_op;
|
|
|
|
|
account_update_op.account = account_object_to_modify.id;
|
|
|
|
|
account_update_op.new_options = account_object_to_modify.options;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back( account_update_op );
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (account_to_modify)(voting_account)(broadcast) ) }
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false)
|
|
|
|
|
{
|
|
|
|
|
flat_set<account_id_type> req_active_approvals;
|
|
|
|
|
flat_set<account_id_type> req_owner_approvals;
|
|
|
|
|
|
|
|
|
|
tx.visit( operation_get_required_auths( req_active_approvals, req_owner_approvals ) );
|
|
|
|
|
|
|
|
|
|
// TODO: Only sign if the wallet considers ACCOUNTS to be owned.
|
|
|
|
|
// Currently the wallet only owns KEYS and will happily sign
|
|
|
|
|
// for any account...
|
|
|
|
|
|
|
|
|
|
// std::merge lets us de-duplicate account_id's that occur in both
|
|
|
|
|
// sets, and dump them into a vector (as required by remote_db api)
|
|
|
|
|
// at the same time
|
|
|
|
|
vector< account_id_type > v_approving_account_ids;
|
|
|
|
|
std::merge(req_active_approvals.begin(), req_active_approvals.end(),
|
|
|
|
|
req_owner_approvals.begin() , req_owner_approvals.end(),
|
|
|
|
|
std::back_inserter(v_approving_account_ids));
|
|
|
|
|
|
|
|
|
|
vector< optional<account_object> > approving_account_objects =
|
|
|
|
|
_remote_db->get_accounts( v_approving_account_ids );
|
|
|
|
|
|
|
|
|
|
FC_ASSERT( approving_account_objects.size() == v_approving_account_ids.size() );
|
|
|
|
|
|
|
|
|
|
flat_map< account_id_type, account_object* > approving_account_lut;
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
for( optional<account_object>& approving_acct : approving_account_objects )
|
|
|
|
|
{
|
|
|
|
|
if( !approving_acct.valid() )
|
|
|
|
|
{
|
|
|
|
|
wlog( "operation_get_required_auths said approval of non-existing account ${id} was needed",
|
|
|
|
|
("id", v_approving_account_ids[i]) );
|
|
|
|
|
i++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
approving_account_lut[ approving_acct->id ] = &(*approving_acct);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flat_set< key_id_type > approving_key_set;
|
|
|
|
|
for( account_id_type& acct_id : req_active_approvals )
|
|
|
|
|
{
|
|
|
|
|
const auto it = approving_account_lut.find( acct_id );
|
|
|
|
|
if( it == approving_account_lut.end() )
|
|
|
|
|
continue;
|
|
|
|
|
const account_object* acct = it->second;
|
|
|
|
|
vector<key_id_type> v_approving_keys = acct->active.get_keys();
|
|
|
|
|
for( const key_id_type& approving_key : v_approving_keys )
|
|
|
|
|
approving_key_set.insert( approving_key );
|
|
|
|
|
}
|
|
|
|
|
for( account_id_type& acct_id : req_owner_approvals )
|
|
|
|
|
{
|
|
|
|
|
const auto it = approving_account_lut.find( acct_id );
|
|
|
|
|
if( it == approving_account_lut.end() )
|
|
|
|
|
continue;
|
|
|
|
|
const account_object* acct = it->second;
|
|
|
|
|
vector<key_id_type> v_approving_keys = acct->owner.get_keys();
|
|
|
|
|
for( const key_id_type& approving_key : v_approving_keys )
|
|
|
|
|
approving_key_set.insert( approving_key );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx.set_expiration(get_dynamic_global_properties().head_block_id);
|
|
|
|
|
|
|
|
|
|
for( key_id_type& key : approving_key_set )
|
|
|
|
|
{
|
|
|
|
|
auto it = _keys.find(key);
|
|
|
|
|
if( it != _keys.end() )
|
|
|
|
|
{
|
|
|
|
|
fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );
|
|
|
|
|
if( !privkey.valid() )
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT( false, "Malformed private key in _keys" );
|
|
|
|
|
}
|
|
|
|
|
tx.sign( key, *privkey );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( broadcast )
|
|
|
|
|
_remote_net->broadcast_transaction( tx );
|
|
|
|
|
|
|
|
|
|
return tx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction sell_asset(string seller_account,
|
|
|
|
|
string amount_to_sell,
|
|
|
|
|
string symbol_to_sell,
|
|
|
|
|
string min_to_receive,
|
|
|
|
|
string symbol_to_receive,
|
|
|
|
|
uint32_t timeout_sec = 0,
|
|
|
|
|
bool fill_or_kill = false,
|
|
|
|
|
bool broadcast = false)
|
|
|
|
|
{
|
|
|
|
|
account_object seller = get_account( seller_account );
|
|
|
|
|
|
|
|
|
|
limit_order_create_operation op;
|
|
|
|
|
op.seller = seller.id;
|
|
|
|
|
op.amount_to_sell = get_asset(symbol_to_sell).amount_from_string(amount_to_sell);
|
|
|
|
|
op.min_to_receive = get_asset(symbol_to_receive).amount_from_string(min_to_receive);
|
|
|
|
|
if( timeout_sec )
|
|
|
|
|
op.expiration = fc::time_point::now() + fc::seconds(timeout_sec);
|
|
|
|
|
op.fill_or_kill = fill_or_kill;
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back(op);
|
|
|
|
|
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction( tx, broadcast );
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
signed_transaction borrow_asset(string seller_name, string amount_to_borrow, string asset_symbol,
|
2015-06-08 15:50:35 +00:00
|
|
|
string amount_of_collateral, bool broadcast = false)
|
|
|
|
|
{
|
|
|
|
|
account_object seller = get_account(seller_name);
|
|
|
|
|
asset_object mia = get_asset(asset_symbol);
|
|
|
|
|
FC_ASSERT(mia.is_market_issued());
|
|
|
|
|
asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset);
|
|
|
|
|
|
2015-06-16 15:22:28 +00:00
|
|
|
call_order_update_operation op;
|
|
|
|
|
op.funding_account = seller.id;
|
2015-06-26 21:47:03 +00:00
|
|
|
op.delta_debt = mia.amount_from_string(amount_to_borrow);
|
2015-06-18 19:17:48 +00:00
|
|
|
op.delta_collateral = collateral.amount_from_string(amount_of_collateral);
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
signed_transaction trx;
|
|
|
|
|
trx.operations = {op};
|
|
|
|
|
trx.visit(operation_set_fee(_remote_db->get_global_properties().parameters.current_fees));
|
|
|
|
|
trx.validate();
|
|
|
|
|
idump((broadcast));
|
|
|
|
|
|
|
|
|
|
return sign_transaction(trx, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction cancel_order(object_id_type order_id, bool broadcast = false)
|
|
|
|
|
{ try {
|
|
|
|
|
FC_ASSERT(!is_locked());
|
|
|
|
|
FC_ASSERT(order_id.space() == protocol_ids, "Invalid order ID ${id}", ("id", order_id));
|
|
|
|
|
signed_transaction trx;
|
|
|
|
|
|
2015-06-16 15:22:28 +00:00
|
|
|
limit_order_cancel_operation op;
|
|
|
|
|
op.fee_paying_account = get_object<limit_order_object>(order_id).seller;
|
|
|
|
|
op.order = order_id;
|
|
|
|
|
op.fee = op.calculate_fee(_remote_db->get_global_properties().parameters.current_fees);
|
|
|
|
|
trx.operations = {op};
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
trx.validate();
|
|
|
|
|
return sign_transaction(trx, broadcast);
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW((order_id)) }
|
|
|
|
|
|
|
|
|
|
signed_transaction transfer(string from, string to, string amount,
|
|
|
|
|
string asset_symbol, string memo, bool broadcast = false)
|
|
|
|
|
{ try {
|
|
|
|
|
FC_ASSERT( !self.is_locked() );
|
|
|
|
|
fc::optional<asset_object> asset_obj = get_asset(asset_symbol);
|
|
|
|
|
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol));
|
|
|
|
|
|
|
|
|
|
account_object from_account = get_account(from);
|
|
|
|
|
account_object to_account = get_account(to);
|
|
|
|
|
account_id_type from_id = from_account.id;
|
|
|
|
|
account_id_type to_id = get_account_id(to);
|
|
|
|
|
|
|
|
|
|
transfer_operation xfer_op;
|
|
|
|
|
|
|
|
|
|
xfer_op.from = from_id;
|
|
|
|
|
xfer_op.to = to_id;
|
|
|
|
|
xfer_op.amount = asset_obj->amount_from_string(amount);
|
|
|
|
|
|
|
|
|
|
if( memo.size() )
|
|
|
|
|
{
|
|
|
|
|
xfer_op.memo = memo_data();
|
2015-06-16 18:42:02 +00:00
|
|
|
xfer_op.memo->from = from_account.options.memo_key;
|
|
|
|
|
xfer_op.memo->to = to_account.options.memo_key;
|
|
|
|
|
xfer_op.memo->set_message(get_private_key(from_account.options.memo_key),
|
|
|
|
|
get_public_key(to_account.options.memo_key), memo);
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back(xfer_op);
|
|
|
|
|
tx.visit(operation_set_fee(_remote_db->get_global_properties().parameters.current_fees));
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction(tx, broadcast);
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) }
|
|
|
|
|
|
|
|
|
|
signed_transaction issue_asset(string to_account, string amount, string symbol,
|
|
|
|
|
string memo, bool broadcast = false)
|
|
|
|
|
{
|
|
|
|
|
auto asset_obj = get_asset(symbol);
|
|
|
|
|
|
|
|
|
|
account_object to = get_account(to_account);
|
|
|
|
|
account_object issuer = get_account(asset_obj.issuer);
|
|
|
|
|
|
|
|
|
|
asset_issue_operation issue_op;
|
|
|
|
|
issue_op.issuer = asset_obj.issuer;
|
|
|
|
|
issue_op.asset_to_issue = asset_obj.amount_from_string(amount);
|
|
|
|
|
issue_op.issue_to_account = to.id;
|
|
|
|
|
|
|
|
|
|
if( memo.size() )
|
|
|
|
|
{
|
|
|
|
|
issue_op.memo = memo_data();
|
2015-06-16 18:42:02 +00:00
|
|
|
issue_op.memo->from = issuer.options.memo_key;
|
|
|
|
|
issue_op.memo->to = to.options.memo_key;
|
|
|
|
|
issue_op.memo->set_message(get_private_key(issuer.options.memo_key),
|
|
|
|
|
get_public_key(to.options.memo_key), memo);
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction tx;
|
|
|
|
|
tx.operations.push_back(issue_op);
|
|
|
|
|
tx.visit(operation_set_fee(_remote_db->get_global_properties().parameters.current_fees));
|
|
|
|
|
tx.validate();
|
|
|
|
|
|
|
|
|
|
return sign_transaction(tx, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::map<string,std::function<string(fc::variant,const fc::variants&)>> get_result_formatters() const
|
|
|
|
|
{
|
2015-06-26 21:47:03 +00:00
|
|
|
std::map<string,std::function<string(fc::variant,const fc::variants&)> > m;
|
|
|
|
|
m["help"] = [](variant result, const fc::variants& a)
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
2015-06-26 21:47:03 +00:00
|
|
|
return result.get_string();
|
|
|
|
|
};
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
m["gethelp"] = [](variant result, const fc::variants& a)
|
|
|
|
|
{
|
|
|
|
|
return result.get_string();
|
|
|
|
|
};
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
m["get_account_history"] = [this](variant result, const fc::variants& a)
|
|
|
|
|
{
|
|
|
|
|
auto r = result.as<vector<operation_history_object>>();
|
|
|
|
|
std::stringstream ss;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
for( const operation_history_object& i : r )
|
|
|
|
|
{
|
|
|
|
|
auto b = _remote_db->get_block_header(i.block_num);
|
|
|
|
|
FC_ASSERT(b);
|
|
|
|
|
ss << b->timestamp.to_iso_string() << " ";
|
|
|
|
|
i.op.visit(operation_printer(ss, *this, i.result));
|
|
|
|
|
ss << " \n";
|
|
|
|
|
}
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
return ss.str();
|
|
|
|
|
};
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
m["list_account_balances"] = [this](variant result, const fc::variants& a)
|
|
|
|
|
{
|
|
|
|
|
auto r = result.as<vector<asset>>();
|
|
|
|
|
vector<asset_object> asset_recs;
|
|
|
|
|
std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) {
|
|
|
|
|
return get_asset(a.asset_id);
|
|
|
|
|
});
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
|
for( unsigned i = 0; i < asset_recs.size(); ++i )
|
|
|
|
|
ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n";
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
return ss.str();
|
|
|
|
|
};
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
return m;
|
|
|
|
|
}
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
void dbg_make_uia(string creator, string symbol)
|
|
|
|
|
{
|
|
|
|
|
asset_object::asset_options opts;
|
|
|
|
|
opts.flags &= ~(white_list | disable_force_settle | global_settle);
|
|
|
|
|
opts.issuer_permissions = opts.flags;
|
|
|
|
|
opts.core_exchange_rate = price(asset(1), asset(1,1));
|
|
|
|
|
create_asset(get_account(creator).name, symbol, 2, opts, {}, true);
|
|
|
|
|
}
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
void dbg_make_mia(string creator, string symbol)
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
2015-06-26 21:47:03 +00:00
|
|
|
asset_object::asset_options opts;
|
|
|
|
|
opts.flags &= ~white_list;
|
|
|
|
|
opts.issuer_permissions = opts.flags;
|
|
|
|
|
opts.core_exchange_rate = price(asset(1), asset(1,1));
|
|
|
|
|
asset_object::bitasset_options bopts;
|
|
|
|
|
create_asset(get_account(creator).name, symbol, 2, opts, bopts, true);
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
void flood_network(string prefix, uint32_t number_of_transactions)
|
|
|
|
|
{
|
|
|
|
|
const account_object& master = *_wallet.my_accounts.get<by_name>().lower_bound("import");
|
|
|
|
|
int number_of_accounts = number_of_transactions / 3;
|
|
|
|
|
number_of_transactions -= number_of_accounts;
|
|
|
|
|
auto key = derive_private_key("floodshill", 0);
|
|
|
|
|
try {
|
|
|
|
|
dbg_make_uia(master.name, "SHILL");
|
|
|
|
|
} catch(...) {/* Ignore; the asset probably already exists.*/}
|
|
|
|
|
|
|
|
|
|
fc::time_point start = fc::time_point::now(); for( int i = 0; i < number_of_accounts; ++i )
|
|
|
|
|
create_account_with_private_key(key, prefix + fc::to_string(i), master.name, master.name, true, false);
|
|
|
|
|
fc::time_point end = fc::time_point::now();
|
|
|
|
|
ilog("Created ${n} accounts in ${time} milliseconds",
|
|
|
|
|
("n", number_of_accounts)("time", (end - start).count() / 1000));
|
|
|
|
|
|
|
|
|
|
start = fc::time_point::now();
|
|
|
|
|
for( int i = 0; i < number_of_accounts; ++i )
|
|
|
|
|
{
|
|
|
|
|
transfer(master.name, prefix + fc::to_string(i), "10", "CORE", "", true);
|
|
|
|
|
transfer(master.name, prefix + fc::to_string(i), "1", "CORE", "", true);
|
|
|
|
|
}
|
|
|
|
|
end = fc::time_point::now();
|
|
|
|
|
ilog("Transferred to ${n} accounts in ${time} milliseconds",
|
|
|
|
|
("n", number_of_accounts*2)("time", (end - start).count() / 1000));
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
start = fc::time_point::now();
|
|
|
|
|
for( int i = 0; i < number_of_accounts; ++i )
|
|
|
|
|
issue_asset(prefix + fc::to_string(i), "1000", "SHILL", "", true);
|
|
|
|
|
end = fc::time_point::now();
|
|
|
|
|
ilog("Issued to ${n} accounts in ${time} milliseconds",
|
|
|
|
|
("n", number_of_accounts)("time", (end - start).count() / 1000));
|
|
|
|
|
|
|
|
|
|
}
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
string _wallet_filename;
|
|
|
|
|
wallet_data _wallet;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
map<key_id_type,string> _keys;
|
|
|
|
|
fc::sha512 _checksum;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
fc::api<login_api> _remote_api;
|
|
|
|
|
fc::api<database_api> _remote_db;
|
|
|
|
|
fc::api<network_api> _remote_net;
|
|
|
|
|
fc::api<history_api> _remote_hist;
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
#ifdef __unix__
|
2015-06-26 21:47:03 +00:00
|
|
|
mode_t _old_umask;
|
2015-06-08 15:50:35 +00:00
|
|
|
#endif
|
2015-06-26 21:47:03 +00:00
|
|
|
const string _wallet_filename_extension = ".wallet";
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
mutable map<asset_id_type, asset_object> _asset_cache;
|
2015-06-08 15:50:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void operation_printer::fee(const asset& a)const {
|
|
|
|
|
out << " (Fee: " << wallet.get_asset(a.asset_id).amount_to_pretty_string(a) << ")";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operation_printer::operator()(const transfer_operation& op) const
|
|
|
|
|
{
|
|
|
|
|
out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount)
|
|
|
|
|
<< " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name;
|
|
|
|
|
if( op.memo )
|
|
|
|
|
{
|
|
|
|
|
if( wallet.is_locked() )
|
|
|
|
|
{
|
|
|
|
|
out << " -- Unlock wallet to see memo.";
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
optional<key_object> sender_key = wallet._remote_db->get_objects({op.memo->from}).front().as<optional<key_object>>();
|
|
|
|
|
FC_ASSERT(sender_key, "Sender key ${k} does not exist.", ("k", op.memo->from));
|
|
|
|
|
FC_ASSERT(wallet._keys.count(op.memo->to), "Memo is encrypted to a key ${k} not in this wallet.",
|
|
|
|
|
("k", op.memo->to));
|
|
|
|
|
auto my_key = wif_to_key(wallet._keys.at(op.memo->to));
|
|
|
|
|
FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted.");
|
|
|
|
|
out << " -- Memo: " << op.memo->get_message(*my_key, sender_key->key());
|
|
|
|
|
} catch (const fc::exception& e) {
|
|
|
|
|
out << " -- could not decrypt memo";
|
|
|
|
|
elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fee(op.fee);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operation_printer::operator()(const account_create_operation& op) const
|
|
|
|
|
{
|
|
|
|
|
out << "Create Account '" << op.name << "'";
|
|
|
|
|
fee(op.fee);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operation_printer::operator()(const account_update_operation& op) const
|
|
|
|
|
{
|
|
|
|
|
out << "Update Account '" << wallet.get_account(op.account).name << "'";
|
|
|
|
|
fee(op.fee);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operation_printer::operator()(const key_create_operation& op) const
|
|
|
|
|
{
|
|
|
|
|
out << "Register";
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
typedef void result_type;
|
|
|
|
|
ostream& out;
|
|
|
|
|
void operator()(const public_key_type& key) {
|
|
|
|
|
out << " key " << string(key);
|
|
|
|
|
}
|
|
|
|
|
void operator()(const address& addr) {
|
|
|
|
|
out << " address " << string(addr);
|
|
|
|
|
}
|
|
|
|
|
} printer{out};
|
|
|
|
|
op.key_data.visit(printer);
|
|
|
|
|
out << " as " << fc::json::to_string(result.get<object_id_type>());
|
|
|
|
|
fee(op.fee);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operation_printer::operator()(const asset_create_operation& op) const
|
|
|
|
|
{
|
|
|
|
|
out << "Create ";
|
|
|
|
|
if( op.bitasset_options.valid() )
|
|
|
|
|
out << "BitAsset ";
|
|
|
|
|
else
|
|
|
|
|
out << "User-Issue Asset ";
|
|
|
|
|
out << "'" << op.symbol << "' with issuer " << wallet.get_account(op.issuer).name;
|
|
|
|
|
fee(op.fee);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}}}
|
|
|
|
|
namespace graphene { namespace wallet {
|
|
|
|
|
|
|
|
|
|
wallet_api::wallet_api(fc::api<login_api> rapi)
|
|
|
|
|
: my(new detail::wallet_api_impl(*this, rapi))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wallet_api::~wallet_api()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool wallet_api::copy_wallet_file(string destination_filename)
|
|
|
|
|
{
|
|
|
|
|
return my->copy_wallet_file(destination_filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
optional<signed_block> 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)
|
|
|
|
|
{
|
|
|
|
|
if( auto real_id = detail::maybe_id<account_id_type>(id) )
|
|
|
|
|
return my->_remote_db->get_account_balances(*real_id, flat_set<asset_id_type>());
|
|
|
|
|
return my->_remote_db->get_account_balances(get_account(id).id, flat_set<asset_id_type>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<asset_object> wallet_api::list_assets(const string& lowerbound, uint32_t limit)const
|
|
|
|
|
{
|
|
|
|
|
return my->_remote_db->list_assets( lowerbound, limit );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<operation_history_object> wallet_api::get_account_history(string name, int limit)const
|
|
|
|
|
{
|
|
|
|
|
return my->_remote_hist->get_account_history(get_account(name).get_id(), operation_history_id_type(), limit, operation_history_id_type());
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-25 13:38:31 +00:00
|
|
|
vector<bucket_object> wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket )const
|
|
|
|
|
{
|
|
|
|
|
return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, fc::time_point_sec(), fc::time_point::now() );
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
vector<limit_order_object> wallet_api::get_limit_orders(string a, string b, uint32_t limit)const
|
|
|
|
|
{
|
|
|
|
|
return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<call_order_object> wallet_api::get_call_orders(string a, uint32_t limit)const
|
|
|
|
|
{
|
|
|
|
|
return my->_remote_db->get_call_orders(get_asset(a).id, limit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<force_settlement_object> wallet_api::get_settle_orders(string a, uint32_t limit)const
|
|
|
|
|
{
|
|
|
|
|
return my->_remote_db->get_settle_orders(get_asset(a).id, limit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string wallet_api::suggest_brain_key()const
|
|
|
|
|
{
|
|
|
|
|
return string("dummy");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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});
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-26 21:47:03 +00:00
|
|
|
string wallet_api::get_wallet_filename() const
|
|
|
|
|
{
|
|
|
|
|
return my->get_wallet_filename();
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
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, int 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::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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asset_object wallet_api::get_asset(string asset_name_or_id) const
|
|
|
|
|
{
|
|
|
|
|
auto a = my->find_asset(asset_name_or_id);
|
|
|
|
|
FC_ASSERT(a);
|
|
|
|
|
return *a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_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
|
|
|
|
|
string shorthash = detail::address_to_shorthash(wif_to_key(wif_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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string wallet_api::normalize_brain_key(string s) const
|
|
|
|
|
{
|
|
|
|
|
return detail::normalize_brain_key( s );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
variant wallet_api::info()
|
|
|
|
|
{
|
|
|
|
|
return my->info();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
uint8_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_object::asset_options common,
|
|
|
|
|
fc::optional<asset_object::bitasset_options> bitasset_opts,
|
|
|
|
|
bool broadcast)
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-19 23:26:29 +00:00
|
|
|
signed_transaction wallet_api::update_asset(string symbol,
|
|
|
|
|
optional<string> new_issuer,
|
|
|
|
|
asset_object::asset_options new_options,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
|
|
|
|
return my->update_asset(symbol, new_issuer, new_options, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction wallet_api::update_bitasset(string symbol,
|
|
|
|
|
asset_object::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::burn_asset(string from,
|
|
|
|
|
string amount,
|
|
|
|
|
string symbol,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
|
|
|
|
return my->fund_asset_fee_pool(from, amount, symbol, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction wallet_api::global_settle_asset(string symbol,
|
|
|
|
|
price settle_price,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
2015-06-20 22:33:58 +00:00
|
|
|
return my->global_settle_asset(symbol, settle_price, broadcast);
|
2015-06-19 23:26:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction wallet_api::settle_asset(string account_to_settle,
|
|
|
|
|
string amount_to_settle,
|
|
|
|
|
string symbol,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
2015-06-20 22:33:58 +00:00
|
|
|
return my->settle_asset(account_to_settle, amount_to_settle, symbol, 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);
|
2015-06-19 23:26:29 +00:00
|
|
|
}
|
|
|
|
|
|
2015-06-22 23:37:18 +00:00
|
|
|
signed_transaction wallet_api::create_delegate(string owner_account,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
|
|
|
|
return my->create_delegate(owner_account, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signed_transaction wallet_api::create_witness(string owner_account,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
|
|
|
|
return my->create_witness(owner_account, broadcast);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-22 22:02:51 +00:00
|
|
|
signed_transaction wallet_api::vote_for_delegate(string voting_account,
|
|
|
|
|
string witness,
|
|
|
|
|
bool approve,
|
|
|
|
|
bool broadcast /* = false */)
|
|
|
|
|
{
|
|
|
|
|
return my->vote_for_delegate(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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-22 23:37:18 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
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) ) }
|
|
|
|
|
|
|
|
|
|
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::flood_network(string prefix, uint32_t number_of_transactions)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(!is_locked());
|
|
|
|
|
my->flood_network(prefix, number_of_transactions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string wallet_api::help()const
|
|
|
|
|
{
|
2015-06-26 21:47:03 +00:00
|
|
|
std::vector<std::string> method_names = my->method_documentation.get_method_names();
|
2015-06-08 15:50:35 +00:00
|
|
|
std::stringstream ss;
|
2015-06-26 21:47:03 +00:00
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-08 15:50:35 +00:00
|
|
|
return ss.str();
|
|
|
|
|
}
|
2015-06-26 21:47:03 +00:00
|
|
|
|
|
|
|
|
string wallet_api::gethelp(const string& method)const
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
|
|
|
|
fc::api<wallet_api> tmp;
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "\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_object::asset_options() );
|
|
|
|
|
ss << "\nExample value of BITASSET_OPTIONS: \n";
|
|
|
|
|
ss << fc::json::to_pretty_string( graphene::chain::asset_object::bitasset_options() );
|
|
|
|
|
ss << "\nBITASSET_OPTIONS may be null\n";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-06-26 21:47:03 +00:00
|
|
|
std::string doxygenHelpString = my->method_documentation.get_detailed_description(method);
|
|
|
|
|
if (!doxygenHelpString.empty())
|
|
|
|
|
ss << doxygenHelpString;
|
|
|
|
|
else
|
|
|
|
|
ss << "No help defined for method " << method << "\n";
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool wallet_api::load_wallet_file( string wallet_filename )
|
|
|
|
|
{
|
|
|
|
|
return my->load_wallet_file( wallet_filename );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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({});
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-28 20:16:24 +00:00
|
|
|
signed_transaction wallet_api::import_balance( string name_or_id, const vector<string>& wif_keys, bool broadcast )
|
|
|
|
|
{ try {
|
|
|
|
|
FC_ASSERT(!is_locked());
|
|
|
|
|
account_object claimer = get_account( name_or_id );
|
|
|
|
|
|
|
|
|
|
vector<address> addrs;
|
|
|
|
|
map<address,private_key_type> keys;
|
|
|
|
|
for( auto wif_key : wif_keys )
|
|
|
|
|
{
|
|
|
|
|
auto priv_key = wif_to_key(wif_key);
|
|
|
|
|
FC_ASSERT( priv_key, "Invalid Private Key", ("key",wif_key) );
|
|
|
|
|
addrs.push_back( priv_key->get_public_key() );
|
|
|
|
|
keys[addrs.back()] = *priv_key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto balances = my->_remote_db->get_balance_objects( addrs );
|
|
|
|
|
wdump((balances));
|
|
|
|
|
addrs.clear();
|
|
|
|
|
|
|
|
|
|
set<asset_id_type> bal_types;
|
|
|
|
|
for( auto b : balances ) bal_types.insert( b.balance.asset_id );
|
|
|
|
|
|
|
|
|
|
set<address> required_addrs;
|
|
|
|
|
signed_transaction trx;
|
|
|
|
|
for( auto a : bal_types )
|
|
|
|
|
{
|
|
|
|
|
balance_claim_operation op;
|
|
|
|
|
op.total_claimed = asset( 0, a );
|
|
|
|
|
for( auto b : balances )
|
|
|
|
|
{
|
|
|
|
|
if( b.balance.asset_id == a )
|
|
|
|
|
{
|
|
|
|
|
op.total_claimed += b.balance;
|
|
|
|
|
op.owners.insert( b.owner );
|
|
|
|
|
op.deposit_to_account = claimer.id;
|
|
|
|
|
required_addrs.insert( b.owner );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
trx.operations.push_back(op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trx.visit( operation_set_fee( my->_remote_db->get_global_properties().parameters.current_fees ) );
|
|
|
|
|
trx.validate();
|
|
|
|
|
|
|
|
|
|
auto tx = sign_transaction( trx, false );
|
|
|
|
|
|
|
|
|
|
for( auto a : required_addrs )
|
2015-06-30 19:11:26 +00:00
|
|
|
tx.sign( keys[a] );
|
2015-06-28 20:16:24 +00:00
|
|
|
|
|
|
|
|
if( broadcast )
|
|
|
|
|
my->_remote_net->broadcast_transaction(tx);
|
|
|
|
|
|
|
|
|
|
return tx;
|
|
|
|
|
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (name_or_id) ) }
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
map<key_id_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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-16 15:22:28 +00:00
|
|
|
signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell,
|
2015-06-08 15:50:35 +00:00
|
|
|
string asset_symbol, string amount_of_collateral, bool broadcast)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(!is_locked());
|
2015-06-16 15:22:28 +00:00
|
|
|
return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast);
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|
|
|
|
|
} }
|
|
|
|
|
|
2015-06-18 13:19:14 +00:00
|
|
|
void fc::to_variant(const account_multi_index_type& accts, fc::variant& vo)
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
|
|
|
|
vo = vector<account_object>(accts.begin(), accts.end());
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-18 13:19:14 +00:00
|
|
|
void fc::from_variant(const fc::variant& var, account_multi_index_type& vo)
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
|
|
|
|
const vector<account_object>& v = var.as<vector<account_object>>();
|
2015-06-18 13:19:14 +00:00
|
|
|
vo = account_multi_index_type(v.begin(), v.end());
|
2015-06-08 15:50:35 +00:00
|
|
|
}
|