Merge branch 'master' into fee_refactor

This commit is contained in:
Daniel Larimer 2015-07-09 08:43:45 -04:00
commit 786c65d4b4
47 changed files with 1084 additions and 466 deletions

3
.gitignore vendored
View file

@ -33,3 +33,6 @@ witness_node_data_dir
programs/witness_node/object_database/*
object_database/*
*.pyc
*.pyo

View file

@ -118,15 +118,38 @@ API 0 is accessible using regular JSON-RPC:
$ curl --data '{"jsonrpc": "2.0", "method": "get_accounts", "params": [["1.2.0"]], "id": 1}' http://127.0.0.1:8090/rpc
You can use the login API to obtain `network`, `database` and `history` API's. Here is an example of how to call `add_node` from the `network` API:
Accessing restricted API's
--------------------------
You can restrict API's to particular users by specifying an `apiaccess` file in `config.ini`. Here is an example `apiaccess` file which allows
user `bytemaster` with password `supersecret` to access four different API's:
{
"permission_map" :
[
[
"bytemaster",
{
"password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=",
"password_salt_b64" : "INDdM6iCi/8=",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api"]
}
]
]
}
Passwords are stored in `base64` as as salted `sha256` hashes. A simple Python script, `saltpass.py` is avaliable to obtain hash and salt values from a password.
A single asterisk `"*"` may be specified as username or password hash to accept any value.
With the above configuration, here is an example of how to call `add_node` from the `network_node` API:
{"id":1, "method":"call", "params":[1,"login",["bytemaster", "supersecret"]]}
{"id":2, "method":"call", "params":[1,"network",[]]}
{"id":2, "method":"call", "params":[1,"network_node",[]]}
{"id":3, "method":"call", "params":[2,"add_node",["127.0.0.1:9090"]]}
Note, the call to `network` is necessary to obtain the correct API identifier for the network API. It is not guaranteed that the network API identifier will always be `2`.
Note, the call to `network_node` is necessary to obtain the correct API identifier for the network API. It is not guaranteed that the network API identifier will always be `2`.
Since the `network` API requires login, it is only accessible over the websocket RPC. Our `doxygen` documentation contains the most up-to-date information
Since the `network_node` API requires login, it is only accessible over the websocket RPC. Our `doxygen` documentation contains the most up-to-date information
about API's for the [witness node](https://bitshares.github.io/doxygen/namespacegraphene_1_1app.html) and the
[wallet](https://bitshares.github.io/doxygen/classgraphene_1_1wallet_1_1wallet__api.html).
If you want information which is not available from an API, it might be available
@ -166,7 +189,15 @@ Questions
- Is there a way to generate help with parameter names and method descriptions?
Yes. Documentation of the code base, including APIs, can be generated using Doxygen. Simply run `doxygen` in this directory.
We are thinking of integrating Doxygen's XML output format to provide a better `help` command to the CLI wallet.
If both Doxygen and perl are available in your build environment, the CLI wallet's `help` and `gethelp`
commands will display help generated from the doxygen documentation.
If your CLI wallet's `help` command displays descriptions without parameter names like
`signed_transaction transfer(string, string, string, string, string, bool)`
it means CMake was unable to find Doxygen or perl during configuration. If found, the
output should look like this:
`signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast)`
- Is there a way to allow external program to drive `cli_wallet` via websocket, JSONRPC, or HTTP?

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/app/api.hpp>
#include <graphene/app/api_access.hpp>
#include <graphene/app/application.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/utilities/key_conversion.hpp>
@ -350,21 +351,53 @@ namespace graphene { namespace app {
bool login_api::login(const string& user, const string& password)
{
auto db_api = std::make_shared<database_api>(std::ref(*_app.chain_database()));
auto net_api = std::make_shared<network_api>(std::ref(_app));
auto hist_api = std::make_shared<history_api>(_app);
_database_api = db_api;
_network_api = net_api;
_history_api = hist_api;
optional< api_access_info > acc = _app.get_api_access_info( user );
if( !acc.valid() )
return false;
if( acc->password_hash_b64 != "*" )
{
std::string password_salt = fc::base64_decode( acc->password_salt_b64 );
std::string acc_password_hash = fc::base64_decode( acc->password_hash_b64 );
fc::sha256 hash_obj = fc::sha256::hash( password + password_salt );
if( hash_obj.data_size() != acc_password_hash.length() )
return false;
if( memcmp( hash_obj.data(), acc_password_hash.c_str(), hash_obj.data_size() ) != 0 )
return false;
}
for( const std::string& api_name : acc->allowed_apis )
enable_api( api_name );
return true;
}
network_api::network_api(application& a):_app(a)
void login_api::enable_api( const std::string& api_name )
{
if( api_name == "database_api" )
{
_database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ) );
}
else if( api_name == "network_broadcast_api" )
{
_network_broadcast_api = std::make_shared< network_broadcast_api >( std::ref( _app ) );
}
else if( api_name == "history_api" )
{
_history_api = std::make_shared< history_api >( _app );
}
else if( api_name == "network_node_api" )
{
_network_node_api = std::make_shared< network_node_api >( std::ref(_app) );
}
return;
}
network_broadcast_api::network_broadcast_api(application& a):_app(a)
{
_applied_block_connection = _app.chain_database()->applied_block.connect([this](const signed_block& b){ on_applied_block(b); });
}
void network_api::on_applied_block( const signed_block& b )
void network_broadcast_api::on_applied_block( const signed_block& b )
{
if( _callbacks.size() )
{
@ -382,18 +415,14 @@ namespace graphene { namespace app {
}
}
void network_api::add_node(const fc::ip::endpoint& ep)
{
_app.p2p_node()->add_node(ep);
}
void network_api::broadcast_transaction(const signed_transaction& trx)
void network_broadcast_api::broadcast_transaction(const signed_transaction& trx)
{
trx.validate();
_app.chain_database()->push_transaction(trx);
_app.p2p_node()->broadcast_transaction(trx);
}
void network_api::broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx)
void network_broadcast_api::broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx)
{
trx.validate();
_callbacks[trx.id()] = cb;
@ -401,16 +430,30 @@ namespace graphene { namespace app {
_app.p2p_node()->broadcast_transaction(trx);
}
network_node_api::network_node_api( application& a ) : _app( a )
{
}
std::vector<net::peer_status> network_api::get_connected_peers() const
void network_node_api::add_node(const fc::ip::endpoint& ep)
{
_app.p2p_node()->add_node(ep);
}
std::vector<net::peer_status> network_node_api::get_connected_peers() const
{
return _app.p2p_node()->get_connected_peers();
}
fc::api<network_api> login_api::network()const
fc::api<network_broadcast_api> login_api::network_broadcast()const
{
FC_ASSERT(_network_api);
return *_network_api;
FC_ASSERT(_network_broadcast_api);
return *_network_broadcast_api;
}
fc::api<network_node_api> login_api::network_node()const
{
FC_ASSERT(_network_node_api);
return *_network_node_api;
}
fc::api<database_api> login_api::database()const

View file

@ -15,9 +15,10 @@
* 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 <graphene/app/api.hpp>
#include <graphene/app/api_access.hpp>
#include <graphene/app/application.hpp>
#include <graphene/app/plugin.hpp>
#include <graphene/app/api.hpp>
#include <graphene/net/core_messages.hpp>
@ -62,6 +63,9 @@ namespace detail {
genesis_state_type initial_state;
initial_state.initial_parameters.current_fees = fee_schedule::get_default();//->set_all_fees(GRAPHENE_BLOCKCHAIN_PRECISION);
initial_state.initial_active_witnesses = 10;
initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() /
initial_state.initial_parameters.block_interval *
initial_state.initial_parameters.block_interval);
for( int i = 0; i < initial_state.initial_active_witnesses; ++i )
{
auto name = "init"+fc::to_string(i);
@ -181,12 +185,14 @@ namespace detail {
bool clean = !fc::exists(_data_dir / "blockchain/dblock");
fc::create_directories(_data_dir / "blockchain/dblock");
genesis_state_type initial_state;
if( _options->count("genesis-json") )
initial_state = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
.as<genesis_state_type>();
else
initial_state = create_example_genesis();
auto initial_state = [&] {
ilog("Initializing database...");
if( _options->count("genesis-json") )
return fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
.as<genesis_state_type>();
else
return create_example_genesis();
};
if( _options->count("resync-blockchain") )
_chain_db->wipe(_data_dir / "blockchain", true);
@ -194,12 +200,29 @@ namespace detail {
if( _options->count("replay-blockchain") )
{
ilog("Replaying blockchain on user request.");
_chain_db->reindex(_data_dir/"blockchain", initial_state);
_chain_db->reindex(_data_dir/"blockchain", initial_state());
} else if( clean )
_chain_db->open(_data_dir / "blockchain", initial_state);
else {
wlog("Detected unclean shutdown. Replaying blockchain...");
_chain_db->reindex(_data_dir / "blockchain", initial_state);
_chain_db->reindex(_data_dir / "blockchain", initial_state());
}
if( _options->count("apiaccess") )
_apiaccess = fc::json::from_file( _options->at("apiaccess").as<boost::filesystem::path>() )
.as<api_access>();
else
{
// TODO: Remove this generous default access policy
// when the UI logs in properly
_apiaccess = api_access();
api_access_info wild_access;
wild_access.password_hash_b64 = "*";
wild_access.password_salt_b64 = "*";
wild_access.allowed_apis.push_back( "database_api" );
wild_access.allowed_apis.push_back( "network_broadcast_api" );
wild_access.allowed_apis.push_back( "history_api" );
_apiaccess.permission_map["*"] = wild_access;
}
reset_p2p_node(_data_dir);
@ -207,10 +230,23 @@ namespace detail {
reset_websocket_tls_server();
} FC_CAPTURE_AND_RETHROW() }
optional< api_access_info > get_api_access_info(const string& username)const
{
optional< api_access_info > result;
auto it = _apiaccess.permission_map.find(username);
if( it == _apiaccess.permission_map.end() )
{
it = _apiaccess.permission_map.find("*");
if( it == _apiaccess.permission_map.end() )
return result;
}
return it->second;
}
/**
* If delegate has the item, the network has no need to fetch it.
*/
virtual bool has_item( const net::item_id& id ) override
virtual bool has_item(const net::item_id& id) override
{
try
{
@ -230,7 +266,7 @@ namespace detail {
*
* @throws exception if error validating the item, otherwise the item is safe to broadcast on.
*/
virtual bool handle_block( const graphene::net::block_message& blk_msg, bool sync_mode ) override
virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode) override
{ try {
ilog("Got block #${n} from network", ("n", blk_msg.block.block_num()));
try {
@ -241,7 +277,7 @@ namespace detail {
}
} FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) }
virtual bool handle_transaction( const graphene::net::trx_message& trx_msg, bool sync_mode ) override
virtual bool handle_transaction(const graphene::net::trx_message& trx_msg, bool sync_mode) override
{ try {
ilog("Got transaction from network");
_chain_db->push_transaction( trx_msg.trx );
@ -296,18 +332,18 @@ namespace detail {
/**
* Given the hash of the requested data, fetch the body.
*/
virtual message get_item( const item_id& id ) override
virtual message get_item(const item_id& id) override
{ try {
ilog("Request for item ${id}", ("id", id));
if( id.item_type == graphene::net::block_message_type )
{
auto opt_block = _chain_db->fetch_block_by_id( id.item_hash );
auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);
if( !opt_block )
elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}",
("id", id.item_hash)("id2", _chain_db->get_block_id_for_num(block_header::num_from_id(id.item_hash))));
FC_ASSERT( opt_block.valid() );
ilog("Serving up block #${num}", ("num", opt_block->block_num()));
return block_message( std::move(*opt_block) );
return block_message(std::move(*opt_block));
}
return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );
} FC_CAPTURE_AND_RETHROW( (id) ) }
@ -332,18 +368,18 @@ namespace detail {
* &c.
* the last item in the list will be the hash of the most recent block on our preferred chain
*/
virtual std::vector<item_hash_t> get_blockchain_synopsis( uint32_t item_type,
const graphene::net::item_hash_t& reference_point,
uint32_t number_of_blocks_after_reference_point ) override
virtual std::vector<item_hash_t> get_blockchain_synopsis(uint32_t item_type,
const graphene::net::item_hash_t& reference_point,
uint32_t number_of_blocks_after_reference_point) override
{ try {
std::vector<item_hash_t> result;
result.reserve(30);
auto head_block_num = _chain_db->head_block_num();
result.push_back( _chain_db->head_block_id() );
result.push_back(_chain_db->head_block_id());
auto current = 1;
while( current < head_block_num )
{
result.push_back( _chain_db->get_block_id_for_num( head_block_num - current ) );
result.push_back(_chain_db->get_block_id_for_num(head_block_num - current));
current = current*2;
}
std::reverse( result.begin(), result.end() );
@ -358,7 +394,7 @@ namespace detail {
* @param item_count the number of items known to the node that haven't been sent to handle_item() yet.
* After `item_count` more calls to handle_item(), the node will be in sync
*/
virtual void sync_status( uint32_t item_type, uint32_t item_count ) override
virtual void sync_status(uint32_t item_type, uint32_t item_count) override
{
// any status reports to GUI go here
}
@ -366,7 +402,7 @@ namespace detail {
/**
* Call any time the number of connected peers changes.
*/
virtual void connection_count_changed( uint32_t c ) override
virtual void connection_count_changed(uint32_t c) override
{
// any status reports to GUI go here
}
@ -412,6 +448,7 @@ namespace detail {
fc::path _data_dir;
const bpo::variables_map* _options = nullptr;
api_access _apiaccess;
std::shared_ptr<graphene::chain::database> _chain_db;
std::shared_ptr<graphene::net::node> _p2p_network;
@ -451,6 +488,7 @@ void application::set_program_options(boost::program_options::options_descriptio
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
("apiaccess", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
;
command_line_options.add(configuration_file_options);
command_line_options.add_options()
@ -521,6 +559,11 @@ void application::set_block_production(bool producing_blocks)
my->_is_block_producer = producing_blocks;
}
optional< api_access_info > application::get_api_access_info( const string& username )const
{
return my->get_api_access_info( username );
}
void graphene::app::application::add_plugin(const string& name, std::shared_ptr<graphene::app::abstract_plugin> p)
{
my->_plugins[name] = p;

View file

@ -28,7 +28,6 @@
#include <graphene/chain/balance_object.hpp>
#include <graphene/net/node.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <fc/api.hpp>
@ -44,7 +43,7 @@ namespace graphene { namespace app {
*
* This API exposes accessors on the database which query state tracked by a blockchain validating node. This API is
* read-only; all modifications to the database must be performed via transactions. Transactions are broadcast via
* the @ref network_api.
* the @ref network_broadcast_api.
*/
class database_api
{
@ -331,14 +330,12 @@ namespace graphene { namespace app {
};
/**
* @brief The network_api class implements the RPC API for the network
*
* This API has methods to query the network status, connect to new peers, and send transactions.
* @brief The network_broadcast_api class allows broadcasting of transactions.
*/
class network_api
class network_broadcast_api
{
public:
network_api(application& a);
network_broadcast_api(application& a);
struct transaction_confirmation
{
@ -366,15 +363,13 @@ namespace graphene { namespace app {
void broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx);
/**
* @brief add_node Connect to a new peer
* @param ep The IP/Port of the peer to connect to
* @brief Not reflected, thus not accessible to API clients.
*
* This function is registered to receive the applied_block
* signal from the chain database when a block is received.
* It then dispatches callbacks to clients who have requested
* to be notified when a particular txid is included in a block.
*/
void add_node(const fc::ip::endpoint& ep);
/**
* @brief Get status of all current connections to peers
*/
std::vector<net::peer_status> get_connected_peers() const;
void on_applied_block( const signed_block& b );
private:
boost::signals2::scoped_connection _applied_block_connection;
@ -382,6 +377,35 @@ namespace graphene { namespace app {
application& _app;
};
/**
* @brief The network_node_api class allows maintenance of p2p connections.
*/
class network_node_api
{
public:
network_node_api(application& a);
/**
* @brief add_node Connect to a new peer
* @param ep The IP/Port of the peer to connect to
*/
void add_node(const fc::ip::endpoint& ep);
/**
* @brief Get status of all current connections to peers
* @brief Not reflected, thus not accessible to API clients.
*
* This function is registered to receive the applied_block
* signal from the chain database when a block is received.
* It then dispatches callbacks to clients who have requested
* to be notified when a particular txid is included in a block.
*/
std::vector<net::peer_status> get_connected_peers() const;
private:
application& _app;
};
/**
* @brief The login_api class implements the bottom layer of the RPC API
*
@ -403,23 +427,29 @@ namespace graphene { namespace app {
* has sucessfully authenticated.
*/
bool login(const string& user, const string& password);
/// @brief Retrieve the network API
fc::api<network_api> network()const;
/// @brief Retrieve the network broadcast API
fc::api<network_broadcast_api> network_broadcast()const;
/// @brief Retrieve the database API
fc::api<database_api> database()const;
/// @brief Retrieve the history API
fc::api<history_api> history()const;
/// @brief Retrieve the network node API
fc::api<network_node_api> network_node()const;
private:
/// @brief Called to enable an API, not reflected.
void enable_api( const string& api_name );
application& _app;
optional< fc::api<database_api> > _database_api;
optional< fc::api<network_api> > _network_api;
optional< fc::api<network_broadcast_api> > _network_broadcast_api;
optional< fc::api<network_node_api> > _network_node_api;
optional< fc::api<history_api> > _history_api;
};
}} // graphene::app
FC_REFLECT( graphene::app::network_api::transaction_confirmation,
FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation,
(id)(block_num)(trx_num)(trx) )
FC_API(graphene::app::database_api,
@ -460,13 +490,23 @@ FC_API(graphene::app::database_api,
(get_margin_positions)
(get_balance_objects)
)
FC_API(graphene::app::history_api, (get_account_history)(get_market_history)(get_market_history_buckets))
FC_API(graphene::app::network_api, (broadcast_transaction)(broadcast_transaction_with_callback)
/* (add_node)(get_connected_peers) */
)
FC_API(graphene::app::history_api,
(get_account_history)
(get_market_history)
(get_market_history_buckets)
)
FC_API(graphene::app::network_broadcast_api,
(broadcast_transaction)
(broadcast_transaction_with_callback)
)
FC_API(graphene::app::network_node_api,
(add_node)
(get_connected_peers)
)
FC_API(graphene::app::login_api,
(login)
(network)
(network_broadcast)
(database)
(history)
(network_node)
)

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* 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.
*/
#pragma once
#include <fc/reflect/reflect.hpp>
#include <map>
#include <string>
#include <vector>
namespace graphene { namespace app {
struct api_access_info
{
std::string password_hash_b64;
std::string password_salt_b64;
std::vector< std::string > allowed_apis;
};
struct api_access
{
std::map< std::string, api_access_info > permission_map;
};
} } // graphene::app
FC_REFLECT( graphene::app::api_access_info,
(password_hash_b64)
(password_salt_b64)
(allowed_apis)
)
FC_REFLECT( graphene::app::api_access,
(permission_map)
)

View file

@ -17,6 +17,7 @@
*/
#pragma once
#include <graphene/app/api_access.hpp>
#include <graphene/net/node.hpp>
#include <graphene/chain/database.hpp>
@ -74,6 +75,7 @@ namespace graphene { namespace app {
std::shared_ptr<chain::database> chain_database()const;
void set_block_production(bool producing_blocks);
fc::optional< api_access_info > get_api_access_info( const string& username )const;
private:
void add_plugin( const string& name, std::shared_ptr<abstract_plugin> p );

View file

@ -20,6 +20,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <functional>
@ -393,6 +394,8 @@ void_result asset_settle_evaluator::do_evaluate(const asset_settle_evaluator::op
FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() );
if( bitasset.is_prediction_market )
FC_ASSERT( bitasset.has_settlement(), "global settlement must occur before force settling a prediction market" );
else if( bitasset.current_feed.settlement_price.is_null() )
FC_THROW_EXCEPTION(insufficient_feeds, "Cannot force settle with no price feed.");
FC_ASSERT(d.get_balance(d.get(op.account), *asset_to_settle) >= op.amount);
return void_result();

View file

@ -7,27 +7,38 @@ void_result balance_claim_evaluator::do_evaluate(const balance_claim_operation&
database& d = db();
balance = &op.balance_to_claim(d);
FC_ASSERT(op.balance_owner_key == balance->owner ||
GRAPHENE_ASSERT(
op.balance_owner_key == balance->owner ||
pts_address(op.balance_owner_key, false, 56) == balance->owner ||
pts_address(op.balance_owner_key, true, 56) == balance->owner ||
pts_address(op.balance_owner_key, false, 0) == balance->owner ||
pts_address(op.balance_owner_key, true, 0) == balance->owner,
"balance_owner_key does not match balance's owner");
balance_claim_owner_mismatch,
"Balance owner key was specified as '${op}' but balance's actual owner is '${bal}'",
("op", op.balance_owner_key)
("bal", balance->owner)
);
if( !(d.get_node_properties().skip_flags & (database::skip_authority_check |
database::skip_transaction_signatures)) )
FC_ASSERT(op.total_claimed.asset_id == balance->asset_type());
if( balance->is_vesting_balance() ) {
if( !balance->vesting_policy->is_withdraw_allowed({balance->balance,
d.head_block_time(),
op.total_claimed}) )
FC_THROW_EXCEPTION(invalid_claim_amount,
"Attempted to claim ${c} from a vesting balance with ${a} available",
("c", op.total_claimed)("a", balance->available(d.head_block_time())));
if( d.head_block_time() - balance->last_claim_date < fc::days(1) )
FC_THROW_EXCEPTION(balance_claimed_too_often,
"Genesis vesting balances may not be claimed more than once per day.");
if( balance->is_vesting_balance() )
{
GRAPHENE_ASSERT(
balance->vesting_policy->is_withdraw_allowed(
{ balance->balance,
d.head_block_time(),
op.total_claimed } ),
balance_claim_invalid_claim_amount,
"Attempted to claim ${c} from a vesting balance with ${a} available",
("c", op.total_claimed)("a", balance->available(d.head_block_time()))
);
GRAPHENE_ASSERT(
d.head_block_time() - balance->last_claim_date >= fc::days(1),
balance_claim_claimed_too_often,
"Genesis vesting balances may not be claimed more than once per day."
);
return {};
}

View file

@ -193,6 +193,9 @@ void database::initialize_indexes()
void database::init_genesis(const genesis_state_type& genesis_state)
{ try {
FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." );
FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % GRAPHENE_DEFAULT_BLOCK_INTERVAL == 0,
"Genesis timestamp must be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL." );
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(),
@ -300,8 +303,8 @@ void database::init_genesis(const genesis_state_type& genesis_state)
p.parameters.current_fees->zero_all_fees();
});
create<dynamic_global_property_object>( [&](dynamic_global_property_object& p) {
p.time = fc::time_point_sec(GRAPHENE_GENESIS_TIMESTAMP);
create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
p.time = genesis_state.initial_timestamp;
p.witness_budget = 0;
});
create<block_summary_object>([&](block_summary_object&) {});
@ -313,13 +316,16 @@ void database::init_genesis(const genesis_state_type& genesis_state)
cop.name = account.name;
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
cop.owner = authority(1, account.owner_key, 1);
if( account.owner_key != account.active_key )
if( account.active_key == public_key_type() )
{
cop.active = authority(1, account.owner_key, 1);
} else {
cop.active = cop.owner;
cop.options.memo_key = account.owner_key;
}
else
{
cop.active = authority(1, account.active_key, 1);
cop.options.memo_key = account.active_key;
}
cop.options.memo_key = account.owner_key;
account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());
if( account.is_lifetime_member )
@ -457,31 +463,45 @@ void database::init_genesis(const genesis_state_type& genesis_state)
adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));
}
// Create initial witnesses and delegates
// Create initial witnesses
std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(),
[&](const genesis_state_type::initial_witness_type& witness) {
witness_create_operation op;
op.witness_account = get_account_id(witness.owner_name);
op.block_signing_key = witness.block_signing_key;
op.initial_secret = secret_hash_type::hash( secret_hash_type() );
op.witness_account = get_account_id(witness.owner_name);
apply_operation(genesis_eval_state, op);
});
// Create initial committee members
std::for_each(genesis_state.initial_committee_candidates.begin(), genesis_state.initial_committee_candidates.end(),
[&](const genesis_state_type::initial_committee_member_type& member) {
delegate_create_operation op;
op.delegate_account = get_account_id(member.owner_name);
apply_operation(genesis_eval_state, op).get<object_id_type>();
apply_operation(genesis_eval_state, op);
});
// Create initial workers
std::for_each(genesis_state.initial_worker_candidates.begin(), genesis_state.initial_worker_candidates.end(),
[&](const genesis_state_type::initial_worker_type& worker)
{
worker_create_operation op;
op.owner = get_account_id(worker.owner_name);
op.work_begin_date = genesis_state.initial_timestamp;
op.work_end_date = time_point_sec::maximum();
op.daily_pay = worker.daily_pay;
op.name = "Genesis-Worker-" + worker.owner_name;
op.initializer = vesting_balance_worker_initializer{uint16_t(0)};
apply_operation(genesis_eval_state, std::move(op));
});
// Set active witnesses
modify(get_global_properties(), [&](global_property_object& p) {
auto idx = get_index_type<witness_index>().indices();
for( auto itr = idx.begin();
itr != idx.end() && p.active_witnesses.size() < genesis_state.initial_active_witnesses;
++itr )
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
{
p.active_witnesses.insert(itr->id);
p.witness_accounts.insert(itr->witness_account);
p.active_witnesses.insert(i);
p.witness_accounts.insert(get(witness_id_type(i)).witness_account);
}
});

View file

@ -44,7 +44,11 @@ vector<std::reference_wrapper<const typename Index::object_type>> database::sort
[](const ObjectType& o) { return std::cref(o); });
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
[this](const ObjectType& a, const ObjectType& b)->bool {
return _vote_tally_buffer[a.vote_id] > _vote_tally_buffer[b.vote_id];
share_type oa_vote = _vote_tally_buffer[a.vote_id];
share_type ob_vote = _vote_tally_buffer[b.vote_id];
if( oa_vote != ob_vote )
return oa_vote > ob_vote;
return a.vote_id < b.vote_id;
});
refs.resize(count, refs.front());
@ -127,9 +131,10 @@ void database::update_active_witnesses()
share_type stake_target = _total_voting_stake / 2;
share_type stake_tally = _witness_count_histogram_buffer[0];
size_t witness_count = 0;
while( (witness_count < _witness_count_histogram_buffer.size() - 1)
&& (stake_tally <= stake_target) )
stake_tally += _witness_count_histogram_buffer[++witness_count];
if( stake_target > 0 )
while( (witness_count < _witness_count_histogram_buffer.size() - 1)
&& (stake_tally <= stake_target) )
stake_tally += _witness_count_histogram_buffer[++witness_count];
auto wits = sort_votable_objects<witness_index>(std::max(witness_count*2+1, (size_t)GRAPHENE_MIN_WITNESS_COUNT));
const global_property_object& gpo = get_global_properties();
@ -192,9 +197,10 @@ void database::update_active_delegates()
uint64_t stake_target = _total_voting_stake / 2;
uint64_t stake_tally = _committee_count_histogram_buffer[0];
size_t delegate_count = 0;
while( (delegate_count < _committee_count_histogram_buffer.size() - 1)
&& (stake_tally <= stake_target) )
stake_tally += _committee_count_histogram_buffer[++delegate_count];
if( stake_target > 0 )
while( (delegate_count < _committee_count_histogram_buffer.size() - 1)
&& (stake_tally <= stake_target) )
stake_tally += _committee_count_histogram_buffer[++delegate_count];
auto delegates = sort_votable_objects<delegate_index>(std::max(delegate_count*2+1, (size_t)GRAPHENE_MIN_DELEGATE_COUNT));
@ -396,7 +402,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
uint32_t offset = id.instance();
// if they somehow managed to specify an illegal offset, ignore it.
if( offset < d._vote_tally_buffer.size() )
d._vote_tally_buffer[ offset ] += voting_stake;
d._vote_tally_buffer[offset] += voting_stake;
}
if( opinion_account.options.num_witness <= props.parameters.maximum_witness_count )
@ -409,7 +415,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// in particular, this takes care of the case where a
// member was voting for a high number, then the
// parameter was lowered.
d._witness_count_histogram_buffer[ offset ] += voting_stake;
d._witness_count_histogram_buffer[offset] += voting_stake;
}
if( opinion_account.options.num_committee <= props.parameters.maximum_committee_count )
{
@ -419,7 +425,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// are turned into votes for maximum_committee_count.
//
// same rationale as for witnesses
d._committee_count_histogram_buffer[ offset ] += voting_stake;
d._committee_count_histogram_buffer[offset] += voting_stake;
}
d._total_voting_stake += voting_stake;

View file

@ -21,6 +21,8 @@
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <functional>
namespace graphene { namespace chain {
database::database()
@ -34,31 +36,10 @@ database::~database(){
_pending_block_session->commit();
}
void database::open( const fc::path& data_dir, const genesis_state_type& initial_allocation )
{ try {
object_database::open( data_dir );
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
if( !find(global_property_id_type()) )
{
// ilog( "Init Genesis State" );
init_genesis(initial_allocation);
}
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
auto last_block= _block_id_to_block.last();
if( last_block.valid() )
_fork_db.start_block( *last_block );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
{ try {
wipe(data_dir, false);
open(data_dir, initial_allocation);
open(data_dir, [&initial_allocation]{return initial_allocation;});
auto start = fc::time_point::now();
auto last_block = _block_id_to_block.last();
@ -70,13 +51,13 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
//_undo_db.disable();
for( uint32_t i = 1; i <= last_block_num; ++i )
{
apply_block( *_block_id_to_block.fetch_by_number(i), skip_delegate_signature |
apply_block(*_block_id_to_block.fetch_by_number(i), skip_delegate_signature |
skip_transaction_signatures |
skip_undo_block |
skip_undo_transaction |
skip_transaction_dupe_check |
skip_tapos_check |
skip_authority_check );
skip_authority_check);
}
//_undo_db.enable();
auto end = fc::time_point::now();

View file

@ -23,9 +23,6 @@
namespace graphene { namespace chain {
static_assert((GRAPHENE_GENESIS_TIMESTAMP % GRAPHENE_DEFAULT_BLOCK_INTERVAL) == 0,
"GRAPHENE_GENESIS_TIMESTAMP must be aligned to a block interval.");
pair<witness_id_type, bool> database::get_scheduled_witness(uint32_t slot_num)const
{
if( slot_num == 0 )

View file

@ -17,7 +17,9 @@
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/delegate_object.hpp>
@ -88,14 +90,24 @@ database& generic_evaluator::db()const { return trx_state->db(); }
operation_get_required_authorities( op, active_auths, owner_auths, other_auths );
for( auto id : active_auths )
FC_ASSERT(verify_authority(id(db()), authority::active) ||
verify_authority(id(db()), authority::owner), "", ("id", id));
GRAPHENE_ASSERT(
verify_authority(id(db()), authority::active) ||
verify_authority(id(db()), authority::owner),
tx_missing_active_auth,
"missing required active authority ${id}", ("id", id));
for( auto id : owner_auths )
FC_ASSERT(verify_authority(id(db()), authority::owner), "", ("id", id));
GRAPHENE_ASSERT(
verify_authority(id(db()), authority::owner),
tx_missing_owner_auth,
"missing required owner authority ${id}", ("id", id));
for( const auto& auth : other_auths )
FC_ASSERT(trx_state->check_authority(auth), "invalid authority", ("auth",auth)("sigs",trx_state->_sigs));
GRAPHENE_ASSERT(
trx_state->check_authority(auth),
tx_missing_other_auth,
"missing required authority ${auth}",
("auth",auth)("sigs",trx_state->_sigs));
} FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -236,7 +236,7 @@ namespace graphene { namespace chain {
/**
* @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account
* is an potential signing authority.
* is an potential signing authority.
*/
class account_member_index : public secondary_index
{

View file

@ -86,7 +86,7 @@
#define GRAPHENE_DEFAULT_MAX_WITNESSES (1001) // SHOULD BE ODD
#define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD
#define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks
#define GRAPHENE_DEFAULT_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks
#define GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks
#define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
#define GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE (30*GRAPHENE_1_PERCENT)
#define GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT (50*GRAPHENE_1_PERCENT)
@ -100,7 +100,6 @@
#define GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD GRAPHENE_BLOCKCHAIN_PRECISION * 100;
#define GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE 1000
#define GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS 4
#define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL
#define GRAPHENE_MAX_WORKER_NAME_LENGTH 63
@ -139,7 +138,7 @@
* Reserved Account IDs with special meaning
*/
///@{
/// Represents the current committee members, two-week review period (GRAPHENE_DEFAULT_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC)
/// Represents the current committee members, two-week review period
#define GRAPHENE_COMMITTEE_ACCOUNT (graphene::chain::account_id_type(0))
/// Represents the current witnesses
#define GRAPHENE_WITNESS_ACCOUNT (graphene::chain::account_id_type(1))

View file

@ -69,7 +69,7 @@ namespace graphene { namespace chain {
* @class database
* @brief tracks the blockchain state in an extensible manner
*/
class database : public object_database
class database : public db::object_database
{
public:
//////////////////// db_management.cpp ////////////////////
@ -93,7 +93,19 @@ namespace graphene { namespace chain {
skip_assert_evaluation = 0x400 ///< used while reindexing
};
void open(const fc::path& data_dir, const genesis_state_type& initial_allocation = genesis_state_type());
/**
* @brief Open a database, creating a new one if necessary
*
* Opens a database in the specified directory. If no initialized database is found, genesis_loader is called
* and its return value is used as the genesis state when initializing the new database
*
* genesis_loader will not be called if an existing database is found.
*
* @param data_dir Path to open or create database in
* @param genesis_loader A callable object which returns the genesis state to initialize new databases on
*/
template<typename F>
void open(const fc::path& data_dir, F&& genesis_loader);
/**
* @brief Rebuild object graph from block history and open detabase
*
@ -477,4 +489,24 @@ namespace graphene { namespace chain {
(void)l;
}
}
template<typename F>
void database::open(const fc::path& data_dir, F&& genesis_loader)
{ try {
object_database::open(data_dir);
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
if( !find(global_property_id_type()) )
init_genesis(genesis_loader());
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
auto last_block= _block_id_to_block.last();
if( last_block.valid() )
_fork_db.start_block( *last_block );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
} }

View file

@ -18,12 +18,121 @@
#pragma once
#include <fc/exception/exception.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#define GRAPHENE_ASSERT( expr, exc_type, ... ) \
if( !(expr) ) \
{ \
FC_THROW_EXCEPTION( exc_type, __VA_ARGS__ ); \
}
#define GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( op_name ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _validate_exception, \
graphene::chain::operation_validate_exception, \
3040000 + 100 * operation::tag< op_name ## _operation >::value, \
#op_name "_operation validation exception" \
) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _evaluate_exception, \
graphene::chain::operation_evaluate_exception, \
3050000 + 100 * operation::tag< op_name ## _operation >::value, \
#op_name "_operation evaluation exception" \
)
#define GRAPHENE_DECLARE_OP_VALIDATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _ ## exc_name, \
graphene::chain::op_name ## _validate_exception, \
3040000 + 100 * operation::tag< op_name ## _operation >::value \
+ seqnum, \
msg \
)
#define GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _ ## exc_name, \
graphene::chain::op_name ## _evaluate_exception, \
3050000 + 100 * operation::tag< op_name ## _operation >::value \
+ seqnum, \
msg \
)
namespace graphene { namespace chain {
// registered in chain_database.cpp
FC_DECLARE_EXCEPTION( chain_exception, 30000, "Blockchain Exception" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, graphene::chain::chain_exception, 30001, "invalid pts address" )
FC_DECLARE_EXCEPTION( chain_exception, 3000000, "blockchain exception" )
FC_DECLARE_DERIVED_EXCEPTION( database_query_exception, graphene::chain::chain_exception, 3010000, "database query exception" )
FC_DECLARE_DERIVED_EXCEPTION( block_validate_exception, graphene::chain::chain_exception, 3020000, "block validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( transaction_exception, graphene::chain::chain_exception, 3030000, "transaction validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( operation_validate_exception, graphene::chain::chain_exception, 3040000, "operation validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, graphene::chain::chain_exception, 3050000, "operation evaluation exception" )
FC_DECLARE_DERIVED_EXCEPTION( utility_exception, graphene::chain::chain_exception, 3060000, "utility method exception" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, graphene::chain::transaction_exception, 3030001, "missing required active authority" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, graphene::chain::transaction_exception, 3030002, "missing required owner authority" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_other_auth, graphene::chain::transaction_exception, 3030003, "missing required other authority" )
//FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_authority, graphene::chain::transaction_exception, 3030004, "irrelevant authority" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, graphene::chain::utility_exception, 3060001, "invalid pts address" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, graphene::chain::chain_exception, 37006, "insufficient feeds" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1, "owner mismatch" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2, "owner mismatch" )
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_cancel );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( call_order_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_whitelist );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_upgrade );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_transfer );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_bitasset );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_feed_producers );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_issue );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_fund_fee_pool );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_settle );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_global_settle );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_publish_feed );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( delegate_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_withdraw_pay );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_create );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1, "review_period required" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2, "review_period insufficient" )
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_delete );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_claim );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_delete );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( fill_order );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( global_parameters_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( vesting_balance_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( vesting_balance_withdraw );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( worker_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( custom );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( assert );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( balance_claim );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( claimed_too_often, balance_claim, 1, "balance claimed too often" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_claim_amount, balance_claim, 2, "invalid claim amount" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, balance_claim, 3, "owner mismatch" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" )
/*
FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" )
FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" )
FC_DECLARE_DERIVED_EXCEPTION( asset_type_mismatch, graphene::chain::chain_exception, 30004, "asset/price mismatch" )
@ -63,8 +172,6 @@ namespace graphene { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( expired_transaction, graphene::chain::evaluation_error, 31010, "expired transaction" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_transaction_expiration, graphene::chain::evaluation_error, 31011, "invalid transaction expiration" )
FC_DECLARE_DERIVED_EXCEPTION( oversized_transaction, graphene::chain::evaluation_error, 31012, "transaction exceeded the maximum transaction size" )
FC_DECLARE_DERIVED_EXCEPTION( balance_claimed_too_often, graphene::chain::evaluation_error, 31013, "balance claimed too often" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_claim_amount, graphene::chain::evaluation_error, 31013, "invalid claim amount" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_account_name, graphene::chain::evaluation_error, 32001, "invalid account name" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_account_id, graphene::chain::evaluation_error, 32002, "unknown account id" )
@ -111,5 +218,6 @@ namespace graphene { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( price_multiplication_overflow, graphene::chain::evaluation_error, 38001, "price multiplication overflow" )
FC_DECLARE_DERIVED_EXCEPTION( price_multiplication_underflow, graphene::chain::evaluation_error, 38002, "price multiplication underflow" )
FC_DECLARE_DERIVED_EXCEPTION( price_multiplication_undefined, graphene::chain::evaluation_error, 38003, "price multiplication undefined product 0*inf" )
*/
} } // graphene::chain

View file

@ -23,7 +23,7 @@ struct genesis_state_type {
string name;
public_key_type owner_key;
public_key_type active_key;
bool is_lifetime_member;
bool is_lifetime_member = false;
};
struct initial_asset_type {
string symbol;
@ -79,36 +79,51 @@ struct genesis_state_type {
/// Must correspond to one of the initial accounts
string owner_name;
};
struct initial_worker_type {
/// Must correspond to one of the initial accounts
string owner_name;
share_type daily_pay;
};
chain_parameters initial_parameters;
vector<initial_account_type> initial_accounts;
vector<initial_asset_type> initial_assets;
vector<initial_balance_type> initial_balances;
vector<initial_vesting_balance_type> initial_vesting_balances;
int initial_active_witnesses = GRAPHENE_DEFAULT_NUM_WITNESSES;
vector<initial_witness_type> initial_witness_candidates;
// These are only candidates; the chain will have no active committee members at genesis
vector<initial_committee_member_type> initial_committee_candidates;
time_point_sec initial_timestamp;
chain_parameters initial_parameters;
vector<initial_account_type> initial_accounts;
vector<initial_asset_type> initial_assets;
vector<initial_balance_type> initial_balances;
vector<initial_vesting_balance_type> initial_vesting_balances;
int initial_active_witnesses = GRAPHENE_DEFAULT_NUM_WITNESSES;
vector<initial_witness_type> initial_witness_candidates;
vector<initial_committee_member_type> initial_committee_candidates;
vector<initial_worker_type> initial_worker_candidates;
};
} } // namespace graphene::chain
FC_REFLECT(graphene::chain::genesis_state_type::initial_account_type, (name)(owner_key)(active_key)(is_lifetime_member))
FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
(owner)(asset_symbol)(amount))
FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type,
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance))
FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key))
FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_bitasset_options::initial_collateral_position,
(collateral)(debt))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_bitasset_options,
(feed_lifetime_sec)(minimum_feeds)(force_settlement_delay_sec)(force_settlement_offset_percent)
(maximum_force_settlement_volume)(backing_asset_symbol)(maintenance_collateral_ratio)(collateral_records))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type,
(symbol)(description)(precision)(issuer_name)(max_supply)(market_fee_percent)
(issuer_permissions)(flags)(bitasset_options)(initial_accumulated_fees))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_bitasset_options,
(feed_lifetime_sec)(minimum_feeds)(force_settlement_delay_sec)(force_settlement_offset_percent)
(maximum_force_settlement_volume)(backing_asset_symbol)(maintenance_collateral_ratio)(collateral_records))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_bitasset_options::initial_collateral_position,
(collateral)(debt))
FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
(owner)(asset_symbol)(amount))
FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type,
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance))
FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key))
FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name))
FC_REFLECT(graphene::chain::genesis_state_type::initial_worker_type, (owner_name)(daily_pay))
FC_REFLECT(graphene::chain::genesis_state_type,
(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
(initial_timestamp)(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
(initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates)
(initial_committee_candidates))
(initial_committee_candidates)(initial_worker_candidates))

View file

@ -36,7 +36,7 @@ namespace graphene { namespace chain {
smart_ref<fee_schedule> current_fees; ///< current schedule of fees
uint8_t block_interval = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks
uint32_t maintenance_interval = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL; ///< interval in sections between blockchain maintenance events
uint32_t genesis_proposal_review_period = GRAPHENE_DEFAULT_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC; ///< minimum time in seconds that a proposed transaction requiring genesis authority may not be signed, prior to expiration
uint32_t committee_proposal_review_period = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC; ///< minimum time in seconds that a proposed transaction requiring genesis authority may not be signed, prior to expiration
uint32_t maximum_transaction_size = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE; ///< maximum allowable size in bytes for a transaction
uint32_t maximum_block_size = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE; ///< maximum allowable size in bytes for a block
uint32_t maximum_undo_history = GRAPHENE_DEFAULT_MAX_UNDO_HISTORY; ///< maximum number of undo states to keep in RAM
@ -87,7 +87,7 @@ namespace graphene { namespace chain {
"Block size limit is too low" );
FC_ASSERT( maximum_time_until_expiration > block_interval,
"Maximum transaction expiration time must be greater than a block interval" );
FC_ASSERT( maximum_proposal_lifetime - genesis_proposal_review_period > block_interval,
FC_ASSERT( maximum_proposal_lifetime - committee_proposal_review_period > block_interval,
"Genesis proposal review period must be less than the maximum proposal lifetime" );
}
};
@ -98,7 +98,7 @@ FC_REFLECT( graphene::chain::chain_parameters,
(current_fees)
(block_interval)
(maintenance_interval)
(genesis_proposal_review_period)
(committee_proposal_review_period)
(maximum_transaction_size)
(maximum_block_size)
(maximum_undo_history)

View file

@ -38,6 +38,7 @@
#include <cstdint>
#include <graphene/chain/protocol/address.hpp>
#include <graphene/db/object_id.hpp>
#include <graphene/chain/protocol/config.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
@ -316,7 +317,6 @@ namespace graphene { namespace chain {
return std::to_string(type()) + ":" + std::to_string(instance());
}
};
struct public_key_type
{
struct binary_key
@ -325,9 +325,7 @@ namespace graphene { namespace chain {
uint32_t check;
fc::ecc::public_key_data data;
};
fc::ecc::public_key_data key_data;
public_key_type();
public_key_type( const fc::ecc::public_key_data& data );
public_key_type( const fc::ecc::public_key& pubkey );
@ -338,12 +336,12 @@ namespace graphene { namespace chain {
friend bool operator == ( const public_key_type& p1, const fc::ecc::public_key& p2);
friend bool operator == ( const public_key_type& p1, const public_key_type& p2);
friend bool operator != ( const public_key_type& p1, const public_key_type& p2);
// TODO: This is temporary for testing
bool is_valid_v1( const std::string& base58str );
};
} } // graphene::chain
namespace fc
@ -399,7 +397,6 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
FC_REFLECT_ENUM( graphene::chain::meta_info_object_type, (meta_account_object_type)(meta_asset_object_type) )
FC_REFLECT_TYPENAME( graphene::chain::share_type )
FC_REFLECT_TYPENAME( graphene::chain::account_id_type )

View file

@ -30,6 +30,7 @@ namespace graphene { namespace chain {
struct vesting_balance_worker_initializer
{
vesting_balance_worker_initializer(uint16_t days=0):pay_vesting_period_days(days){}
uint16_t pay_vesting_period_days = 0;
};

View file

@ -17,6 +17,7 @@
*/
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/exceptions.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
@ -117,8 +118,8 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
if( _bitasset_data->is_prediction_market )
FC_ASSERT( o.delta_collateral.amount == o.delta_debt.amount );
else
FC_ASSERT( !_bitasset_data->current_feed.settlement_price.is_null() );
else if( _bitasset_data->current_feed.settlement_price.is_null() )
FC_THROW_EXCEPTION(insufficient_feeds, "Cannot borrow asset with no price feed.");
if( o.delta_debt.amount < 0 )
{

View file

@ -19,6 +19,7 @@
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
@ -45,8 +46,21 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
FC_ASSERT( other.size() == 0 ); // TODO: what about other???
if( auths.find(account_id_type()) != auths.end() )
FC_ASSERT( o.review_period_seconds
&& *o.review_period_seconds >= global_parameters.genesis_proposal_review_period );
{
GRAPHENE_ASSERT(
o.review_period_seconds.valid(),
proposal_create_review_period_required,
"Review period not given, but at least ${min} required",
("min", global_parameters.committee_proposal_review_period)
);
GRAPHENE_ASSERT(
*o.review_period_seconds >= global_parameters.committee_proposal_review_period,
proposal_create_review_period_insufficient,
"Review period of ${t} required, but at least ${min} required",
("t", *o.review_period_seconds)
("min", global_parameters.committee_proposal_review_period)
);
}
}
for( const op_wrapper& op : o.proposed_ops )

View file

@ -8,7 +8,7 @@ proposal_create_operation proposal_create_operation::genesis_proposal(const chai
{
proposal_create_operation op;
op.expiration_time = head_block_time + global_params.maximum_proposal_lifetime;
op.review_period_seconds = global_params.genesis_proposal_review_period;
op.review_period_seconds = global_params.committee_proposal_review_period;
return op;
}

View file

@ -17,6 +17,7 @@
*/
#include <graphene/chain/transfer_evaluator.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
@ -30,8 +31,20 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
if( asset_type.options.flags & white_list )
{
FC_ASSERT( to_account.is_authorized_asset( asset_type ) );
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
GRAPHENE_ASSERT(
from_account.is_authorized_asset( asset_type ),
transfer_from_account_not_whitelisted,
"'from' account ${from} is not whitelisted for asset ${asset}",
("from",op.from)
("asset",op.amount.asset_id)
);
GRAPHENE_ASSERT(
to_account.is_authorized_asset( asset_type ),
transfer_to_account_not_whitelisted,
"'to' account ${to} is not whitelisted for asset ${asset}",
("to",op.to)
("asset",op.amount.asset_id)
);
}
if( fee_asset_type.options.flags & white_list )
@ -60,7 +73,12 @@ void_result override_transfer_evaluator::do_evaluate( const override_transfer_op
database& d = db();
const asset_object& asset_type = op.amount.asset_id(d);
FC_ASSERT( asset_type.can_override() );
GRAPHENE_ASSERT(
asset_type.can_override(),
override_transfer_not_permitted,
"override_transfer not permitted for asset ${asset}",
("asset", op.amount.asset_id)
);
FC_ASSERT( asset_type.issuer == op.issuer );
const account_object& from_account = op.from(d);

View file

@ -19,6 +19,7 @@
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {

View file

@ -301,7 +301,7 @@ public:
: self(s),
_remote_api(rapi),
_remote_db(rapi->database()),
_remote_net(rapi->network()),
_remote_net_broadcast(rapi->network_broadcast()),
_remote_hist(rapi->history())
{
_remote_db->subscribe_to_objects( [=]( const fc::variant& obj )
@ -741,7 +741,7 @@ public:
}
if( broadcast )
_remote_net->broadcast_transaction( tx );
_remote_net_broadcast->broadcast_transaction( tx );
return tx;
} FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)(referrer_account)(referrer_percent)(broadcast) ) }
@ -868,7 +868,7 @@ public:
if( save_wallet )
save_wallet_file();
if( broadcast )
_remote_net->broadcast_transaction( tx );
_remote_net_broadcast->broadcast_transaction( tx );
return tx;
} FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) }
@ -1451,7 +1451,7 @@ public:
}
if( broadcast )
_remote_net->broadcast_transaction( tx );
_remote_net_broadcast->broadcast_transaction( tx );
return tx;
}
@ -1696,7 +1696,7 @@ public:
fc::api<login_api> _remote_api;
fc::api<database_api> _remote_db;
fc::api<network_api> _remote_net;
fc::api<network_broadcast_api> _remote_net_broadcast;
fc::api<history_api> _remote_hist;
#ifdef __unix__
@ -2441,7 +2441,7 @@ signed_transaction wallet_api::import_balance( string name_or_id, const vector<s
boost::erase(tx.signatures, boost::unique<boost::return_found_end>(boost::sort(tx.signatures)));
if( broadcast )
my->_remote_net->broadcast_transaction(tx);
my->_remote_net_broadcast->broadcast_transaction(tx);
return tx;
} FC_CAPTURE_AND_RETHROW( (name_or_id) ) }

View file

@ -365,7 +365,7 @@ int main( int argc, char** argv )
detail_ns::js_name<static_variant<address,public_key_type>>::name("key_data");
detail_ns::js_name<operation_result>::name("operation_result");
detail_ns::js_name<header_extension>::name("header_extension");
detail_ns::js_name<parameter_extension>::name("parameter_extension");
detail_ns::js_name<worker_initializer>::name("worker_initializer");
detail_ns::js_name<static_variant<linear_vesting_policy_initializer,cdd_vesting_policy_initializer>>::name("vesting_policy_initializer");
detail_ns::serializer<signed_block>::init();
detail_ns::serializer<block_header>::init();

View file

@ -119,7 +119,7 @@ int main(int argc, char** argv) {
node.startup_plugins();
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
#ifdef __unix__
#if defined __APPLE__ || defined __unix__
fc::set_signal_handler([&exit_promise](int signal) {
exit_promise->set_value(signal);
}, SIGINT);

View file

@ -0,0 +1,23 @@
#!/usr/bin/env python3
import base64
import getpass
import hashlib
import json
import os
pw = getpass.getpass("enter your password: ")
pw_bytes = pw.encode("utf-8")
salt_bytes = os.urandom(8)
salt_b64 = base64.b64encode( salt_bytes )
pw_hash = hashlib.sha256( pw_bytes + salt_bytes ).digest()
pw_hash_b64 = base64.b64encode( pw_hash )
print(json.dumps(
{
"password_hash_b64" : pw_hash_b64.decode("ascii"),
"password_salt_b64" : salt_b64.decode("ascii"),
},
sort_keys=True,
indent=3, separators=(',', ' : ')
))

View file

@ -0,0 +1,50 @@
"name": "dummy0",
"owner_key": "BTS6qkMe8pHmQ4zUetLV1bbVKoQJYTNb1fSUbkQzuzpscYhonWpgk",
"active_key": "BTS6qkMe8pHmQ4zUetLV1bbVKoQJYTNb1fSUbkQzuzpscYhonWpgk",
"is_lifetime_member": true
},{
"name": "dummy1",
"owner_key": "BTS7wXsTzBBR2QEetjrgxcSmN7Kuzey3RAzQWNNHwbPQsKYxkP6fp",
"active_key": "BTS7wXsTzBBR2QEetjrgxcSmN7Kuzey3RAzQWNNHwbPQsKYxkP6fp",
"is_lifetime_member": true
},{
"name": "dummy2",
"owner_key": "BTS7rzifzfJxS8RWhev9aU8HDYoJi1EGwJRHG9B2fJKxnZAiF2Rsh",
"active_key": "BTS7rzifzfJxS8RWhev9aU8HDYoJi1EGwJRHG9B2fJKxnZAiF2Rsh",
"is_lifetime_member": true
},{
"name": "dummy3",
"owner_key": "BTS6QZdcwFEFMtHsfW27YBGTv9KLaLTvgx5wgGrPHeUxDTrYEQJ2d",
"active_key": "BTS6QZdcwFEFMtHsfW27YBGTv9KLaLTvgx5wgGrPHeUxDTrYEQJ2d",
"is_lifetime_member": true
},{
"name": "dummy4",
"owner_key": "BTS7q5MqhSP2a6CRTWaJk77ZcGdpnv14JbT4cVzbXaoAsWJoCxFJG",
"active_key": "BTS7q5MqhSP2a6CRTWaJk77ZcGdpnv14JbT4cVzbXaoAsWJoCxFJG",
"is_lifetime_member": true
},{
"name": "dummy5",
"owner_key": "BTS5sRXxgDCnteHLUS623xtxJM5WKKVygwDMzEso6LigwxvprJqBA",
"active_key": "BTS5sRXxgDCnteHLUS623xtxJM5WKKVygwDMzEso6LigwxvprJqBA",
"is_lifetime_member": true
},{
"name": "dummy6",
"owner_key": "BTS5V4HEQJbVbMjUWASeknQ42NT3NP9bZaygt83XMuvy6v4QMJuSP",
"active_key": "BTS5V4HEQJbVbMjUWASeknQ42NT3NP9bZaygt83XMuvy6v4QMJuSP",
"is_lifetime_member": true
},{
"name": "dummy7",
"owner_key": "BTS86ukuPAufzKouerZf1dCxjVSmxQPA5kLwvnYEjn9GRqi5qXBop",
"active_key": "BTS86ukuPAufzKouerZf1dCxjVSmxQPA5kLwvnYEjn9GRqi5qXBop",
"is_lifetime_member": true
},{
"name": "dummy8",
"owner_key": "BTS7Sdg3kQuz2pPT8mA8Yr3mkBe7zr6293mnBmoR36z9xxtRdiMmJ",
"active_key": "BTS7Sdg3kQuz2pPT8mA8Yr3mkBe7zr6293mnBmoR36z9xxtRdiMmJ",
"is_lifetime_member": true
},{
"name": "dummy9",
"owner_key": "BTS5WCj1mMiiqEE4QRs7xhaFfSaiFroejUp3GuZE9wvfue9nxhPPn",
"active_key": "BTS5WCj1mMiiqEE4QRs7xhaFfSaiFroejUp3GuZE9wvfue9nxhPPn",
"is_lifetime_member": true
},{

View file

@ -0,0 +1,41 @@
"initial_balances": [{
"owner": "BTSHYhQcrjVg5kBzCoeeD38eQdncCC5pBgee",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTSPgQZg5929ht1NBdEvsGKqoQ7buRu3nKf4",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTSC9zrLXSAPUQaVmQPk1S9dMqSzT7jPqYU7",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTS93aQPtbbkXwaSjtHaREsNVcCvbfHo93aZ",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTS6RM4UfsYFPDuhbmgkvDS9ip8Kvqundvyk",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTSNVkZXdqWWSzqHVxvfetMe347is6kEkC4K",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTS5GHzWZ64Luoajqsz6JGjTKVMgWYkGV9SQ",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTSDCVRFez92bW9doRLjnFCKLJnpM58mgmMb",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTS5CCdX3JYLBptYMuCjbsezqGYzN9vG9JCu",
"asset_symbol": "CORE",
"amount": 100000000000
},{
"owner": "BTSEQ3yQdr3EMDL2eRqGiceMCpoanaW16Puw",
"asset_symbol": "CORE",
"amount": 100000000000
},{

View file

@ -0,0 +1,10 @@
5KCNDLVGqvX8p3GcMFun9sMe6XbMvycVTm4bGrkB5aZGWCbAAtr
5HvFQ1bcAWk8H1A2qXj1AqSNp93GUAb6b2w5TVfLb1jWL6yNF3f
5JSxv2kgaBSm9nGseRNhLhgEKTBmoKJ5CkgLbbk5RW4RBCNsLJC
5K5E2TQtrodDFzsqPq3oVFi9rVX15AN8sLE3iTHfVsX1b49y49J
5HxC3fwN7VDZXKVkbbX3SzCczh18Fetx8TXBfJ3z3ovDUSPKvVd
5KSr4w978PDanQDYtftarcfJVvGe4wedYb1sYbdH6HNpi15heRa
5Kan4si6qWvDVpZuqug4c6KQH4zkvDhwspaGQiFKYniJv6qji6t
5KcZri5DDsMcDp1DjNeMkZSijkWurPoAoR7gBKTnnetNQ9CpXoJ
5K5TRZyEhC6GPgi57t5FhiSMRGVTHEbwXngbBEtCA41gM8LPFhF
5KXVG4oP4Vj3RawRpta79UFAg7pWp17FGf4DnrKfkr69ELytDMv

View file

@ -40,7 +40,8 @@ BOOST_AUTO_TEST_CASE( two_node_network )
fc::temp_directory app2_dir;
fc::temp_file genesis_json;
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
// TODO: Time should be read from the blockchain
fc::time_point_sec now( 1431700000 );
graphene::app::application app1;
app1.register_plugin<graphene::account_history::account_history_plugin>();

View file

@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench )
{
database db;
db.open(data_dir.path(), genesis_state);
db.open(data_dir.path(), [&]{return genesis_state;});
for( int i = 11; i < account_count + 11; ++i)
BOOST_CHECK(db.get_balance(account_id_type(i), asset_id_type()).amount == GRAPHENE_MAX_SHARE_SUPPLY / account_count);
@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench )
database db;
fc::time_point start_time = fc::time_point::now();
db.open(data_dir.path());
db.open(data_dir.path(), [&]{return genesis_state;});
ilog("Opened database in ${t} milliseconds.", ("t", (fc::time_point::now() - start_time).count() / 1000));
for( int i = 11; i < account_count + 11; ++i)

View file

@ -48,6 +48,16 @@ database_fixture::database_fixture()
: app(), db( *app.chain_database() )
{
try {
int argc = boost::unit_test::framework::master_test_suite().argc;
char** argv = boost::unit_test::framework::master_test_suite().argv;
for( int i=1; i<argc; i++ )
{
const std::string arg = argv[i];
if( arg == "--record-assert-trip" )
fc::enable_record_assert_trip = true;
if( arg == "--show-test-names" )
std::cout << "running test " << boost::unit_test::framework::current_test_case().p_name << std::endl;
}
auto ahplugin = app.register_plugin<graphene::account_history::account_history_plugin>();
auto mhplugin = app.register_plugin<graphene::market_history::market_history_plugin>();
delegate_pub_key = delegate_priv_key.get_public_key();
@ -60,6 +70,8 @@ database_fixture::database_fixture()
mhplugin->plugin_set_app(&app);
mhplugin->plugin_initialize(options);
genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
genesis_state.initial_active_witnesses = 10;
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
{
@ -274,7 +286,7 @@ void database_fixture::open_database()
{
if( !data_dir ) {
data_dir = fc::temp_directory();
db.open(data_dir->path(), genesis_state);
db.open(data_dir->path(), [this]{return genesis_state;});
}
}

View file

@ -21,8 +21,12 @@
#include <graphene/chain/database.hpp>
#include <fc/io/json.hpp>
#include <iostream>
using namespace graphene::db;
#define GRAPHENE_TESTING_GENESIS_TIMESTAMP (1431700000)
#define PUSH_TX \
graphene::chain::test::_push_transaction
@ -37,13 +41,6 @@ using namespace graphene::db;
op.validate(); \
op.field = temp; \
}
#define REQUIRE_OP_VALIDATION_FAILURE( op, field, value ) \
{ \
const auto temp = op.field; \
op.field = value; \
BOOST_REQUIRE_THROW( op.validate(), fc::exception ); \
op.field = temp; \
}
#define REQUIRE_OP_EVALUATION_SUCCESS( op, field, value ) \
{ \
const auto temp = op.field; \
@ -52,17 +49,65 @@ using namespace graphene::db;
op.field = temp; \
db.push_transaction( trx, ~0 ); \
}
///Shortcut to require an exception when processing a transaction with an operation containing an expected bad value
/// Uses require insteach of check, because these transactions are expected to fail. If they don't, subsequent tests
/// may spuriously succeed or fail due to unexpected database state.
#define REQUIRE_THROW_WITH_VALUE(op, field, value) \
#define GRAPHENE_REQUIRE_THROW( expr, exc_type ) \
{ \
std::string req_throw_info = fc::json::to_string( \
fc::mutable_variant_object() \
("source_file", __FILE__) \
("source_lineno", __LINE__) \
("expr", #expr) \
("exc_type", #exc_type) \
); \
if( fc::enable_record_assert_trip ) \
std::cout << "GRAPHENE_REQUIRE_THROW begin " \
<< req_throw_info << std::endl; \
BOOST_REQUIRE_THROW( expr, exc_type ); \
if( fc::enable_record_assert_trip ) \
std::cout << "GRAPHENE_REQUIRE_THROW end " \
<< req_throw_info << std::endl; \
}
#define GRAPHENE_CHECK_THROW( expr, exc_type ) \
{ \
std::string req_throw_info = fc::json::to_string( \
fc::mutable_variant_object() \
("source_file", __FILE__) \
("source_lineno", __LINE__) \
("expr", #expr) \
("exc_type", #exc_type) \
); \
if( fc::enable_record_assert_trip ) \
std::cout << "GRAPHENE_CHECK_THROW begin " \
<< req_throw_info << std::endl; \
BOOST_CHECK_THROW( expr, exc_type ); \
if( fc::enable_record_assert_trip ) \
std::cout << "GRAPHENE_CHECK_THROW end " \
<< req_throw_info << std::endl; \
}
#define REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, exc_type ) \
{ \
const auto temp = op.field; \
op.field = value; \
GRAPHENE_REQUIRE_THROW( op.validate(), exc_type ); \
op.field = temp; \
}
#define REQUIRE_OP_VALIDATION_FAILURE( op, field, value ) \
REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, fc::exception )
#define REQUIRE_THROW_WITH_VALUE_2(op, field, value, exc_type) \
{ \
auto bak = op.field; \
op.field = value; \
trx.operations.back() = op; \
op.field = bak; \
BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); \
GRAPHENE_REQUIRE_THROW(db.push_transaction(trx, ~0), exc_type); \
}
#define REQUIRE_THROW_WITH_VALUE( op, field, value ) \
REQUIRE_THROW_WITH_VALUE_2( op, field, value, fc::exception )
///This simply resets v back to its default-constructed value. Requires v to have a working assingment operator and
/// default constructor.
#define RESET(v) v = decltype(v)()
@ -71,7 +116,7 @@ using namespace graphene::db;
#define INVOKE(test) ((struct test*)this)->test_method(); trx.clear()
#define PREP_ACTOR(name) \
fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name));
fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name));
#define ACTOR(name) \
PREP_ACTOR(name) \
@ -102,8 +147,6 @@ struct database_fixture {
fc::ecc::private_key delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
public_key_type delegate_pub_key;
fc::time_point_sec genesis_time = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP );
fc::time_point_sec now = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP );
optional<fc::temp_directory> data_dir;
bool skip_key_index_test = false;
uint32_t anon_acct_count;
@ -146,10 +189,20 @@ struct database_fixture {
);
void force_global_settle(const asset_object& what, const price& p);
void force_settle(account_id_type who, asset what)
{ force_settle(who(db), what); }
void force_settle(const account_object& who, asset what);
void update_feed_producers(asset_id_type mia, flat_set<account_id_type> producers)
{ update_feed_producers(mia(db), producers); }
void update_feed_producers(const asset_object& mia, flat_set<account_id_type> producers);
void publish_feed(asset_id_type mia, account_id_type by, const price_feed& f)
{ publish_feed(mia(db), by(db), f); }
void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f);
void borrow(account_id_type who, asset what, asset collateral)
{ borrow(who(db), what, collateral); }
void borrow(const account_object& who, asset what, asset collateral);
void cover(account_id_type who, asset what, asset collateral_freed)
{ cover(who(db), what, collateral_freed); }
void cover(const account_object& who, asset what, asset collateral_freed);
const asset_object& get_asset( const string& symbol )const;
@ -163,7 +216,7 @@ struct database_fixture {
uint16_t market_fee_percent = 100 /*1%*/,
uint16_t flags = charge_market_fee);
const asset_object& create_user_issued_asset( const string& name );
const asset_object& create_user_issued_asset( const string& name,
const asset_object& create_user_issued_asset( const string& name,
const account_object& issuer,
uint16_t flags );
void issue_uia( const account_object& recipient, asset amount );

View file

@ -22,10 +22,11 @@
#include <boost/test/unit_test.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <fc/crypto/digest.hpp>
@ -71,7 +72,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture )
generate_block( skip_flags );
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.genesis_proposal_review_period = fc::hours(1).to_seconds();
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
transaction tx;
@ -206,7 +207,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture )
trx.sign( *owner_privkey[i] );
if( i < int(create_op.owner.weight_threshold-1) )
{
BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception);
GRAPHENE_REQUIRE_THROW(db.push_transaction(trx), fc::exception);
}
else
{
@ -401,4 +402,91 @@ BOOST_FIXTURE_TEST_CASE( generic_scheduler_mc_test, database_fixture )
}
}
BOOST_FIXTURE_TEST_CASE( tapos_rollover, database_fixture )
{
try
{
ACTORS((alice)(bob));
const auto& core = asset_id_type()(db);
BOOST_TEST_MESSAGE( "Give Alice some money" );
transfer(genesis_account, alice_id, asset(10000));
generate_block();
BOOST_TEST_MESSAGE( "Generate up to block 0xFF00" );
generate_blocks( 0xFF00 );
signed_transaction xfer_tx;
BOOST_TEST_MESSAGE( "Transfer money at/about 0xFF00" );
transfer_operation xfer_op;
xfer_op.from = alice_id;
xfer_op.to = bob_id;
xfer_op.amount = asset(1000);
xfer_tx.operations.push_back( xfer_op );
xfer_tx.set_expiration( db.head_block_id(), 0x1000 );
sign( xfer_tx, alice_private_key );
PUSH_TX( db, xfer_tx, 0 );
generate_block();
BOOST_TEST_MESSAGE( "Sign new tx's" );
xfer_tx.set_expiration( db.head_block_id(), 0x1000 );
xfer_tx.signatures.clear();
sign( xfer_tx, alice_private_key );
BOOST_TEST_MESSAGE( "Generate up to block 0x10010" );
generate_blocks( 0x110 );
BOOST_TEST_MESSAGE( "Transfer at/about block 0x10010 using reference block at/about 0xFF00" );
PUSH_TX( db, xfer_tx, 0 );
generate_block();
}
catch (fc::exception& e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_FIXTURE_TEST_CASE(bulk_discount, database_fixture)
{ try {
ACTOR(nathan);
// Give nathan ALLLLLL the money!
transfer(GRAPHENE_COMMITTEE_ACCOUNT, nathan_id, db.get_balance(GRAPHENE_COMMITTEE_ACCOUNT, asset_id_type()));
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
upgrade_to_lifetime_member(nathan_id);
share_type new_fees;
while( nathan_id(db).statistics(db).lifetime_fees_paid + new_fees < GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN )
{
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
new_fees += transfer_operation().calculate_fee(db.current_fee_schedule());
}
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
auto old_cashback = nathan_id(db).cashback_balance(db).balance;
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
BOOST_CHECK_EQUAL(nathan_id(db).cashback_balance(db).balance.amount.value,
old_cashback.amount.value + GRAPHENE_BLOCKCHAIN_PRECISION * 8);
new_fees = 0;
while( nathan_id(db).statistics(db).lifetime_fees_paid + new_fees < GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX )
{
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
new_fees += transfer_operation().calculate_fee(db.current_fee_schedule());
}
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
old_cashback = nathan_id(db).cashback_balance(db).balance;
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK_EQUAL(nathan_id(db).cashback_balance(db).balance.amount.value,
old_cashback.amount.value + GRAPHENE_BLOCKCHAIN_PRECISION * 9);
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()

View file

@ -1,7 +1,7 @@
#include <boost/test/unit_test.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>

View file

@ -20,6 +20,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
@ -80,7 +81,7 @@ BOOST_AUTO_TEST_CASE( any_two_of_three )
transfer_operation op = {asset(), nathan.id, account_id_type(), core.amount(500)};
trx.operations.push_back(op);
sign(trx, nathan_key1);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
sign(trx, nathan_key2);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500);
@ -100,7 +101,7 @@ BOOST_AUTO_TEST_CASE( any_two_of_three )
trx.signatures.clear();
//sign(trx, fc::ecc::private_key::generate());
sign(trx,nathan_key3);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
@ -135,16 +136,16 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
BOOST_TEST_MESSAGE( "Attempting to transfer with no signatures, should fail" );
transfer_operation op = {asset(), child.id, account_id_type(), core.amount(500)};
trx.operations.push_back(op);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting to transfer with parent1 signature, should fail" );
sign(trx,parent1_key);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Attempting to transfer with parent2 signature, should fail" );
sign(trx,parent2_key);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting to transfer with parent1 and parent2 signature, should succeed" );
sign(trx,parent1_key);
@ -172,14 +173,14 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
op = {asset(),child.id, account_id_type(), core.amount(500)};
trx.operations.push_back(op);
BOOST_TEST_MESSAGE( "Attempting transfer with no signatures, should fail" );
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting transfer just parent1, should fail" );
sign(trx, parent1_key);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Attempting transfer just parent2, should fail" );
sign(trx, parent2_key);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting transfer both parents, should succeed" );
sign(trx, parent1_key);
@ -216,7 +217,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
trx.operations.push_back(op);
sign(trx, parent1_key);
sign(trx, parent2_key);
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
trx.signatures.clear();
trx.sign( parent2_key );
trx.sign( grandparent_key );
@ -244,7 +245,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
sign(trx, grandparent_key);
sign(trx, delegate_priv_key);
//Fails due to recursion depth.
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "verify child key can override recursion checks" );
trx.signatures.clear();
sign(trx, child_key);
@ -268,7 +269,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
trx.operations.push_back(op);
sign(trx, parent2_key);
//Fails due to recursion depth.
BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
@ -331,11 +332,11 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
trx.operations = {proposal_update_operation{account_id_type(), asset(), proposal.id,{nathan.id},flat_set<account_id_type>{},flat_set<account_id_type>{},flat_set<account_id_type>{},flat_set<public_key_type>{},flat_set<public_key_type>{}}};
trx.sign( genesis_key );
//Genesis may not add nathan's approval.
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
trx.operations = {proposal_update_operation{account_id_type(), asset(), proposal.id,{account_id_type()},flat_set<account_id_type>{},flat_set<account_id_type>{},flat_set<account_id_type>{},flat_set<public_key_type>{},flat_set<public_key_type>{}}};
trx.sign( genesis_key );
//Genesis has no stake in the transaction.
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
trx.signatures.clear();
trx.operations = {proposal_update_operation{nathan.id, asset(), proposal.id,{nathan.id},flat_set<account_id_type>{},flat_set<account_id_type>{},flat_set<account_id_type>{},flat_set<public_key_type>{},flat_set<public_key_type>{}}};
@ -366,31 +367,31 @@ BOOST_AUTO_TEST_CASE( genesis_authority )
// Signatures are for suckers.
db.modify(db.get_global_properties(), [](global_property_object& p) {
// Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.
p.parameters.genesis_proposal_review_period = fc::days(1).to_seconds();
p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();
});
BOOST_TEST_MESSAGE( "transfering 100000 CORE to nathan, signing with genesis key" );
trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan.id, asset(100000)}));
sign(trx, genesis_key);
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
auto sign = [&] { trx.signatures.clear(); trx.sign(nathan_key); };
proposal_create_operation pop;
pop.proposed_ops.push_back({trx.operations.front()});
pop.expiration_time = db.head_block_time() + global_params.genesis_proposal_review_period*2;
pop.expiration_time = db.head_block_time() + global_params.committee_proposal_review_period*2;
pop.fee_paying_account = nathan.id;
trx.operations = {pop};
sign();
// The review period isn't set yet. Make sure it throws.
BOOST_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception );
pop.review_period_seconds = global_params.genesis_proposal_review_period / 2;
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), proposal_create_review_period_required );
pop.review_period_seconds = global_params.committee_proposal_review_period / 2;
trx.operations.back() = pop;
sign();
// The review period is too short. Make sure it throws.
BOOST_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception );
pop.review_period_seconds = global_params.genesis_proposal_review_period;
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), proposal_create_review_period_insufficient );
pop.review_period_seconds = global_params.committee_proposal_review_period;
trx.operations.back() = pop;
sign();
proposal_object prop = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());
@ -405,7 +406,7 @@ BOOST_AUTO_TEST_CASE( genesis_authority )
generate_block();
BOOST_REQUIRE(db.find_object(prop.id));
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);
BOOST_TEST_MESSAGE( "Checking that the proposal is not authorized to execute" );
BOOST_REQUIRE(!db.get<proposal_object>(prop.id).is_authorized_to_execute(db));
trx.operations.clear();
@ -435,7 +436,7 @@ BOOST_AUTO_TEST_CASE( genesis_authority )
trx.operations.back() = uop;
trx.sign( genesis_key);
// Should throw because the transaction is now in review.
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
generate_blocks(prop.expiration_time);
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);
@ -456,7 +457,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture )
db.modify(db.get_global_properties(), [](global_property_object& p) {
// Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.
p.parameters.genesis_proposal_review_period = fc::days(1).to_seconds();
p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();
});
for( int i = 0; i < 15; ++i )
@ -590,7 +591,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_two_accounts, database_fixture )
uop.active_approvals_to_add = {dan.get_id()};
trx.operations.push_back(uop);
trx.sign(nathan_key);
BOOST_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
trx.sign(dan_key);
PUSH_TX( db, trx );
@ -840,7 +841,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )
generate_block();
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.genesis_proposal_review_period = fc::hours(1).to_seconds();
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
transaction tx;
@ -911,7 +912,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )
if( num_keys > max_authority_membership )
{
BOOST_REQUIRE_THROW(PUSH_TX( db, tx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, ~0 ), fc::exception);
}
else
{
@ -976,7 +977,7 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
trx.operations.push_back( xfer_op );
BOOST_TEST_MESSAGE( "Invalidating Alices Signature" );
// Alice's signature is now invalid
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception );
// Re-sign, now OK (sig is replaced)
BOOST_TEST_MESSAGE( "Resign with Alice's Signature" );
trx.signatures.clear();
@ -988,7 +989,7 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
trx.sign( alice_key );
trx.sign( charlie_key );
// Signed by third-party Charlie (irrelevant key, not in authority)
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception );
}
FC_LOG_AND_RETHROW()
}
@ -1029,7 +1030,7 @@ BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture )
trx.operations.push_back(op);
trx.sign(vikram_private_key);
// Fails because num_committee is larger than the cardinality of committee members being voted for
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
op.new_options->num_committee = 3;
trx.operations = {op};
trx.signatures.clear();

View file

@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE( data_fees )
BOOST_AUTO_TEST_CASE( exceptions )
{
BOOST_CHECK_THROW(FC_THROW_EXCEPTION(invalid_claim_amount, "Etc"), invalid_claim_amount);
GRAPHENE_CHECK_THROW(FC_THROW_EXCEPTION(balance_claim_invalid_claim_amount, "Etc"), balance_claim_invalid_claim_amount);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -36,6 +36,9 @@ using namespace graphene::chain;
genesis_state_type make_genesis() {
genesis_state_type genesis_state;
genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
genesis_state.initial_active_witnesses = 10;
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
@ -117,7 +120,7 @@ BOOST_AUTO_TEST_CASE( block_database_test )
BOOST_AUTO_TEST_CASE( generate_empty_blocks )
{
try {
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
fc::temp_directory data_dir;
signed_block b;
@ -126,7 +129,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
{
database db;
db.open(data_dir.path(), make_genesis() );
db.open(data_dir.path(), make_genesis );
b = db.generate_block(now, db.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
for( uint32_t i = 1; i < 200; ++i )
@ -134,25 +137,25 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
BOOST_CHECK( db.head_block_id() == b.id() );
witness_id_type prev_witness = b.witness;
now += db.block_interval();
witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first;
witness_id_type cur_witness = db.get_scheduled_witness(1).first;
BOOST_CHECK( cur_witness != prev_witness );
b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing );
b = db.generate_block(now, cur_witness, delegate_priv_key, database::skip_nothing);
BOOST_CHECK( b.witness == cur_witness );
}
db.close();
}
{
database db;
db.open(data_dir.path() );
db.open(data_dir.path(), []{return genesis_state_type();});
BOOST_CHECK_EQUAL( db.head_block_num(), 200 );
for( uint32_t i = 0; i < 200; ++i )
{
BOOST_CHECK( db.head_block_id() == b.id() );
witness_id_type prev_witness = b.witness;
now += db.block_interval();
witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first;
witness_id_type cur_witness = db.get_scheduled_witness(1).first;
BOOST_CHECK( cur_witness != prev_witness );
b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing );
b = db.generate_block(now, cur_witness, delegate_priv_key, database::skip_nothing);
}
BOOST_CHECK_EQUAL( db.head_block_num(), 400 );
}
@ -168,8 +171,8 @@ BOOST_AUTO_TEST_CASE( undo_block )
fc::temp_directory data_dir;
{
database db;
db.open(data_dir.path(), make_genesis() );
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
db.open(data_dir.path(), make_genesis);
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
for( uint32_t i = 0; i < 5; ++i )
@ -205,12 +208,12 @@ BOOST_AUTO_TEST_CASE( fork_blocks )
try {
fc::temp_directory data_dir1;
fc::temp_directory data_dir2;
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
database db1;
db1.open(data_dir1.path(), make_genesis());
db1.open(data_dir1.path(), make_genesis);
database db2;
db2.open(data_dir2.path(), make_genesis());
db2.open(data_dir2.path(), make_genesis);
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
for( uint32_t i = 0; i < 10; ++i )
@ -251,7 +254,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks )
b.transactions.back().operations.emplace_back(transfer_operation());
b.sign(delegate_priv_key);
BOOST_CHECK_EQUAL(b.block_num(), 14);
BOOST_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception);
}
BOOST_CHECK_EQUAL(db1.head_block_num(), 13);
BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip);
@ -269,11 +272,11 @@ BOOST_AUTO_TEST_CASE( fork_blocks )
BOOST_AUTO_TEST_CASE( undo_pending )
{
try {
fc::time_point_sec now(GRAPHENE_GENESIS_TIMESTAMP);
fc::time_point_sec now(GRAPHENE_TESTING_GENESIS_TIMESTAMP);
fc::temp_directory data_dir;
{
database db;
db.open(data_dir.path(), make_genesis());
db.open(data_dir.path(), make_genesis);
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
public_key_type delegate_pub_key = delegate_priv_key.get_public_key();
@ -331,10 +334,10 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
dir2;
database db1,
db2;
db1.open(dir1.path(), make_genesis());
db2.open(dir2.path(), make_genesis());
db1.open(dir1.path(), make_genesis);
db2.open(dir2.path(), make_genesis);
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
public_key_type delegate_pub_key = delegate_priv_key.get_public_key();
const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type);
@ -355,7 +358,7 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
BOOST_CHECK(nathan_id(db1).name == "nathan");
now = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP );
now = fc::time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
now += db2.block_interval();
b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
db1.push_block(b);
@ -364,7 +367,7 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
db1.push_block(b);
BOOST_CHECK_THROW(nathan_id(db1), fc::exception);
GRAPHENE_CHECK_THROW(nathan_id(db1), fc::exception);
PUSH_TX( db2, trx );
@ -384,13 +387,13 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
BOOST_AUTO_TEST_CASE( duplicate_transactions )
{
try {
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
fc::temp_directory dir1,
dir2;
database db1,
db2;
db1.open(dir1.path(), make_genesis());
db2.open(dir2.path(), make_genesis());
db1.open(dir1.path(), make_genesis);
db2.open(dir2.path(), make_genesis);
auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check;
@ -414,14 +417,14 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions )
trx.sign( delegate_priv_key );
PUSH_TX( db1, trx, skip_sigs );
BOOST_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception);
now += db1.block_interval();
auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, skip_sigs );
PUSH_BLOCK( db2, b, skip_sigs );
BOOST_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception);
BOOST_CHECK_THROW(PUSH_TX( db2, trx, skip_sigs ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db2, trx, skip_sigs ), fc::exception);
BOOST_CHECK_EQUAL(db1.get_balance(nathan_id, asset_id_type()).amount.value, 500);
BOOST_CHECK_EQUAL(db2.get_balance(nathan_id, asset_id_type()).amount.value, 500);
} catch (fc::exception& e) {
@ -433,13 +436,13 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions )
BOOST_AUTO_TEST_CASE( tapos )
{
try {
fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP );
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
fc::temp_directory dir1,
dir2;
database db1,
db2;
db1.open(dir1.path(), make_genesis());
db2.open(dir2.path(), make_genesis());
db1.open(dir1.path(), make_genesis);
db2.open(dir2.path(), make_genesis);
const account_object& init1 = *db1.get_index_type<account_index>().indices().get<by_name>().find("init1");
@ -448,7 +451,7 @@ BOOST_AUTO_TEST_CASE( tapos )
const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type);
now += db1.block_interval();
auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing );
auto b = db1.generate_block(now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing);
signed_transaction trx;
//This transaction must be in the next block after its reference, or it is invalid.
@ -464,16 +467,12 @@ BOOST_AUTO_TEST_CASE( tapos )
db1.push_transaction(trx);
now += db1.block_interval();
b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
/*
now += db1.block_interval();
b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
*/
trx.clear();
trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)}));
trx.sign(delegate_priv_key);
//relative_expiration is 1, but ref block is 2 blocks old, so this should fail.
BOOST_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ), fc::exception);
trx.set_expiration(db1.head_block_id(), 2);
trx.signatures.clear();
trx.sign(delegate_priv_key);
@ -598,17 +597,17 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture )
trx.validate();
BOOST_TEST_MESSAGE( "Verify that not-signing causes an exception" );
BOOST_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
BOOST_TEST_MESSAGE( "Verify that double-signing causes an exception" );
trx.sign(bob_private_key);
trx.sign(bob_private_key);
BOOST_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
BOOST_TEST_MESSAGE( "Verify that signing with an extra, unused key fails" );
trx.signatures.pop_back();
trx.sign(generate_private_key("bogus"));
BOOST_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception );
BOOST_TEST_MESSAGE( "Verify that signing once with the proper key passes" );
trx.signatures.pop_back();
@ -622,7 +621,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture )
generate_block();
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.genesis_proposal_review_period = fc::hours(1).to_seconds();
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
{
@ -773,7 +772,7 @@ BOOST_FIXTURE_TEST_CASE( unimp_force_settlement, database_fixture )
trx.operations.push_back(sop);
trx.sign(key_id_type(),private_key);
//Trx has expired by now. Make sure it throws.
BOOST_CHECK_THROW(settle_id = PUSH_TX( db, trx ).operation_results.front().get<object_id_type>(), fc::exception);
GRAPHENE_CHECK_THROW(settle_id = PUSH_TX( db, trx ).operation_results.front().get<object_id_type>(), fc::exception);
trx.set_expiration(db.head_block_time() + fc::minutes(1));
trx.sign(key_id_type(),private_key);
settle_id = PUSH_TX( db, trx ).operation_results.front().get<object_id_type>();
@ -823,13 +822,13 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )
// Sam is the creator of accounts
private_key_type genesis_key = delegate_priv_key;
private_key_type sam_key = generate_private_key("sam");
account_object sam_account_object = create_account( "sam", sam_key );
account_object sam_account_object = create_account("sam", sam_key);
//Get a sane head block time
generate_block( skip_flags );
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.genesis_proposal_review_period = fc::hours(1).to_seconds();
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
transaction tx;
@ -839,18 +838,16 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )
// transfer from genesis account to Sam account
transfer(genesis_account_object, sam_account_object, core.amount(100000));
generate_block( skip_flags );
generate_block(skip_flags);
create_account( "alice" );
generate_block( skip_flags );
create_account( "bob" );
generate_block( skip_flags );
create_account("alice");
generate_block(skip_flags);
create_account("bob");
generate_block(skip_flags);
db.pop_block();
db.pop_block();
}
catch( const fc::exception& e )
{
} catch(const fc::exception& e) {
edump( (e.to_detail_string()) );
throw;
}
@ -882,73 +879,4 @@ BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture )
});
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( account_create_fee_scaling, database_fixture )
{ try {
auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale;
enable_fees(1);
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 1);
for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i )
create_account("shill" + fc::to_string(i));
generate_block();
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 16);
for( int i = 0; i < accounts_per_scale; ++i )
create_account("moreshills" + fc::to_string(i));
generate_block();
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 256);
for( int i = 0; i < accounts_per_scale; ++i )
create_account("moarshills" + fc::to_string(i));
generate_block();
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 4096);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 1);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( tapos_rollover, database_fixture )
{
try
{
ACTORS((alice)(bob));
const auto& core = asset_id_type()(db);
BOOST_TEST_MESSAGE( "Give Alice some money" );
transfer(genesis_account, alice_id, asset(10000));
generate_block();
BOOST_TEST_MESSAGE( "Generate up to block 0xFF00" );
generate_blocks( 0xFF00 );
signed_transaction xfer_tx;
BOOST_TEST_MESSAGE( "Transfer money at/about 0xFF00" );
transfer_operation xfer_op;
xfer_op.from = alice_id;
xfer_op.to = bob_id;
xfer_op.amount = asset(1000);
xfer_tx.operations.push_back( xfer_op );
xfer_tx.set_expiration( db.head_block_id(), 0x1000 );
sign( xfer_tx, alice_private_key );
PUSH_TX( db, xfer_tx, 0 );
generate_block();
BOOST_TEST_MESSAGE( "Sign new tx's" );
xfer_tx.set_expiration( db.head_block_id(), 0x1000 );
xfer_tx.signatures.clear();
sign( xfer_tx, alice_private_key );
BOOST_TEST_MESSAGE( "Generate up to block 0x10010" );
generate_blocks( 0x110 );
BOOST_TEST_MESSAGE( "Transfer at/about block 0x10010 using reference block at/about 0xFF00" );
PUSH_TX( db, xfer_tx, 0 );
generate_block();
}
catch (fc::exception& e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -130,45 +130,4 @@ BOOST_AUTO_TEST_CASE( cashback_test )
BOOST_CHECK_EQUAL(stud_id(db).cashback_balance(db).balance.amount.value, 25750);
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(bulk_discount)
{ try {
ACTOR(nathan);
// Give nathan ALLLLLL the money!
transfer(GRAPHENE_COMMITTEE_ACCOUNT, nathan_id, db.get_balance(GRAPHENE_COMMITTEE_ACCOUNT, asset_id_type()));
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
upgrade_to_lifetime_member(nathan_id);
share_type new_fees;
while( nathan_id(db).statistics(db).lifetime_fees_paid + new_fees < GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN )
{
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
new_fees += transfer_operation().calculate_fee(db.current_fee_schedule());
}
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
auto old_cashback = nathan_id(db).cashback_balance(db).balance;
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
BOOST_CHECK_EQUAL(nathan_id(db).cashback_balance(db).balance.amount.value,
old_cashback.amount.value + GRAPHENE_BLOCKCHAIN_PRECISION * 8);
new_fees = 0;
while( nathan_id(db).statistics(db).lifetime_fees_paid + new_fees < GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX )
{
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
new_fees += transfer_operation().calculate_fee(db.current_fee_schedule());
}
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10);
old_cashback = nathan_id(db).cashback_balance(db).balance;
transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1));
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK_EQUAL(nathan_id(db).cashback_balance(db).balance.amount.value,
old_cashback.amount.value + GRAPHENE_BLOCKCHAIN_PRECISION * 9);
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()

View file

@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_test )
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 5000 );
BOOST_TEST_MESSAGE( "verifying that attempting to cover the full amount without claiming the collateral fails" );
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0) ), fc::exception );
GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0) ), fc::exception );
cover( dan, bitusd.amount(2500), core.amount(5000));
@ -127,14 +127,14 @@ BOOST_AUTO_TEST_CASE( call_order_update_test )
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 );
BOOST_TEST_MESSAGE( "increasing debt without increasing collateral again" );
BOOST_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0)), fc::exception );
GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0)), fc::exception );
BOOST_TEST_MESSAGE( "attempting to claim all collateral without paying off debt" );
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000)), fc::exception );
GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000)), fc::exception );
BOOST_TEST_MESSAGE( "attempting reduce collateral without paying off any debt" );
cover( dan, bitusd.amount(0), asset(1000));
BOOST_TEST_MESSAGE( "attempting change call price to be below minimum for debt/collateral ratio" );
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0)), fc::exception );
GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0)), fc::exception );
} catch (fc::exception& e) {
edump((e.to_detail_string()));
@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE( black_swan )
force_settle(borrower, bitusd.amount(100));
BOOST_TEST_MESSAGE( "Verify that we cannot borrow after black swan" );
BOOST_REQUIRE_THROW( borrow(borrower, bitusd.amount(1000), asset(2000)), fc::exception );
GRAPHENE_REQUIRE_THROW( borrow(borrower, bitusd.amount(1000), asset(2000)), fc::exception );
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
@ -275,22 +275,22 @@ BOOST_AUTO_TEST_CASE( prediction_market )
transfer(genesis_account, nathan_id, asset(init_balance));
BOOST_TEST_MESSAGE( "Require throw for mismatch collateral amounts" );
BOOST_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000) ), fc::exception );
GRAPHENE_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000) ), fc::exception );
BOOST_TEST_MESSAGE( "Open position with equal collateral" );
borrow( dan, pmark.amount(1000), asset(1000) );
BOOST_TEST_MESSAGE( "Cover position with unequal asset should fail." );
BOOST_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000) ), fc::exception );
GRAPHENE_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000) ), fc::exception );
BOOST_TEST_MESSAGE( "Cover half of position with equal ammounts" );
cover( dan, pmark.amount(500), asset(500) );
BOOST_TEST_MESSAGE( "Verify that forced settlment fails before global settlement" );
BOOST_REQUIRE_THROW( force_settle( dan, pmark.amount(100) ), fc::exception );
GRAPHENE_REQUIRE_THROW( force_settle( dan, pmark.amount(100) ), fc::exception );
BOOST_TEST_MESSAGE( "Shouldn't be allowed to force settle at more than 1 collateral per debt" );
BOOST_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(105) ), fc::exception );
GRAPHENE_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(105) ), fc::exception );
force_global_settle( pmark, pmark.amount(100) / core.amount(95) );
@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE( create_account_test )
op.owner.add_authority(account_id_type(9999999999), 10);
trx.operations.back() = op;
op.owner = auth_bak;
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
op.owner = auth_bak;
trx.operations.back() = op;
@ -487,7 +487,7 @@ BOOST_AUTO_TEST_CASE( create_mia )
BOOST_CHECK(bitusd.symbol == "BITUSD");
BOOST_CHECK(bitusd.bitasset_data(db).options.short_backing_asset == asset_id_type());
BOOST_CHECK(bitusd.dynamic_asset_data_id(db).current_supply == 0);
BOOST_REQUIRE_THROW( create_bitasset("BITUSD"), fc::exception);
GRAPHENE_REQUIRE_THROW( create_bitasset("BITUSD"), fc::exception);
} catch ( const fc::exception& e ) {
elog( "${e}", ("e", e.to_detail_string() ) );
throw;
@ -574,7 +574,7 @@ BOOST_AUTO_TEST_CASE( create_uia )
BOOST_CHECK(test_asset.options.max_supply == 100000000);
BOOST_CHECK(!test_asset.bitasset_data_id.valid());
BOOST_CHECK(test_asset.options.market_fee_percent == GRAPHENE_MAX_MARKET_FEE_PERCENT/100);
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
const asset_dynamic_data_object& test_asset_dynamic_data = test_asset.dynamic_asset_data_id(db);
BOOST_CHECK(test_asset_dynamic_data.current_supply == 0);
@ -649,7 +649,7 @@ BOOST_AUTO_TEST_CASE( update_uia )
trx.operations.back() = op;
PUSH_TX( db, trx, ~0 );
op.issuer = account_id_type();
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
op.new_issuer.reset();
} catch(fc::exception& e) {
edump((e.to_detail_string()));
@ -1078,7 +1078,7 @@ BOOST_AUTO_TEST_CASE( limit_order_fill_or_kill )
trx.operations.clear();
trx.operations.push_back(op);
BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
op.fill_or_kill = false;
trx.operations.back() = op;
PUSH_TX( db, trx, ~0 );
@ -1088,7 +1088,7 @@ BOOST_AUTO_TEST_CASE( limit_order_fill_or_kill )
BOOST_AUTO_TEST_CASE( fill_order )
{ try {
fill_order_operation o;
BOOST_CHECK_THROW(o.validate(), fc::exception);
GRAPHENE_CHECK_THROW(o.validate(), fc::exception);
o.calculate_fee(db.current_fee_schedule());
} FC_LOG_AND_RETHROW() }

View file

@ -42,8 +42,6 @@ BOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture )
BOOST_AUTO_TEST_CASE( withdraw_permission_create )
{ try {
//ACTORS((nathan)(dan))
//idump((nathan)(dan));
auto nathan_private_key = generate_private_key("nathan");
auto dan_private_key = generate_private_key("dan");
account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id;
@ -110,7 +108,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test )
op.amount_to_withdraw = asset(1);
trx.operations.push_back(op);
//Throws because we haven't entered the first withdrawal period yet.
BOOST_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
//Get to the actual withdrawal period
generate_blocks(permit(db).period_start_time);
@ -167,7 +165,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test )
trx.operations.push_back(op);
trx.sign(dan_private_key);
//Throws because nathan doesn't have the money
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
op.amount_to_withdraw = asset(1);
trx.clear();
trx.operations = {op};
@ -202,7 +200,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test )
trx.operations.push_back(op);
trx.sign(dan_private_key);
//Throws because the permission has expired
BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
}
} FC_LOG_AND_RETHROW() }
@ -370,7 +368,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds )
op.publisher = nathan_id;
trx.operations.back() = op;
BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
}
} FC_LOG_AND_RETHROW() }
@ -895,7 +893,7 @@ BOOST_AUTO_TEST_CASE( unimp_force_settlement_unavailable )
sop.amount = asset(50, bit_usd);
trx.operations = {sop};
//Force settlement is disabled; check that it fails
BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
{
//Enable force settlement
asset_update_operation op;
@ -969,7 +967,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test )
op.predicates.back() = fc::raw::pack(predicate(pred::account_name_eq_lit{ nathan_id, "dan" }));
trx.operations.back() = op;
trx.sign(nathan_private_key);
BOOST_CHECK_THROW( PUSH_TX( db, trx ), fc::exception );
GRAPHENE_CHECK_THROW( PUSH_TX( db, trx ), fc::exception );
} FC_LOG_AND_RETHROW()
}
@ -1003,7 +1001,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
genesis_state.initial_accounts.emplace_back("n", n_key.get_public_key());
db.open(td.path(), genesis_state);
db.open(td.path(), [this]{return genesis_state;});
const balance_object& balance = balance_id_type()(db);
BOOST_CHECK_EQUAL(balance.balance.amount.value, 1);
BOOST_CHECK_EQUAL(balance_id_type(1)(db).balance.amount.value, 1);
@ -1016,7 +1014,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
trx.operations = {op};
trx.sign(n_key);
// Fail because I'm claiming from an address which hasn't signed
BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception);
GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception);
trx.clear();
op.balance_to_claim = balance_id_type();
op.balance_owner_key = n_key.get_public_key();
@ -1031,6 +1029,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
auto slot = db.get_slot_at_time(starting_time);
db.generate_block(starting_time, db.get_scheduled_witness(slot).first, delegate_priv_key, database::skip_nothing);
trx.set_expiration(db.head_block_id());
const balance_object& vesting_balance_1 = balance_id_type(2)(db);
const balance_object& vesting_balance_2 = balance_id_type(3)(db);
@ -1045,12 +1044,11 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
op.total_claimed = asset(1);
op.balance_owner_key = v1_key.get_public_key();
trx.clear();
trx.set_expiration(db.head_block_id());
trx.operations = {op};
trx.sign(n_key);
trx.sign(v1_key);
// Attempting to claim 1 from a balance with 0 available
BOOST_CHECK_THROW(db.push_transaction(trx), invalid_claim_amount);
GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_invalid_claim_amount);
op.balance_to_claim = vesting_balance_2.id;
op.total_claimed.amount = 151;
@ -1060,7 +1058,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
trx.sign(n_key);
trx.sign(v2_key);
// Attempting to claim 151 from a balance with 150 available
BOOST_CHECK_THROW(db.push_transaction(trx), invalid_claim_amount);
GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_invalid_claim_amount);
op.balance_to_claim = vesting_balance_2.id;
op.total_claimed.amount = 100;
@ -1070,33 +1068,58 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
trx.sign(n_key);
trx.sign(v2_key);
db.push_transaction(trx);
BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 101);
BOOST_CHECK_EQUAL(vesting_balance_2.balance.amount.value, 300);
op.total_claimed.amount = 10;
trx.operations = {op};
trx.signatures.clear();
trx.sign(n_key);
trx.sign(v2_key);
// Attempting to claim twice within a day
BOOST_CHECK_THROW(db.push_transaction(trx), balance_claimed_too_often);
GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_claimed_too_often);
// TODO: test withdrawing entire vesting_balance_1 balance, remainder of vesting_balance_2 balance
// slot = db.get_slot_at_time(vesting_balance_1.vesting_policy->begin_timestamp + 60);
// db.generate_block(starting_time, db.get_scheduled_witness(slot).first, delegate_priv_key, database::skip_nothing);
db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
slot = db.get_slot_at_time(vesting_balance_1.vesting_policy->begin_timestamp + 60);
db.generate_block(db.get_slot_time(slot), db.get_scheduled_witness(slot).first, delegate_priv_key, database::skip_nothing);
trx.set_expiration(db.head_block_id());
// op.balance_to_claim = vesting_balance_1.id;
// op.total_claimed.amount = 500;
// op.balance_owner_key = v1_key.get_public_key();
// trx.operations = {op};
// trx.signatures.clear();
// trx.sign(n_key);
// trx.sign(v2_key);
// db.push_transaction(trx);
// BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr);
op.balance_to_claim = vesting_balance_1.id;
op.total_claimed.amount = 500;
op.balance_owner_key = v1_key.get_public_key();
trx.operations = {op};
trx.signatures.clear();
trx.sign(n_key);
trx.sign(v1_key);
db.push_transaction(trx);
BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr);
BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 601);
op.balance_to_claim = vesting_balance_2.id;
op.balance_owner_key = v2_key.get_public_key();
op.total_claimed.amount = 10;
trx.operations = {op};
trx.signatures.clear();
trx.sign(n_key);
trx.sign(v2_key);
// Attempting to claim twice within a day
GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_claimed_too_often);
db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
slot = db.get_slot_at_time(db.head_block_time() + fc::days(1));
db.generate_block(db.get_slot_time(slot), db.get_scheduled_witness(slot).first, delegate_priv_key, database::skip_nothing);
trx.set_expiration(db.head_block_id());
op.total_claimed = vesting_balance_2.balance;
trx.operations = {op};
trx.signatures.clear();
trx.sign(n_key);
trx.sign(v2_key);
db.push_transaction(trx);
BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr);
BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 901);
} FC_LOG_AND_RETHROW() }
// TODO: Write linear VBO tests
BOOST_AUTO_TEST_SUITE_END()

View file

@ -19,6 +19,7 @@
#include <boost/test/unit_test.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/account_object.hpp>
@ -84,10 +85,10 @@ BOOST_AUTO_TEST_CASE( override_transfer_test )
trx.operations.push_back(otrans);
BOOST_TEST_MESSAGE( "Require throwing without signature" );
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), tx_missing_active_auth );
BOOST_TEST_MESSAGE( "Require throwing with dan's signature" );
trx.sign( dan_private_key );
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), tx_missing_active_auth );
BOOST_TEST_MESSAGE( "Pass with issuer's signature" );
trx.signatures.clear();
trx.sign( sam_private_key );
@ -112,14 +113,14 @@ BOOST_AUTO_TEST_CASE( override_transfer_test2 )
trx.operations.push_back(otrans);
BOOST_TEST_MESSAGE( "Require throwing without signature" );
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);
BOOST_TEST_MESSAGE( "Require throwing with dan's signature" );
trx.sign( dan_private_key );
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);
BOOST_TEST_MESSAGE( "Fail because overide_authority flag is not set" );
trx.signatures.clear();
trx.sign( sam_private_key );
BOOST_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception );
BOOST_REQUIRE_EQUAL( get_balance( dan, advanced ), 1000 );
BOOST_REQUIRE_EQUAL( get_balance( eric, advanced ), 0 );
@ -137,7 +138,7 @@ BOOST_AUTO_TEST_CASE( issue_whitelist_uia )
asset_issue_operation op({asset(), advanced.issuer, advanced.amount(1000), nathan.id});
trx.operations.emplace_back(op);
//Fail because nathan is not whitelisted.
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
account_whitelist_operation wop({asset(), account_id_type(), nathan.id, account_whitelist_operation::white_listed});
@ -168,7 +169,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
transfer_operation op({advanced.amount(0), nathan.id, dan.id, advanced.amount(100)});
trx.operations.push_back(op);
//Fail because dan is not whitelisted.
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), transfer_to_account_not_whitelisted );
account_whitelist_operation wop({asset(), account_id_type(), dan.id, account_whitelist_operation::white_listed});
trx.operations.back() = wop;
@ -187,14 +188,14 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
op.amount = advanced.amount(50);
trx.operations.back() = op;
//Fail because nathan is blacklisted
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), transfer_from_account_not_whitelisted );
trx.operations = {asset_reserve_operation{asset(), nathan.id, advanced.amount(10)}};
//Fail because nathan is blacklisted
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
std::swap(op.from, op.to);
trx.operations.back() = op;
//Fail because nathan is blacklisted
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
{
asset_update_operation op;
@ -221,7 +222,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
trx.operations.back() = op;
//Fail because nathan is blacklisted
BOOST_CHECK(!nathan.is_authorized_asset(advanced));
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
//Remove nathan from genesis' whitelist, add him to dan's. This should not authorize him to hold ADVANCED.
wop.authorizing_account = account_id_type();
@ -238,7 +239,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
trx.operations.back() = op;
//Fail because nathan is not whitelisted
BOOST_CHECK(!nathan.is_authorized_asset(advanced));
BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
trx.operations = {asset_reserve_operation{asset(), dan.id, advanced.amount(10)}};
PUSH_TX(db, trx, ~0);