Merge branch 'master' of github.com:cryptonomex/graphene
This commit is contained in:
commit
c537e41fcb
11 changed files with 303 additions and 206 deletions
|
|
@ -54,6 +54,38 @@ namespace bpo = boost::program_options;
|
|||
|
||||
namespace detail {
|
||||
|
||||
genesis_state_type create_example_genesis() {
|
||||
auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
|
||||
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
|
||||
genesis_state_type initial_state;
|
||||
/*
|
||||
fc::reflector<fee_schedule_type>::visit(
|
||||
fee_schedule_type::fee_set_visitor{initial_state.initial_parameters.current_fees, 0});
|
||||
*/
|
||||
initial_state.initial_parameters.current_fees.set_all_fees(0);
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, nathan_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
auto secret = secret_hash_type::hash(enc.result());
|
||||
initial_state.initial_active_witnesses = 10;
|
||||
for( int i = 0; i < initial_state.initial_active_witnesses; ++i )
|
||||
{
|
||||
auto name = "init"+fc::to_string(i);
|
||||
initial_state.initial_accounts.emplace_back(name,
|
||||
nathan_key.get_public_key(),
|
||||
nathan_key.get_public_key(),
|
||||
true);
|
||||
initial_state.initial_committee_candidates.push_back({name});
|
||||
initial_state.initial_witness_candidates.push_back({name, nathan_key.get_public_key(), secret});
|
||||
}
|
||||
|
||||
initial_state.initial_accounts.emplace_back("nathan", nathan_key.get_public_key());
|
||||
initial_state.initial_balances.push_back({nathan_key.get_public_key(),
|
||||
GRAPHENE_SYMBOL,
|
||||
GRAPHENE_MAX_SHARE_SUPPLY});
|
||||
return initial_state;
|
||||
}
|
||||
|
||||
class application_impl : public net::node_delegate
|
||||
{
|
||||
public:
|
||||
|
|
@ -107,7 +139,7 @@ namespace detail {
|
|||
wsc->register_api(fc::api<graphene::app::login_api>(login));
|
||||
c->set_session_data( wsc );
|
||||
});
|
||||
ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
|
||||
ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
|
||||
_websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as<string>()) );
|
||||
_websocket_server->start_accept();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
|
@ -134,7 +166,7 @@ namespace detail {
|
|||
wsc->register_api(fc::api<graphene::app::login_api>(login));
|
||||
c->set_session_data( wsc );
|
||||
});
|
||||
ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
|
||||
ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
|
||||
_websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as<string>()) );
|
||||
_websocket_tls_server->start_accept();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
|
@ -155,8 +187,9 @@ namespace detail {
|
|||
bool clean = !fc::exists(_data_dir / "blockchain/dblock");
|
||||
fc::create_directories(_data_dir / "blockchain/dblock");
|
||||
|
||||
auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
|
||||
genesis_state_type initial_state;
|
||||
/*
|
||||
<<<<<<< HEAD
|
||||
initial_state.initial_parameters.current_fees.set_all_fees(0);
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, nathan_key);
|
||||
|
|
@ -177,11 +210,14 @@ namespace detail {
|
|||
initial_state.initial_balances.push_back({nathan_key.get_public_key(),
|
||||
GRAPHENE_SYMBOL,
|
||||
GRAPHENE_MAX_SHARE_SUPPLY});
|
||||
=======
|
||||
>>>>>>> b89ff4e6b1d732fbfe0cba20b4f41ea7df560f3c
|
||||
*/
|
||||
if( _options->count("genesis-json") )
|
||||
initial_state = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
|
||||
.as<genesis_state_type>();
|
||||
else
|
||||
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
|
||||
initial_state = create_example_genesis();
|
||||
|
||||
if( _options->count("resync-blockchain") )
|
||||
_chain_db->wipe(_data_dir / "blockchain", true);
|
||||
|
|
@ -426,13 +462,11 @@ application::~application()
|
|||
{
|
||||
if( my->_p2p_network )
|
||||
{
|
||||
//ilog("Closing p2p node");
|
||||
my->_p2p_network->close();
|
||||
my->_p2p_network.reset();
|
||||
}
|
||||
if( my->_chain_db )
|
||||
{
|
||||
//ilog("Closing chain database");
|
||||
my->_chain_db->close();
|
||||
}
|
||||
}
|
||||
|
|
@ -451,6 +485,10 @@ void application::set_program_options(boost::program_options::options_descriptio
|
|||
;
|
||||
command_line_options.add(configuration_file_options);
|
||||
command_line_options.add_options()
|
||||
("create-genesis-json", bpo::value<boost::filesystem::path>(),
|
||||
"Path to create a Genesis State at. If a well-formed JSON file exists at the path, it will be parsed and any "
|
||||
"missing fields in a Genesis State will be added, and any unknown fields will be removed. If no file or an "
|
||||
"invalid file is found, it will be replaced with an example Genesis State.")
|
||||
("replay-blockchain", "Rebuild object graph by replaying all blocks")
|
||||
("resync-blockchain", "Delete all blocks and re-sync with network from scratch")
|
||||
;
|
||||
|
|
@ -462,6 +500,31 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti
|
|||
{
|
||||
my->_data_dir = data_dir;
|
||||
my->_options = &options;
|
||||
|
||||
if( options.count("create-genesis-json") )
|
||||
{
|
||||
fc::path genesis_out = options.at("create-genesis-json").as<boost::filesystem::path>();
|
||||
genesis_state_type genesis_state = detail::create_example_genesis();
|
||||
if( fc::exists(genesis_out) )
|
||||
{
|
||||
try {
|
||||
genesis_state = fc::json::from_file(genesis_out).as<genesis_state_type>();
|
||||
} catch(const fc::exception& e) {
|
||||
std::cerr << "Unable to parse existing genesis file:\n" << e.to_string()
|
||||
<< "\nWould you like to replace it? [y/N] ";
|
||||
char response = std::cin.get();
|
||||
if( toupper(response) != 'Y' )
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "Updating genesis state in file " << genesis_out.generic_string() << "\n";
|
||||
} else {
|
||||
std::cerr << "Creating example genesis state in file " << genesis_out.generic_string() << "\n";
|
||||
}
|
||||
fc::json::save_to_file(genesis_state, genesis_out);
|
||||
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
void application::startup()
|
||||
|
|
|
|||
|
|
@ -20,59 +20,50 @@
|
|||
#include <boost/rational.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
bool operator < ( const asset& a, const asset& b )
|
||||
|
||||
bool operator == ( const price& a, const price& b )
|
||||
{
|
||||
return std::tie( a.asset_id, a.amount ) < std::tie( b.asset_id, b.amount);
|
||||
}
|
||||
bool operator <= ( const asset& a, const asset& b )
|
||||
{
|
||||
return std::tie( a.asset_id, a.amount ) <= std::tie( b.asset_id, b.amount);
|
||||
if( std::tie( a.base.asset_id, a.quote.asset_id ) != std::tie( b.base.asset_id, b.quote.asset_id ) )
|
||||
return false;
|
||||
|
||||
const auto amult = fc::uint128( b.quote.amount.value ) * a.base.amount.value;
|
||||
const auto bmult = fc::uint128( a.quote.amount.value ) * b.base.amount.value;
|
||||
|
||||
return amult == bmult;
|
||||
}
|
||||
|
||||
bool operator < ( const price& a, const price& b )
|
||||
{
|
||||
if( a.base.asset_id < b.base.asset_id ) return true;
|
||||
if( a.base.asset_id > b.base.asset_id ) return false;
|
||||
if( a.quote.asset_id < b.quote.asset_id ) return true;
|
||||
if( a.quote.asset_id > b.quote.asset_id ) return false;
|
||||
auto amult = fc::uint128(b.quote.amount.value) * a.base.amount.value;
|
||||
auto bmult = fc::uint128(a.quote.amount.value) * b.base.amount.value;
|
||||
assert( (a.to_real() < b.to_real()) == (amult < bmult) );
|
||||
|
||||
const auto amult = fc::uint128( b.quote.amount.value ) * a.base.amount.value;
|
||||
const auto bmult = fc::uint128( a.quote.amount.value ) * b.base.amount.value;
|
||||
|
||||
return amult < bmult;
|
||||
}
|
||||
|
||||
bool operator <= ( const price& a, const price& b )
|
||||
{
|
||||
if( a.base.asset_id < b.base.asset_id ) return true;
|
||||
if( a.base.asset_id > b.base.asset_id ) return false;
|
||||
if( a.quote.asset_id < b.quote.asset_id ) return true;
|
||||
if( a.quote.asset_id > b.quote.asset_id ) return false;
|
||||
auto amult = fc::uint128(b.quote.amount.value) * a.base.amount.value;
|
||||
auto bmult = fc::uint128(a.quote.amount.value) * b.base.amount.value;
|
||||
assert( (a.to_real() <= b.to_real()) == (amult <= bmult) );
|
||||
return amult <= bmult;
|
||||
}
|
||||
bool operator == ( const price& a, const price& b )
|
||||
{
|
||||
if( a.base.asset_id < b.base.asset_id ) return true;
|
||||
if( a.base.asset_id > b.base.asset_id ) return false;
|
||||
if( a.quote.asset_id < b.quote.asset_id ) return true;
|
||||
if( a.quote.asset_id > b.quote.asset_id ) return false;
|
||||
auto amult = fc::uint128(a.quote.amount.value) * b.base.amount.value;
|
||||
auto bmult = fc::uint128(b.quote.amount.value) * a.base.amount.value;
|
||||
return amult == bmult;
|
||||
return (a == b) || (a < b);
|
||||
}
|
||||
|
||||
bool operator != ( const price& a, const price& b )
|
||||
{
|
||||
return !(a==b);
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
bool operator > ( const price& a, const price& b )
|
||||
{
|
||||
return !(a <= b);
|
||||
}
|
||||
|
||||
bool operator >= ( const price& a, const price& b )
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
bool operator > ( const price& a, const price& b )
|
||||
{
|
||||
return !(a <= b);
|
||||
}
|
||||
|
||||
asset operator * ( const asset& a, const price& b )
|
||||
{
|
||||
|
|
@ -101,18 +92,18 @@ namespace graphene { namespace chain {
|
|||
|
||||
price price::max( asset_id_type base, asset_id_type quote ) { return asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), base ) / asset( share_type(1), quote); }
|
||||
price price::min( asset_id_type base, asset_id_type quote ) { return asset( 1, base ) / asset( GRAPHENE_MAX_SHARE_SUPPLY, quote); }
|
||||
|
||||
|
||||
/**
|
||||
* The black swan price is defined as debt/collateral, we want to perform a margin call
|
||||
* before debt == collateral. Given a debt/collateral ratio of 1 USD / CORE and
|
||||
* a maintenance collateral requirement of 2x we can define the call price to be
|
||||
* 2 USD / CORE.
|
||||
* 2 USD / CORE.
|
||||
*
|
||||
* This method divides the collateral by the maintenance collateral ratio to derive
|
||||
* a call price for the given black swan ratio.
|
||||
*
|
||||
* There exists some cases where the debt and collateral values are so small that
|
||||
* dividing by the collateral ratio will result in a 0 price or really poor
|
||||
* dividing by the collateral ratio will result in a 0 price or really poor
|
||||
* rounding errors. No matter what the collateral part of the price ratio can
|
||||
* never go to 0 and the debt can never go more than GRAPHENE_MAX_SHARE_SUPPLY
|
||||
*
|
||||
|
|
@ -121,7 +112,7 @@ namespace graphene { namespace chain {
|
|||
price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio)
|
||||
{ try {
|
||||
//wdump((debt)(collateral)(collateral_ratio));
|
||||
boost::rational<uint64_t> swan(debt.amount.value,collateral.amount.value);
|
||||
boost::rational<uint64_t> swan(debt.amount.value,collateral.amount.value);
|
||||
boost::rational<uint64_t> ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
|
||||
auto cp = swan * ratio;
|
||||
return ~(asset( cp.numerator(), debt.asset_id ) / asset( cp.denominator(), collateral.asset_id ));
|
||||
|
|
@ -149,7 +140,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
price price_feed::max_short_squeeze_price()const
|
||||
{
|
||||
boost::rational<uint64_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); //debt.amount.value,collateral.amount.value);
|
||||
boost::rational<uint64_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); //debt.amount.value,collateral.amount.value);
|
||||
boost::rational<uint64_t> ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
|
||||
auto cp = sp * ratio;
|
||||
return (asset( cp.numerator(), settlement_price.base.asset_id ) / asset( cp.denominator(), settlement_price.quote.asset_id ));
|
||||
|
|
|
|||
|
|
@ -140,6 +140,8 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
{ try {
|
||||
FC_ASSERT(genesis_state.initial_witness_candidates.size() > 0,
|
||||
"Cannot start a chain with zero witnesses.");
|
||||
FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(),
|
||||
"initial_active_witnesses is larger than the number of candidate witnesses.");
|
||||
|
||||
_undo_db.disable();
|
||||
struct auth_inhibitor {
|
||||
|
|
@ -252,24 +254,12 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
// Create initial accounts
|
||||
for( const auto& account : genesis_state.initial_accounts )
|
||||
{
|
||||
/*
|
||||
key_id_type key_id = apply_operation(genesis_eval_state,
|
||||
key_create_operation({asset(),
|
||||
GRAPHENE_TEMP_ACCOUNT,
|
||||
account.owner_key})).get<object_id_type>();
|
||||
*/
|
||||
account_create_operation cop;
|
||||
cop.name = account.name;
|
||||
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
|
||||
cop.owner = authority(1, account.owner_key, 1);
|
||||
if( account.owner_key != account.active_key )
|
||||
{
|
||||
/*
|
||||
key_id = apply_operation(genesis_eval_state,
|
||||
key_create_operation({asset(),
|
||||
GRAPHENE_TEMP_ACCOUNT,
|
||||
account.owner_key})).get<object_id_type>();
|
||||
*/
|
||||
cop.active = authority(1, account.owner_key, 1);
|
||||
} else {
|
||||
cop.active = cop.owner;
|
||||
|
|
@ -286,21 +276,27 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to get account ID by name
|
||||
const auto& accounts_by_name = get_index_type<account_index>().indices().get<by_name>();
|
||||
auto get_account_id = [&accounts_by_name](const string& name) {
|
||||
auto itr = accounts_by_name.find(name);
|
||||
FC_ASSERT(itr != accounts_by_name.end());
|
||||
FC_ASSERT(itr != accounts_by_name.end(),
|
||||
"Unable to find account '${acct}'. Did you forget to add a record for it to initial_accounts?",
|
||||
("acct", name));
|
||||
return itr->get_id();
|
||||
};
|
||||
|
||||
// Helper function to get asset ID by symbol
|
||||
const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto get_asset_id = [&assets_by_symbol](const string& symbol) {
|
||||
auto itr = assets_by_symbol.find(symbol);
|
||||
FC_ASSERT(itr != assets_by_symbol.end(),
|
||||
"Unable to find asset '${sym}'. Did you forget to add a record for it to initial_assets?",
|
||||
("sym", symbol));
|
||||
return itr->get_id();
|
||||
};
|
||||
|
||||
// Create initial assets
|
||||
const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto get_asset_id = [&assets_by_symbol](const string& symbol) {
|
||||
auto itr = assets_by_symbol.find(symbol);
|
||||
FC_ASSERT(itr != assets_by_symbol.end());
|
||||
return itr->get_id();
|
||||
};
|
||||
|
||||
for( const genesis_state_type::initial_asset_type& asset : genesis_state.initial_assets )
|
||||
{
|
||||
asset_dynamic_data_id_type dynamic_data_id;
|
||||
|
|
@ -314,12 +310,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
int collateral_holder_number = 0;
|
||||
for( const auto& collateral_rec : asset.bitasset_options->collateral_records )
|
||||
{
|
||||
/*
|
||||
key_id_type key_id = apply_operation(genesis_eval_state,
|
||||
key_create_operation{{},
|
||||
GRAPHENE_TEMP_ACCOUNT,
|
||||
collateral_rec.owner}).get<object_id_type>();
|
||||
*/
|
||||
account_create_operation cop;
|
||||
cop.name = asset.symbol + "-collateral-holder-" + std::to_string(collateral_holder_number);
|
||||
boost::algorithm::to_lower(cop.name);
|
||||
|
|
@ -415,17 +405,11 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
// Create initial witnesses and delegates
|
||||
std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(),
|
||||
[&](const genesis_state_type::initial_witness_type& witness) {
|
||||
/*
|
||||
const key_object& signing_key = create<key_object>([&witness](key_object& k) {
|
||||
k.key_data = witness.block_signing_key;
|
||||
});
|
||||
*/
|
||||
|
||||
witness_create_operation op;
|
||||
op.block_signing_key = witness.block_signing_key;
|
||||
op.initial_secret = witness.initial_secret;
|
||||
op.witness_account = get_account_id(witness.owner_name);
|
||||
apply_operation(genesis_eval_state, op).get<object_id_type>();
|
||||
apply_operation(genesis_eval_state, op);
|
||||
});
|
||||
std::for_each(genesis_state.initial_committee_candidates.begin(), genesis_state.initial_committee_candidates.end(),
|
||||
[&](const genesis_state_type::initial_committee_member_type& member) {
|
||||
|
|
@ -459,10 +443,10 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
auto init_witnesses = get_global_properties().active_witnesses;
|
||||
|
||||
_wso.scheduler = witness_scheduler();
|
||||
_wso.scheduler._min_token_count = init_witnesses.size() / 2;
|
||||
_wso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
||||
_wso.scheduler.update(init_witnesses);
|
||||
|
||||
for( size_t i=0; i<init_witnesses.size(); i++ )
|
||||
for( size_t i=0; i<init_witnesses.size(); ++i )
|
||||
_wso.scheduler.produce_schedule(rng);
|
||||
|
||||
_wso.last_scheduling_block = 0;
|
||||
|
|
|
|||
|
|
@ -143,16 +143,16 @@ void database::update_active_witnesses()
|
|||
a.active.weight_threshold += 1;
|
||||
});
|
||||
|
||||
modify( gpo, [&]( global_property_object& gp ){
|
||||
modify(gpo, [&]( global_property_object& gp ){
|
||||
gp.active_witnesses.clear();
|
||||
gp.active_witnesses.reserve( wits.size() );
|
||||
gp.active_witnesses.reserve(wits.size());
|
||||
std::transform(wits.begin(), wits.end(),
|
||||
std::inserter(gp.active_witnesses, gp.active_witnesses.end()),
|
||||
[](const witness_object& w) {
|
||||
return w.id;
|
||||
});
|
||||
gp.witness_accounts.clear();
|
||||
gp.witness_accounts.reserve( wits.size() );
|
||||
gp.witness_accounts.reserve(wits.size());
|
||||
std::transform(wits.begin(), wits.end(),
|
||||
std::inserter(gp.witness_accounts, gp.witness_accounts.end()),
|
||||
[](const witness_object& w) {
|
||||
|
|
@ -161,11 +161,10 @@ void database::update_active_witnesses()
|
|||
});
|
||||
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
modify( wso, [&]( witness_schedule_object& _wso )
|
||||
modify(wso, [&](witness_schedule_object& _wso)
|
||||
{
|
||||
_wso.scheduler.update( gpo.active_witnesses );
|
||||
} );
|
||||
|
||||
_wso.scheduler.update(gpo.active_witnesses);
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::update_active_delegates()
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/types.hpp>
|
||||
#include <graphene/chain/config.hpp>
|
||||
#include <graphene/chain/types.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -42,20 +42,34 @@ namespace graphene { namespace chain {
|
|||
return *this;
|
||||
}
|
||||
asset operator -()const { return asset( -amount, asset_id ); }
|
||||
|
||||
friend bool operator == ( const asset& a, const asset& b )
|
||||
{
|
||||
return tie(a.asset_id,a.amount) == tie(b.asset_id,b.amount);
|
||||
return std::tie( a.asset_id, a.amount ) == std::tie( b.asset_id, b.amount );
|
||||
}
|
||||
friend bool operator >= ( const asset& a, const asset& b )
|
||||
friend bool operator < ( const asset& a, const asset& b )
|
||||
{
|
||||
FC_ASSERT( a.asset_id == b.asset_id );
|
||||
return a.amount >= b.amount;
|
||||
return a.amount < b.amount;
|
||||
}
|
||||
friend bool operator <= ( const asset& a, const asset& b )
|
||||
{
|
||||
return (a == b) || (a < b);
|
||||
}
|
||||
|
||||
friend bool operator != ( const asset& a, const asset& b )
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
friend bool operator > ( const asset& a, const asset& b )
|
||||
{
|
||||
FC_ASSERT( a.asset_id == b.asset_id );
|
||||
return a.amount > b.amount;
|
||||
return !(a <= b);
|
||||
}
|
||||
friend bool operator >= ( const asset& a, const asset& b )
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
friend asset operator - ( const asset& a, const asset& b )
|
||||
{
|
||||
FC_ASSERT( a.asset_id == b.asset_id );
|
||||
|
|
@ -66,7 +80,6 @@ namespace graphene { namespace chain {
|
|||
FC_ASSERT( a.asset_id == b.asset_id );
|
||||
return asset( a.amount + b.amount, a.asset_id );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -63,58 +63,98 @@ bool is_valid_symbol( const string& symbol )
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Valid names can contain [a, z], [0, 9], '.', and '-'
|
||||
* They must start with [a, z]
|
||||
* They must end with [a, z] or [0, 9]
|
||||
* '.' must be followed by [a, z]
|
||||
* '-' must be followed by [a, z] or [0, 9]
|
||||
* Names must comply with the following grammar (RFC 1035):
|
||||
* <domain> ::= <subdomain> | " "
|
||||
* <subdomain> ::= <label> | <subdomain> "." <label>
|
||||
* <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
|
||||
* <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
|
||||
* <let-dig-hyp> ::= <let-dig> | "-"
|
||||
* <let-dig> ::= <letter> | <digit>
|
||||
*
|
||||
* Which is equivalent to the following:
|
||||
*
|
||||
* <domain> ::= <subdomain> | " "
|
||||
* <subdomain> ::= <label> ("." <label>)*
|
||||
* <label> ::= <letter> [ [ <let-dig-hyp>+ ] <let-dig> ]
|
||||
* <let-dig-hyp> ::= <let-dig> | "-"
|
||||
* <let-dig> ::= <letter> | <digit>
|
||||
*
|
||||
* I.e. a valid name consists of a dot-separated sequence
|
||||
* of one or more labels consisting of the following rules:
|
||||
*
|
||||
* - Each label is three characters or more
|
||||
* - Each label begins with a letter
|
||||
* - Each label ends with a letter or digit
|
||||
* - Each label contains only letters, digits or hyphens
|
||||
*
|
||||
* In addition we require the following:
|
||||
*
|
||||
* - All letters are lowercase
|
||||
* - Length is between (inclusive) GRAPHENE_MIN_ACCOUNT_NAME_LENGTH and GRAPHENE_MAX_ACCOUNT_NAME_LENGTH
|
||||
*/
|
||||
|
||||
bool is_valid_name( const string& name )
|
||||
{
|
||||
if( name.size() < GRAPHENE_MIN_ACCOUNT_NAME_LENGTH )
|
||||
const size_t len = name.size();
|
||||
if( len < GRAPHENE_MIN_ACCOUNT_NAME_LENGTH )
|
||||
return false;
|
||||
|
||||
if( name.size() > GRAPHENE_MAX_ACCOUNT_NAME_LENGTH )
|
||||
if( len > GRAPHENE_MAX_ACCOUNT_NAME_LENGTH )
|
||||
return false;
|
||||
|
||||
if( !isalpha( name.front() ) )
|
||||
return false;
|
||||
|
||||
if( !isalpha( name.back() ) && !isdigit( name.back() ) )
|
||||
return false;
|
||||
|
||||
for( size_t i = 0; i < name.size(); ++i )
|
||||
size_t begin = 0;
|
||||
while( true )
|
||||
{
|
||||
const auto c = name.at( i );
|
||||
|
||||
if( isalpha( c ) && islower( c ) )
|
||||
continue;
|
||||
|
||||
if( isdigit( c ) )
|
||||
continue;
|
||||
|
||||
if( c == '.' )
|
||||
{
|
||||
const auto next = name.at( i + 1 );
|
||||
if( !isalpha( next ) )
|
||||
size_t end = name.find_first_of( '.', begin );
|
||||
if( end == std::string::npos )
|
||||
end = len;
|
||||
if( end - begin < 3 )
|
||||
return false;
|
||||
switch( name[begin] )
|
||||
{
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
||||
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
|
||||
case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':
|
||||
case 'y': case 'z':
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch( name[end-1] )
|
||||
{
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
||||
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
|
||||
case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':
|
||||
case 'y': case 'z':
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
|
||||
case '8': case '9':
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
for( size_t i=begin+1; i<end-1; i++ )
|
||||
{
|
||||
switch( name[i] )
|
||||
{
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
||||
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
|
||||
case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':
|
||||
case 'y': case 'z':
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
|
||||
case '8': case '9':
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if( c == '-' )
|
||||
{
|
||||
const auto next = name.at( i + 1 );
|
||||
if( !isalpha( next ) && !isdigit( next ) )
|
||||
return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( end == len )
|
||||
break;
|
||||
begin = end+1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,10 +36,11 @@ void witness_plugin::plugin_set_program_options(
|
|||
boost::program_options::options_description& config_file_options)
|
||||
{
|
||||
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
|
||||
string witness_id_example = fc::json::to_string(chain::witness_id_type());
|
||||
command_line_options.add_options()
|
||||
("enable-stale-production", bpo::bool_switch()->notifier([this](bool e){_production_enabled = e;}), "Enable block production, even if the chain is stale")
|
||||
("witness-id,w", bpo::value<vector<string>>()->composing()->multitoken(),
|
||||
"ID of witness controlled by this node (e.g. \"1.7.0\", quotes are required, may specify multiple times)")
|
||||
("ID of witness controlled by this node (e.g. " + witness_id_example + ", quotes are required, may specify multiple times)").c_str())
|
||||
("private-key", bpo::value<vector<string>>()->composing()->multitoken()->
|
||||
DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
|
||||
"Tuple of [PublicKey, WIF private key] (may specify multiple times)")
|
||||
|
|
@ -56,9 +57,9 @@ void witness_plugin::plugin_initialize(const boost::program_options::variables_m
|
|||
{ try {
|
||||
_options = &options;
|
||||
LOAD_VALUE_SET(options, "witness-id", _witnesses, chain::witness_id_type)
|
||||
|
||||
if( options.count("private-key") )
|
||||
{
|
||||
|
||||
if( options.count("private-key") )
|
||||
{
|
||||
const std::vector<std::string> key_id_to_wif_pair_strings = options["private-key"].as<std::vector<std::string>>();
|
||||
for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ database_fixture::database_fixture()
|
|||
fc::raw::pack(enc, delegate_priv_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
auto secret = secret_hash_type::hash(enc.result());
|
||||
for( int i = 0; i < 10; ++i )
|
||||
genesis_state.initial_active_witnesses = 10;
|
||||
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
|
||||
{
|
||||
auto name = "init"+fc::to_string(i);
|
||||
genesis_state.initial_accounts.emplace_back(name,
|
||||
|
|
|
|||
|
|
@ -46,84 +46,87 @@ BOOST_FIXTURE_TEST_SUITE( basic_tests, database_fixture )
|
|||
*/
|
||||
BOOST_AUTO_TEST_CASE( valid_name_test )
|
||||
{
|
||||
BOOST_REQUIRE( is_valid_name( "a" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "A" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "0" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "." ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "-" ) );
|
||||
BOOST_CHECK( !is_valid_name( "a" ) );
|
||||
BOOST_CHECK( !is_valid_name( "A" ) );
|
||||
BOOST_CHECK( !is_valid_name( "0" ) );
|
||||
BOOST_CHECK( !is_valid_name( "." ) );
|
||||
BOOST_CHECK( !is_valid_name( "-" ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_name( "aa" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aA" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "a0" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "a." ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "a-" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aa" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aA" ) );
|
||||
BOOST_CHECK( !is_valid_name( "a0" ) );
|
||||
BOOST_CHECK( !is_valid_name( "a." ) );
|
||||
BOOST_CHECK( !is_valid_name( "a-" ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_name( "aaa" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aAa" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "a0a" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "a.a" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "a-a" ) );
|
||||
BOOST_CHECK( is_valid_name( "aaa" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aAa" ) );
|
||||
BOOST_CHECK( is_valid_name( "a0a" ) );
|
||||
BOOST_CHECK( !is_valid_name( "a.a" ) );
|
||||
BOOST_CHECK( is_valid_name( "a-a" ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_name( "aa0" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aA0" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "a00" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "a.0" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "a-0" ) );
|
||||
BOOST_CHECK( is_valid_name( "aa0" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aA0" ) );
|
||||
BOOST_CHECK( is_valid_name( "a00" ) );
|
||||
BOOST_CHECK( !is_valid_name( "a.0" ) );
|
||||
BOOST_CHECK( is_valid_name( "a-0" ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_name( "aaa-bbb-1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa-bbb.1" ) );
|
||||
BOOST_CHECK( is_valid_name( "aaa-bbb-ccc" ) );
|
||||
BOOST_CHECK( is_valid_name( "aaa-bbb.ccc" ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa,bbb-1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa_bbb-1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa-BBB-1" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa,bbb-ccc" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa_bbb-ccc" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa-BBB-ccc" ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_name( "1aaa-bbb" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "-aaa-bbb-1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( ".aaa-bbb-1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "/aaa-bbb-1" ) );
|
||||
BOOST_CHECK( !is_valid_name( "1aaa-bbb" ) );
|
||||
BOOST_CHECK( !is_valid_name( "-aaa-bbb-ccc" ) );
|
||||
BOOST_CHECK( !is_valid_name( ".aaa-bbb-ccc" ) );
|
||||
BOOST_CHECK( !is_valid_name( "/aaa-bbb-ccc" ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa-bbb-1-" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa-bbb-1." ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa-bbb-1/" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa-bbb-ccc-" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa-bbb-ccc." ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa-bbb-ccc.." ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa-bbb-ccc/" ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa..bbb-1" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "aaa.bbb-1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa.bbb.1" ) );
|
||||
BOOST_CHECK( !is_valid_name( "aaa..bbb-ccc" ) );
|
||||
BOOST_CHECK( is_valid_name( "aaa.bbb-ccc" ) );
|
||||
BOOST_CHECK( is_valid_name( "aaa.bbb.ccc" ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_name( "aaa--bbb--1" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "xn--sandmnnchen-p8a.de" ) );
|
||||
BOOST_REQUIRE( is_valid_name( "xn-sandmnnchen-p8a.de" ) );
|
||||
BOOST_CHECK( is_valid_name( "aaa--bbb--ccc" ) );
|
||||
BOOST_CHECK( !is_valid_name( "xn--sandmnnchen-p8a.de" ) );
|
||||
BOOST_CHECK( is_valid_name( "xn--sandmnnchen-p8a.dex" ) );
|
||||
BOOST_CHECK( !is_valid_name( "xn-sandmnnchen-p8a.de" ) );
|
||||
BOOST_CHECK( is_valid_name( "xn-sandmnnchen-p8a.dex" ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_name( "this-label-has-less-than-64-char.acters-63-to-be-really-precise" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "this-label-has-more-than-63-char.act.ers-64-to-be-really-precise" ) );
|
||||
BOOST_REQUIRE( !is_valid_name( "none.of.these.labels.has.more.than-63.chars--but.still.not.valid" ) );
|
||||
BOOST_CHECK( is_valid_name( "this-label-has-less-than-64-char.acters-63-to-be-really-precise" ) );
|
||||
BOOST_CHECK( !is_valid_name( "this-label-has-more-than-63-char.act.ers-64-to-be-really-precise" ) );
|
||||
BOOST_CHECK( !is_valid_name( "none.of.these.labels.has.more.than-63.chars--but.still.not.valid" ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( valid_symbol_test )
|
||||
{
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "a" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "0" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "." ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "a" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "0" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "." ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_symbol( "AA" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "Aa" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A0" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A." ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "AA" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "Aa" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A0" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A." ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_symbol( "AAA" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "AaA" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A0A" ) );
|
||||
BOOST_REQUIRE( is_valid_symbol( "A.A" ) );
|
||||
BOOST_CHECK( is_valid_symbol( "AAA" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "AaA" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A0A" ) );
|
||||
BOOST_CHECK( is_valid_symbol( "A.A" ) );
|
||||
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A..A" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A.A." ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A.A.A" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A..A" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A.A." ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A.A.A" ) );
|
||||
|
||||
BOOST_REQUIRE( is_valid_symbol( "AAAAAAAAAAAAAAAA" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "AAAAAAAAAAAAAAAAA" ) );
|
||||
BOOST_REQUIRE( is_valid_symbol( "A.AAAAAAAAAAAAAA" ) );
|
||||
BOOST_REQUIRE( !is_valid_symbol( "A.AAAAAAAAAAAA.A" ) );
|
||||
BOOST_CHECK( is_valid_symbol( "AAAAAAAAAAAAAAAA" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "AAAAAAAAAAAAAAAAA" ) );
|
||||
BOOST_CHECK( is_valid_symbol( "A.AAAAAAAAAAAAAA" ) );
|
||||
BOOST_CHECK( !is_valid_symbol( "A.AAAAAAAAAAAA.A" ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( price_test )
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ genesis_state_type make_genesis() {
|
|||
fc::raw::pack(enc, delegate_priv_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
auto secret = secret_hash_type::hash(enc.result());
|
||||
for( int i = 0; i < 10; ++i )
|
||||
genesis_state.initial_active_witnesses = 10;
|
||||
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
|
||||
{
|
||||
auto name = "init"+fc::to_string(i);
|
||||
genesis_state.initial_accounts.emplace_back(name,
|
||||
|
|
@ -584,19 +585,19 @@ BOOST_FIXTURE_TEST_CASE( limit_order_expiration, database_fixture )
|
|||
BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture )
|
||||
{ try {
|
||||
generate_block();
|
||||
const auto& from = account_id_type()(db);
|
||||
ACTOR(to);
|
||||
const auto& alice = account_id_type()(db);
|
||||
ACTOR(bob);
|
||||
asset amount(1000);
|
||||
|
||||
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
||||
trx.operations.push_back(transfer_operation({ asset(), from.id, to.id, amount, memo_data() }));
|
||||
trx.operations.push_back(transfer_operation({ asset(), alice.id, bob.id, amount, memo_data() }));
|
||||
for( auto& op : trx.operations ) op.visit(operation_set_fee(db.current_fee_schedule()));
|
||||
trx.validate();
|
||||
|
||||
db.push_transaction(trx, ~0);
|
||||
|
||||
trx.operations.clear();
|
||||
trx.operations.push_back(transfer_operation({ asset(), to.id, from.id, amount, memo_data() }));
|
||||
trx.operations.push_back(transfer_operation({ asset(), bob.id, alice.id, amount, memo_data() }));
|
||||
for( auto& op : trx.operations ) op.visit(operation_set_fee(db.current_fee_schedule()));
|
||||
trx.validate();
|
||||
|
||||
|
|
@ -604,8 +605,8 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture )
|
|||
BOOST_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Verify that double-signing causes an exception" );
|
||||
trx.sign(to_private_key);
|
||||
trx.sign(to_private_key);
|
||||
trx.sign(bob_private_key);
|
||||
trx.sign(bob_private_key);
|
||||
BOOST_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Verify that signing with an extra, unused key fails" );
|
||||
|
|
@ -616,7 +617,7 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture )
|
|||
BOOST_TEST_MESSAGE( "Verify that signing once with the proper key passes" );
|
||||
trx.signatures.pop_back();
|
||||
db.push_transaction(trx, 0);
|
||||
trx.sign(to_private_key);
|
||||
trx.sign(bob_private_key);
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
|
|
|
|||
|
|
@ -981,6 +981,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
|
|||
fc::temp_directory td;
|
||||
genesis_state.initial_balances.push_back({generate_private_key("n").get_public_key(), GRAPHENE_SYMBOL, 1});
|
||||
genesis_state.initial_balances.push_back({generate_private_key("x").get_public_key(), GRAPHENE_SYMBOL, 1});
|
||||
// genesis_state.initial_vesting_balances.push_back({generate_private_key("v").get_public_key(), GRAPHENE_SYMBOL, 1});
|
||||
// TODO: vesting genesis balances
|
||||
genesis_state.initial_accounts.emplace_back("n", generate_private_key("n").get_public_key());
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue