Merge branch 'github_master'

Conflicts:
	CMakeLists.txt
	libraries/fc
	libraries/wallet/include/graphene/wallet/wallet.hpp
This commit is contained in:
Eric Frias 2015-07-14 17:33:03 -04:00
commit 8927e800af
240 changed files with 19852 additions and 11830 deletions

13
.gitignore vendored
View file

@ -9,6 +9,10 @@ compile_commands.json
libraries/utilities/git_revision.cpp
libraries/wallet/Doxyfile
libraries/wallet/api_documentation.cpp
libraries/wallet/doxygen
programs/cli_wallet/cli_wallet
programs/js_operation_serializer/js_operation_serializer
programs/witness_node/witness_node
@ -23,3 +27,12 @@ doxygen
wallet.json
witness_node_data_dir
*.wallet
programs/witness_node/object_database/*
object_database/*
*.pyc
*.pyo

5
.gitmodules vendored
View file

@ -1,11 +1,8 @@
[submodule "docs"]
path = docs
url = https://github.com/cryptonomex/graphene.wiki.git
ignore = dirty
[submodule "libraries/fc"]
path = libraries/fc
url = git@git.syncad.com:/fc.git
ignore = dirty
[submodule "libraries/leveldb"]
path = libraries/leveldb
url = https://github.com/bitcoin/leveldb.git
ignore = dirty

View file

@ -56,7 +56,7 @@ IF( WIN32 )
set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries
ENDIF(WIN32)
FIND_PACKAGE(Boost 1.53 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
FIND_PACKAGE(Boost 1.57 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
# For Boost 1.53 on windows, coroutine was not in BOOST_LIBRARYDIR and do not need it to build, but if boost versin >= 1.54, find coroutine otherwise will cause link errors
IF(NOT "${Boost_VERSION}" MATCHES "1.53(.*)")
SET(BOOST_LIBRARIES_TEMP ${Boost_LIBRARIES})
@ -65,22 +65,8 @@ IF(NOT "${Boost_VERSION}" MATCHES "1.53(.*)")
SET(Boost_LIBRARIES ${BOOST_LIBRARIES_TEMP} ${Boost_LIBRARIES})
ENDIF()
set( LEVEL_DB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/leveldb" )
file( GLOB LEVEL_DB_SOURCES "${LEVEL_DB_DIR}/db/*.cc"
"${LEVEL_DB_DIR}/helpers/memenv/memenv.cc"
"${LEVEL_DB_DIR}/table/*.cc"
"${LEVEL_DB_DIR}/util/*.cc" )
foreach( filename ${LEVEL_DB_SOURCES} )
if( ${filename} MATCHES ".*_test.cc" OR ${filename} MATCHES ".*_bench.cc" OR ${filename} MATCHES ".*_main.cc" )
list( REMOVE_ITEM LEVEL_DB_SOURCES ${filename} )
endif()
endforeach()
set(LEVELDB_BUILD_DEFINES)
set(LEVELDB_BUILD_LIBRARIES)
set(LEVELDB_BUILD_PRIVATE_INCLUDES "${LEVEL_DB_DIR}")
if( WIN32 )
message( STATUS "Configuring Graphene on WIN32")
set( DB_VERSION 60 )
set( BDB_STATIC_LIBS 1 )
@ -112,20 +98,7 @@ if( WIN32 )
SET(TCL_LIBS "${TCL_LIBS}${TCL_LIB_PATH}/${TCL_LIB_NAME}g${TCL_LIB_EXT}")
SET(TCL_LIBRARY ${TCL_LIBS})
SET(LEVELDB_PORT_FILE "${LEVEL_DB_DIR}/port/port_win.cc" )
list(APPEND LEVELDB_BUILD_DEFINES OS_WINDOWS LEVELDB_PLATFORM_WINDOWS )
list(APPEND LEVELDB_BUILD_LIBRARIES shlwapi.lib)
list(INSERT LEVELDB_BUILD_PRIVATE_INCLUDES 0 "${CMAKE_CURRENT_SOURCE_DIR}/libraries/leveldb-msvc/include")
else( WIN32 ) # Apple AND Linux
SET(LEVELDB_PORT_FILE "${LEVEL_DB_DIR}/port/port_posix.cc" )
list(APPEND LEVELDB_BUILD_DEFINES LEVELDB_PLATFORM_POSIX LEVELDB_ATOMIC_PRESENT)
if( APPLE )
list(APPEND LEVELDB_BUILD_DEFINES OS_MACOSX)
else() # Linux
list(APPEND LEVELDB_BUILD_DEFINES OS_LINUX)
list(APPEND LEVELDB_BUILD_LIBRARIES pthread)
endif()
find_library(READLINE_LIBRARIES NAMES readline)
find_path(READLINE_INCLUDE_DIR readline/readline.h)
@ -169,13 +142,6 @@ else( WIN32 ) # Apple AND Linux
endif( WIN32 )
list(APPEND LEVEL_DB_SOURCES "${LEVELDB_PORT_FILE}")
add_library( leveldb ${LEVEL_DB_SOURCES} )
target_link_libraries( leveldb ${LEVELDB_BUILD_LIBRARIES} )
target_include_directories( leveldb PRIVATE ${LEVELDB_BUILD_PRIVATE_INCLUDES}
PUBLIC "${LEVEL_DB_DIR}/include" )
set_target_properties(leveldb PROPERTIES COMPILE_DEFINITIONS "${LEVELDB_BUILD_DEFINES}")
find_package( BerkeleyDB )
set(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL "Build Graphene for code coverage analysis")

File diff suppressed because it is too large Load diff

133
README.md
View file

@ -6,6 +6,11 @@ This is a quick introduction to get new developers up to speed on Graphene.
Starting Graphene
-----------------
For Ubuntu 14.04 LTS users, see this link first:
https://github.com/cryptonomex/graphene/wiki/build-ubuntu
and then proceed with:
git clone https://github.com/cryptonomex/graphene.git
cd graphene
git submodule update --init --recursive
@ -13,7 +18,9 @@ Starting Graphene
make
./programs/witness_node/witness_node
This will launch the witness node. If you would like to launch the command-line wallet, you must first specify a port for communication with the witness node. To do this, add text to `witness_node_data_dir/config.ini` as follows, then restart the node:
This will launch the witness node. If you would like to launch the command-line wallet, you must first specify a port
for communication with the witness node. To do this, add text to `witness_node_data_dir/config.ini` as follows, then
restart the node:
rpc-endpoint = 127.0.0.1:8090
@ -26,14 +33,43 @@ To set your iniital password to 'password' use:
>>> set_password password
>>> unlock password
To import your initial balance:
>>> import_balance nathan [5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3] true
If you send private keys over this connection, `rpc-endpoint` should be bound to localhost for security.
A list of CLI wallet commands is available [here](https://bitshares.github.io/doxygen/classgraphene_1_1wallet_1_1wallet__api.html).
A list of CLI wallet commands is available
[here](https://github.com/cryptonomex/graphene/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
Code coverage testing
---------------------
TODO: Write something here
Check how much code is covered by unit tests, using gcov/lcov (see http://ltp.sourceforge.net/coverage/lcov.php ).
cmake -D ENABLE_COVERAGE_TESTING=true -D CMAKE_BUILD_TYPE=Debug .
make
lcov --capture --initial --directory . --output-file base.info --no-external
libraries/fc/bloom_test
libraries/fc/task_cancel_test
libraries/fc/api
libraries/fc/blind
libraries/fc/ecc_test test
libraries/fc/real128_test
libraries/fc/lzma_test README.md
libraries/fc/ntp_test
tests/intense_test
tests/app_test
tests/chain_bench
tests/chain_test
tests/performance_test
lcov --capture --directory . --output-file test.info --no-external
lcov --add-tracefile base.info --add-tracefile test.info --output-file total.info
lcov -o interesting.info -r total.info libraries/fc/vendor/\* libraries/fc/tests/\* tests/\*
mkdir -p lcov
genhtml interesting.info --output-directory lcov --prefix `pwd`
Now open `lcov/index.html` in a browser.
Unit testing
------------
@ -46,7 +82,9 @@ Witness node
The role of the witness node is to broadcast transactions, download blocks, and optionally sign them.
./witness_node --rpc-endpoint "127.0.0.1:8090" --enable-stale-production -w \""1.7.0"\" \""1.7.1"\" \""1.7.2"\" \""1.7.3"\" \""1.7.4"\" --private-key "[\"1.2.0\",\"aeebad4a796fcc2e15dc4c6061b45ed9b373f26adfc798ca7d2d8cc58182718e\"]"
```
./witness_node --rpc-endpoint "127.0.0.1:8090" --enable-stale-production -w \""1.6.0"\" \""1.6.1"\" \""1.6.2"\" \""1.6.3"\" \""1.6.4"\"
```
Running specific tests
----------------------
@ -61,32 +99,57 @@ When running `witness_node`, initially two API's are available:
API 0 provides read-only access to the database, while API 1 is
used to login and gain access to additional, restricted API's.
TODO: the following examples use the old authority definition and thus do not work. They need to be updated.
Here is an example using `wscat` package from `npm` for websockets:
$ npm install -g wscat
$ wscat -c ws://127.0.0.1:8090
> {"id":1, "method":"call", "params":[0,"get_accounts",[["1.3.0"]]]}
< {"id":1,"result":[{"id":"1.3.0","annotations":[],"registrar":"1.3.0","referrer":"1.3.0","referrer_percent":0,"name":"genesis","owner":{"weight_threshold":1,"auths":[["1.2.0",1]]},"active":{"weight_threshold":1,"auths":[["1.2.0",1]]},"memo_key":"1.2.0","voting_account":"1.3.0","num_witness":0,"num_committee":0,"votes":[],"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
> {"id":1, "method":"call", "params":[0,"get_accounts",[["1.2.0"]]]}
< {"id":1,"result":[{"id":"1.2.0","annotations":[],"registrar":"1.2.0","referrer":"1.2.0","referrer_percent":0,"name":"genesis","owner":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"active":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"memo_key":"PUBLIC_KEY","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
$
We can do the same thing using an HTTP client such as `curl` for API's which do not require login or other session state:
$ curl --data '{"jsonrpc": "2.0", "method": "call", "params": [0, "get_accounts", [["1.3.0"]]], "id": 1}' http://127.0.0.1:8090/rpc
{"id":1,"result":[{"id":"1.3.0","annotations":[],"registrar":"1.3.0","referrer":"1.3.0","referrer_percent":0,"name":"genesis","owner":{"weight_threshold":1,"auths":[["1.2.0",1]]},"active":{"weight_threshold":1,"auths":[["1.2.0",1]]},"memo_key":"1.2.0","voting_account":"1.3.0","num_witness":0,"num_committee":0,"votes":[],"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
$ curl --data '{"jsonrpc": "2.0", "method": "call", "params": [0, "get_accounts", [["1.2.0"]]], "id": 1}' http://127.0.0.1:8090/rpc
{"id":1,"result":[{"id":"1.2.0","annotations":[],"registrar":"1.2.0","referrer":"1.2.0","referrer_percent":0,"name":"genesis","owner":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"active":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"memo_key":"PUBLIC_KEY","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
API 0 is accessible using regular JSON-RPC:
$ curl --data '{"jsonrpc": "2.0", "method": "get_accounts", "params": [["1.3.0"]], "id": 1}' http://127.0.0.1:8090/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
@ -109,16 +172,16 @@ witness just because it has the correct private key to do so. There are
ten witnesses at genesis of the testnet, block production can be
enabled for all of them by specifying multiple times in `config.ini`:
witness-id = "1.7.0"
witness-id = "1.7.1"
witness-id = "1.7.2"
witness-id = "1.7.3"
witness-id = "1.7.4"
witness-id = "1.7.5"
witness-id = "1.7.6"
witness-id = "1.7.7"
witness-id = "1.7.8"
witness-id = "1.7.9"
witness-id = "1.6.0"
witness-id = "1.6.1"
witness-id = "1.6.2"
witness-id = "1.6.3"
witness-id = "1.6.4"
witness-id = "1.6.5"
witness-id = "1.6.6"
witness-id = "1.6.7"
witness-id = "1.6.8"
witness-id = "1.6.9"
Questions
---------
@ -126,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?
@ -159,13 +230,13 @@ Questions
- The answer to the previous question was really confusing. Can you make it clearer?
All account ID's are of the form `1.3.x`. If you were the 9735th account to be registered,
your account's ID will be `1.3.9735`. Account `0` is special (it's the "genesis account,"
which is controlled by the delegates and has a few abilities and restrictions other accounts
All account ID's are of the form `1.2.x`. If you were the 9735th account to be registered,
your account's ID will be `1.2.9735`. Account `0` is special (it's the "committee account,"
which is controlled by the committee members and has a few abilities and restrictions other accounts
do not).
All asset ID's are of the form `1.4.x`. If you were the 29th asset to be registered,
your asset's ID will be `1.4.29`. Asset `0` is special (it's BTS, which is considered the "core asset").
All asset ID's are of the form `1.3.x`. If you were the 29th asset to be registered,
your asset's ID will be `1.3.29`. Asset `0` is special (it's BTS, which is considered the "core asset").
The first and second number together identify the kind of thing you're talking about (`1.3` for accounts,
`1.4` for assets). The third number identifies the particular thing.
The first and second number together identify the kind of thing you're talking about (`1.2` for accounts,
`1.3` for assets). The third number identifies the particular thing.

2
docs

@ -1 +1 @@
Subproject commit 1b53a8eca77783d073ce7cc95991447c3f34b927
Subproject commit 97435c1a622e41e0a5fc1be72aaadea62e1b7adb

View file

@ -6,10 +6,10 @@ add_library( graphene_app
plugin.cpp
)
target_link_libraries( graphene_app graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities )
target_link_libraries( graphene_app graphene_market_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities )
target_include_directories( graphene_app
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
if(MSVC)
set_source_files_properties( application.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
set_source_files_properties( application.cpp api.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)

View file

@ -16,11 +16,11 @@
* 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/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/crypto/hex.hpp>
@ -61,6 +61,13 @@ namespace graphene { namespace app {
{
return _db.fetch_block_by_number(block_num);
}
processed_transaction database_api::get_transaction(uint32_t block_num, uint32_t trx_num)const
{
auto opt_block = _db.fetch_block_by_number(block_num);
FC_ASSERT( opt_block );
FC_ASSERT( opt_block->transactions.size() > trx_num );
return opt_block->transactions[trx_num];
}
vector<optional<account_object>> database_api::lookup_account_names(const vector<string>& account_names)const
{
@ -98,17 +105,6 @@ namespace graphene { namespace app {
return _db.get(dynamic_global_property_id_type());
}
vector<optional<key_object>> database_api::get_keys(const vector<key_id_type>& key_ids)const
{
vector<optional<key_object>> result; result.reserve(key_ids.size());
std::transform(key_ids.begin(), key_ids.end(), std::back_inserter(result),
[this](key_id_type id) -> optional<key_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
vector<optional<account_object>> database_api::get_accounts(const vector<account_id_type>& account_ids)const
{
@ -215,22 +211,6 @@ namespace graphene { namespace app {
return result;
}
vector<short_order_object> database_api::get_short_orders(asset_id_type a, uint32_t limit)const
{
const auto& short_order_idx = _db.get_index_type<short_order_index>();
const auto& sell_price_idx = short_order_idx.indices().get<by_price>();
const asset_object& mia = _db.get(a);
FC_ASSERT( mia.is_market_issued(), "must be a market issued asset" );
price index_price = price::min(mia.get_id(), mia.bitasset_data(_db).options.short_backing_asset);
auto short_itr = sell_price_idx.lower_bound(index_price.max());
auto short_end = sell_price_idx.upper_bound(index_price.min());
return vector<short_order_object>(short_itr, short_end);
}
vector<call_order_object> database_api::get_call_orders(asset_id_type a, uint32_t limit)const
{
const auto& call_index = _db.get_index_type<call_order_index>().indices().get<by_price>();
@ -267,46 +247,213 @@ namespace graphene { namespace app {
return result;
}
fc::optional<committee_member_object> database_api::get_committee_member_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();
auto itr = idx.find(account);
if( itr != idx.end() )
return *itr;
return {};
}
fc::optional<witness_object> database_api::get_witness_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<witness_index>().indices().get<by_account>();
auto itr = idx.find(account);
if( itr != idx.end() )
return *itr;
return {};
}
uint64_t database_api::get_witness_count()const
{
return _db.get_index_type<witness_index>().indices().size();
}
map<string, witness_id_type> database_api::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const
{
FC_ASSERT( limit <= 1000 );
const auto& witnesses_by_id = _db.get_index_type<witness_index>().indices().get<by_id>();
// we want to order witnesses by account name, but that name is in the account object
// so the witness_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the
// number of witnesses to be few and the frequency of calls to be rare
std::map<std::string, witness_id_type> witnesses_by_account_name;
for (const witness_object& witness : witnesses_by_id)
if (auto account_iter = _db.find(witness.witness_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id));
auto end_iter = witnesses_by_account_name.begin();
while (end_iter != witnesses_by_account_name.end() && limit--)
++end_iter;
witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end());
return witnesses_by_account_name;
}
map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const
{
FC_ASSERT( limit <= 1000 );
const auto& committee_members_by_id = _db.get_index_type<committee_member_index>().indices().get<by_id>();
// we want to order committee_members by account name, but that name is in the account object
// so the committee_member_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the
// number of committee_members to be few and the frequency of calls to be rare
std::map<std::string, committee_member_id_type> committee_members_by_account_name;
for (const committee_member_object& committee_member : committee_members_by_id)
if (auto account_iter = _db.find(committee_member.committee_member_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id));
auto end_iter = committee_members_by_account_name.begin();
while (end_iter != committee_members_by_account_name.end() && limit--)
++end_iter;
committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end());
return committee_members_by_account_name;
}
vector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const
{
vector<optional<witness_object>> result; result.reserve(witness_ids.size());
std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result),
[this](witness_id_type id) -> optional<witness_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
vector<optional<committee_member_object>> database_api::get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const
{
vector<optional<committee_member_object>> result; result.reserve(committee_member_ids.size());
std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result),
[this](committee_member_id_type id) -> optional<committee_member_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
login_api::login_api(application& a)
:_app(a)
{
}
login_api::~login_api()
{
}
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;
}
void network_api::add_node(const fc::ip::endpoint& ep)
void login_api::enable_api( const std::string& api_name )
{
_app.p2p_node()->add_node(ep);
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;
}
void network_api::broadcast_transaction(const signed_transaction& trx)
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_broadcast_api::on_applied_block( const signed_block& b )
{
if( _callbacks.size() )
{
for( uint32_t trx_num = 0; trx_num < b.transactions.size(); ++trx_num )
{
const auto& trx = b.transactions[trx_num];
auto id = trx.id();
auto itr = _callbacks.find(id);
auto block_num = b.block_num();
if( itr != _callbacks.end() )
{
fc::async( [=](){ itr->second( fc::variant(transaction_confirmation{ id, block_num, trx_num, 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);
}
std::vector<net::peer_status> network_api::get_connected_peers() const
void network_broadcast_api::broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx)
{
trx.validate();
_callbacks[trx.id()] = cb;
_app.chain_database()->push_transaction(trx);
_app.p2p_node()->broadcast_transaction(trx);
}
network_node_api::network_node_api( application& a ) : _app( a )
{
}
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
@ -336,6 +483,10 @@ namespace graphene { namespace app {
{
_subscriptions[id](obj->to_variant());
}
else
{
_subscriptions[id](fc::variant(id));
}
}
});
}
@ -358,15 +509,11 @@ namespace graphene { namespace app {
case operation::tag<limit_order_create_operation>::value:
market = op.op.get<limit_order_create_operation>().get_market();
break;
case operation::tag<short_order_create_operation>::value:
market = op.op.get<limit_order_create_operation>().get_market();
break;
case operation::tag<fill_order_operation>::value:
market = op.op.get<fill_order_operation>().get_market();
break;
/*
case operation::tag<limit_order_cancel_operation>::value:
case operation::tag<short_order_cancel_operation>::value:
*/
default: break;
}
@ -428,7 +575,7 @@ namespace graphene { namespace app {
return fc::to_hex(fc::raw::pack(trx));
}
vector<operation_history_object> history_api::get_account_history(account_id_type account, operation_history_id_type stop, int limit, operation_history_id_type start) const
vector<operation_history_object> history_api::get_account_history(account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start) const
{
FC_ASSERT(_app.chain_database());
const auto& db = *_app.chain_database();
@ -450,4 +597,127 @@ namespace graphene { namespace app {
return result;
}
flat_set<uint32_t> history_api::get_market_history_buckets()const
{
auto hist = _app.get_plugin<market_history_plugin>( "market_history" );
FC_ASSERT( hist );
return hist->tracked_buckets();
}
vector<bucket_object> history_api::get_market_history( asset_id_type a, asset_id_type b,
uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const
{ try {
FC_ASSERT(_app.chain_database());
const auto& db = *_app.chain_database();
vector<bucket_object> result;
result.reserve(100);
if( a > b ) std::swap(a,b);
const auto& bidx = db.get_index_type<bucket_index>();
const auto& by_key_idx = bidx.indices().get<by_key>();
auto itr = by_key_idx.lower_bound( bucket_key( a, b, bucket_seconds, start ) );
while( itr != by_key_idx.end() && itr->key.open <= end && result.size() < 100 )
{
if( !(itr->key.base == a && itr->key.quote == b && itr->key.seconds == bucket_seconds) )
return result;
result.push_back(*itr);
++itr;
}
return result;
} FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) }
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> database_api::get_account_references( account_id_type account_id )const
{
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_account_memberships.find(account_id);
vector<account_id_type> result;
if( itr != refs.account_to_account_memberships.end() )
{
result.reserve( itr->second.size() );
for( auto item : itr->second ) result.push_back(item);
}
return result;
}
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> database_api::get_key_references( public_key_type key )const
{
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_key_memberships.find(key);
vector<account_id_type> result;
if( itr != refs.account_to_key_memberships.end() )
{
result.reserve( itr->second.size() );
for( auto item : itr->second ) result.push_back(item);
}
return result;
}
/** TODO: add secondary index that will accelerate this process */
vector<proposal_object> database_api::get_proposed_transactions( account_id_type id )const
{
const auto& idx = _db.get_index_type<proposal_index>();
vector<proposal_object> result;
idx.inspect_all_objects( [&](const object& obj){
const proposal_object& p = static_cast<const proposal_object&>(obj);
if( p.required_active_approvals.find( id ) != p.required_active_approvals.end() )
result.push_back(p);
else if ( p.required_owner_approvals.find( id ) != p.required_owner_approvals.end() )
result.push_back(p);
else if ( p.available_active_approvals.find( id ) != p.available_active_approvals.end() )
result.push_back(p);
});
return result;
}
vector<call_order_object> database_api::get_margin_positions( const account_id_type& id )const
{ try {
const auto& idx = _db.get_index_type<call_order_index>();
const auto& aidx = idx.indices().get<by_account>();
auto start = aidx.lower_bound( boost::make_tuple( id, 0 ) );
auto end = aidx.lower_bound( boost::make_tuple( id+1, 0 ) );
vector<call_order_object> result;
while( start != end )
{
result.push_back(*start);
++start;
}
return result;
} FC_CAPTURE_AND_RETHROW( (id) ) }
vector<balance_object> database_api::get_balance_objects( const vector<address>& addrs )const
{ try {
const auto& bal_idx = _db.get_index_type<balance_index>();
const auto& by_owner_idx = bal_idx.indices().get<by_owner>();
vector<balance_object> result;
for( const auto& owner : addrs )
{
auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) );
while( itr != by_owner_idx.end() && itr->owner == owner )
{
result.push_back( *itr );
++itr;
}
}
return result;
} FC_CAPTURE_AND_RETHROW( (addrs) ) }
} } // graphene::app

View file

@ -15,20 +15,24 @@
* 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>
#include <graphene/time/time.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/rpc/api_connection.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/signals2.hpp>
#include <iostream>
@ -50,8 +54,37 @@ using chain::block_id_type;
using std::vector;
namespace bpo = boost::program_options;
namespace detail {
genesis_state_type create_example_genesis() {
auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
genesis_state_type initial_state;
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);
initial_state.initial_accounts.emplace_back(name,
nathan_key.get_public_key(),
nathan_key.get_public_key(),
true);
initial_state.initial_committee_candidates.push_back({name});
initial_state.initial_witness_candidates.push_back({name, nathan_key.get_public_key()});
}
initial_state.initial_accounts.emplace_back("nathan", nathan_key.get_public_key());
initial_state.initial_balances.push_back({nathan_key.get_public_key(),
GRAPHENE_SYMBOL,
GRAPHENE_MAX_SHARE_SUPPLY});
return initial_state;
}
class application_impl : public net::node_delegate
{
public:
@ -105,6 +138,7 @@ namespace detail {
wsc->register_api(fc::api<graphene::app::login_api>(login));
c->set_session_data( wsc );
});
ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
_websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as<string>()) );
_websocket_server->start_accept();
} FC_CAPTURE_AND_RETHROW() }
@ -115,7 +149,10 @@ namespace detail {
if( !_options->count("rpc-tls-endpoint") )
return;
if( !_options->count("server-pem") )
{
wlog( "Please specify a server-pem to use rpc-tls-endpoint" );
return;
}
string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password );
@ -128,6 +165,7 @@ namespace detail {
wsc->register_api(fc::api<graphene::app::login_api>(login));
c->set_session_data( wsc );
});
ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
_websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as<string>()) );
_websocket_tls_server->start_accept();
} FC_CAPTURE_AND_RETHROW() }
@ -148,25 +186,58 @@ namespace detail {
bool clean = !fc::exists(_data_dir / "blockchain/dblock");
fc::create_directories(_data_dir / "blockchain/dblock");
auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
genesis_allocation initial_allocation = {{graphene::chain::public_key_type(nathan_key.get_public_key()), 1}};
if( _options->count("genesis-json") )
initial_allocation = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>()).as<genesis_allocation>();
else
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
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);
if( _options->count("checkpoint") )
{
auto cps = _options->at("checkpoint").as<vector<string>>();
flat_map<uint32_t,block_id_type> loaded_checkpoints;
loaded_checkpoints.reserve( cps.size() );
for( auto cp : cps )
{
auto item = fc::json::from_string(cp).as<std::pair<uint32_t,block_id_type> >();
loaded_checkpoints[item.first] = item.second;
}
_chain_db->add_checkpoints( loaded_checkpoints );
}
if( _options->count("replay-blockchain") )
{
ilog("Replaying blockchain on user request.");
_chain_db->reindex(_data_dir/"blockchain", initial_allocation);
_chain_db->reindex(_data_dir/"blockchain", initial_state());
} else if( clean )
_chain_db->open(_data_dir / "blockchain", initial_allocation);
_chain_db->open(_data_dir / "blockchain", initial_state);
else {
wlog("Detected unclean shutdown. Replaying blockchain...");
_chain_db->reindex(_data_dir / "blockchain", initial_allocation);
_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);
@ -174,20 +245,33 @@ 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
{ try {
if( id.item_type == graphene::net::block_message_type )
{
return _chain_db->is_known_block( id.item_hash );
}
else
{
return _chain_db->is_known_transaction( id.item_hash );
}
} FC_CAPTURE_AND_RETHROW( (id) ) }
virtual bool has_item(const net::item_id& id) override
{
try
{
if( id.item_type == graphene::net::block_message_type )
return _chain_db->is_known_block(id.item_hash);
else
return _chain_db->is_known_transaction(id.item_hash);
}
FC_CAPTURE_AND_RETHROW( (id) )
}
/**
* @brief allows the application to validate an item prior to broadcasting to peers.
@ -197,18 +281,24 @@ 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 {
return _chain_db->push_block( blk_msg.block, _is_block_producer? database::skip_nothing : database::skip_transaction_signatures );
return _chain_db->push_block(blk_msg.block, _is_block_producer? database::skip_nothing : database::skip_transaction_signatures);
} catch( const fc::exception& e ) {
elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
throw;
}
if( !_is_finished_syncing && !sync_mode )
{
_is_finished_syncing = true;
_self->syncing_finished();
}
} 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 );
@ -227,7 +317,7 @@ namespace detail {
virtual std::vector<item_hash_t> get_item_ids(uint32_t item_type,
const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count,
uint32_t limit ) override
uint32_t limit) override
{ try {
FC_ASSERT( item_type == graphene::net::block_message_type );
vector<block_id_type> result;
@ -240,7 +330,7 @@ namespace detail {
auto itr = blockchain_synopsis.rbegin();
while( itr != blockchain_synopsis.rend() )
{
if( _chain_db->is_known_block( *itr ) || *itr == block_id_type() )
if( _chain_db->is_known_block(*itr) || *itr == block_id_type() )
{
last_known_block_id = *itr;
break;
@ -257,25 +347,24 @@ namespace detail {
if( block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());
idump((blockchain_synopsis)(limit)(result)(remaining_item_count));
return result;
} FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) }
/**
* 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) ) }
@ -300,18 +389,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() );
@ -326,7 +415,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
}
@ -334,7 +423,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
}
@ -380,6 +469,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;
@ -387,6 +477,8 @@ namespace detail {
std::shared_ptr<fc::http::websocket_tls_server> _websocket_tls_server;
std::map<string, std::shared_ptr<abstract_plugin>> _plugins;
bool _is_finished_syncing = false;
};
}
@ -399,13 +491,11 @@ application::~application()
{
if( my->_p2p_network )
{
ilog("Closing p2p node");
my->_p2p_network->close();
my->_p2p_network.reset();
}
if( my->_chain_db )
{
ilog("Closing chain database");
my->_chain_db->close();
}
}
@ -416,14 +506,20 @@ void application::set_program_options(boost::program_options::options_descriptio
configuration_file_options.add_options()
("p2p-endpoint", bpo::value<string>(), "Endpoint for P2P node to listen on")
("seed-node,s", bpo::value<vector<string>>()->composing(), "P2P nodes to connect to on startup (may specify multiple times)")
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on")
("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on")
("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")
("api-access", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
;
command_line_options.add(configuration_file_options);
command_line_options.add_options()
("create-genesis-json", bpo::value<boost::filesystem::path>(),
"Path to create a Genesis State at. If a well-formed JSON file exists at the path, it will be parsed and any "
"missing fields in a Genesis State will be added, and any unknown fields will be removed. If no file or an "
"invalid file is found, it will be replaced with an example Genesis State.")
("replay-blockchain", "Rebuild object graph by replaying all blocks")
("resync-blockchain", "Delete all blocks and re-sync with network from scratch")
;
@ -435,6 +531,31 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti
{
my->_data_dir = data_dir;
my->_options = &options;
if( options.count("create-genesis-json") )
{
fc::path genesis_out = options.at("create-genesis-json").as<boost::filesystem::path>();
genesis_state_type genesis_state = detail::create_example_genesis();
if( fc::exists(genesis_out) )
{
try {
genesis_state = fc::json::from_file(genesis_out).as<genesis_state_type>();
} catch(const fc::exception& e) {
std::cerr << "Unable to parse existing genesis file:\n" << e.to_string()
<< "\nWould you like to replace it? [y/N] ";
char response = std::cin.get();
if( toupper(response) != 'Y' )
return;
}
std::cerr << "Updating genesis state in file " << genesis_out.generic_string() << "\n";
} else {
std::cerr << "Creating example genesis state in file " << genesis_out.generic_string() << "\n";
}
fc::json::save_to_file(genesis_state, genesis_out);
std::exit(EXIT_SUCCESS);
}
}
void application::startup()
@ -462,6 +583,16 @@ 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 );
}
bool application::is_finished_syncing() const
{
return my->_is_finished_syncing;
}
void graphene::app::application::add_plugin(const string& name, std::shared_ptr<graphene::app::abstract_plugin> p)
{
my->_plugins[name] = p;

View file

@ -16,19 +16,25 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/types.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/key_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/net/node.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <fc/api.hpp>
namespace graphene { namespace app {
using namespace graphene::chain;
using namespace graphene::market_history;
class application;
@ -37,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
{
@ -64,6 +70,12 @@ namespace graphene { namespace app {
* @return the referenced block, or null if no matching block was found
*/
optional<signed_block> get_block(uint32_t block_num)const;
/**
* @brief used to fetch an individual transaction.
*/
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
/**
* @brief Retrieve the current @ref global_property_object
*/
@ -72,14 +84,6 @@ namespace graphene { namespace app {
* @brief Retrieve the current @ref dynamic_global_property_object
*/
dynamic_global_property_object get_dynamic_global_properties()const;
/**
* @brief Get a list of keys by ID
* @param key_ids IDs of the keys to retrieve
* @return The keys corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<key_object>> get_keys(const vector<key_id_type>& key_ids)const;
/**
* @brief Get a list of accounts by ID
* @param account_ids IDs of the accounts to retrieve
@ -142,13 +146,6 @@ namespace graphene { namespace app {
* @return The limit orders, ordered from least price to greatest
*/
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
/**
* @brief Get short orders in a given asset
* @param a ID of asset being sold
* @param limit Maximum number of orders to retrieve
* @return The short orders, ordered from least price to greatest
*/
vector<short_order_object> get_short_orders(asset_id_type a, uint32_t limit)const;
/**
* @brief Get call orders in a given asset
* @param a ID of asset being called
@ -172,6 +169,58 @@ namespace graphene { namespace app {
*/
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
/**
* @brief Get the committee_member owned by a given account
* @param account The ID of the account whose committee_member should be retrieved
* @return The committee_member object, or null if the account does not have a committee_member
*/
fc::optional<committee_member_object> get_committee_member_by_account(account_id_type account)const;
/**
* @brief Get the witness owned by a given account
* @param account The ID of the account whose witness should be retrieved
* @return The witness object, or null if the account does not have a witness
*/
fc::optional<witness_object> get_witness_by_account(account_id_type account)const;
/**
* @brief Get the total number of witnesses registered with the blockchain
*/
uint64_t get_witness_count()const;
/**
* @brief Get names and IDs for registered witnesses
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of witness names to corresponding IDs
*/
map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Get names and IDs for registered committee_members
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of committee_member names to corresponding IDs
*/
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Get a list of witnesses by ID
* @param witness_ids IDs of the witnesses to retrieve
* @return The witnesses corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;
/**
* @brief Get a list of committee_members by ID
* @param committee_member_ids IDs of the committee_members to retrieve
* @return The committee_members corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<committee_member_object>> get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const;
/**
* @group Push Notification Methods
* These methods may be used to get push notifications whenever an object or market is changed
@ -217,6 +266,26 @@ namespace graphene { namespace app {
/// @brief Get a hexdump of the serialized binary form of a transaction
std::string get_transaction_hex(const signed_transaction& trx)const;
/**
* @return the set of proposed transactions relevant to the specified account id.
*/
vector<proposal_object> get_proposed_transactions( account_id_type id )const;
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> get_account_references( account_id_type account_id )const;
vector<account_id_type> get_key_references( public_key_type account_id )const;
/**
* @return all open margin positions for a given account id.
*/
vector<call_order_object> get_margin_positions( const account_id_type& id )const;
/** @return all unclaimed balance objects for a set of addresses */
vector<balance_object> get_balance_objects( const vector<address>& addrs )const;
private:
/** called every time a block is applied to report the objects that were changed */
void on_objects_changed(const vector<object_id_type>& ids);
@ -237,35 +306,46 @@ namespace graphene { namespace app {
*/
class history_api
{
public:
history_api(application& app):_app(app){}
public:
history_api(application& app):_app(app){}
/**
* @brief Get operations relevant to the specificed account
* @param account The account whose history should be queried
* @param stop ID of the earliest operation to retrieve
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start ID of the most recent operation to retrieve
* @return A list of operations performed by account, ordered from most recent to oldest.
*/
vector<operation_history_object> get_account_history(account_id_type account,
operation_history_id_type stop = operation_history_id_type(),
int limit = 100,
operation_history_id_type start = operation_history_id_type())const;
/**
* @brief Get operations relevant to the specificed account
* @param account The account whose history should be queried
* @param stop ID of the earliest operation to retrieve
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start ID of the most recent operation to retrieve
* @return A list of operations performed by account, ordered from most recent to oldest.
*/
vector<operation_history_object> get_account_history(account_id_type account,
operation_history_id_type stop = operation_history_id_type(),
unsigned limit = 100,
operation_history_id_type start = operation_history_id_type())const;
private:
application& _app;
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
fc::time_point_sec start, fc::time_point_sec end )const;
flat_set<uint32_t> get_market_history_buckets()const;
private:
application& _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):_app(a){}
network_broadcast_api(application& a);
struct transaction_confirmation
{
transaction_id_type id;
uint32_t block_num;
uint32_t trx_num;
processed_transaction trx;
};
typedef std::function<void(variant/*transaction_confirmation*/)> confirmation_callback;
/**
* @brief Broadcast a transaction to the network
@ -275,18 +355,55 @@ namespace graphene { namespace app {
* apply locally, an error will be thrown and the transaction will not be broadcast.
*/
void broadcast_transaction(const signed_transaction& trx);
/** this version of broadcast transaction registers a callback method that will be called when the transaction is
* included into a block. The callback method includes the transaction id, block number, and transaction number in the
* block.
*/
void broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx);
/**
* @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 on_applied_block( const signed_block& b );
private:
boost::signals2::scoped_connection _applied_block_connection;
map<transaction_id_type,confirmation_callback> _callbacks;
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;
application& _app;
};
/**
@ -310,29 +427,38 @@ 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_broadcast_api::transaction_confirmation,
(id)(block_num)(trx_num)(trx) )
FC_API(graphene::app::database_api,
(get_objects)
(get_block_header)
(get_block)
(get_transaction)
(get_global_properties)
(get_dynamic_global_properties)
(get_keys)
(get_accounts)
(get_assets)
(lookup_account_names)
@ -342,22 +468,45 @@ FC_API(graphene::app::database_api,
(get_named_account_balances)
(lookup_asset_symbols)
(get_limit_orders)
(get_short_orders)
(get_call_orders)
(get_settle_orders)
(list_assets)
(get_committee_member_by_account)
(get_witnesses)
(get_committee_members)
(get_witness_by_account)
(get_witness_count)
(lookup_witness_accounts)
(lookup_committee_member_accounts)
(subscribe_to_objects)
(unsubscribe_from_objects)
(subscribe_to_market)
(unsubscribe_from_market)
(cancel_all_subscriptions)
(get_transaction_hex)
(get_proposed_transactions)
(get_account_references)
(get_key_references)
(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_broadcast_api,
(broadcast_transaction)
(broadcast_transaction_with_callback)
)
FC_API(graphene::app::network_node_api,
(add_node)
(get_connected_peers)
)
FC_API(graphene::app::history_api, (get_account_history))
FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers))
FC_API(graphene::app::login_api,
(login)
(network)
(network_broadcast)
(database)
(history)
(network_node)
)

View file

@ -15,21 +15,36 @@
* 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/chain/key_object.hpp>
#include <graphene/chain/types.hpp>
#pragma once
namespace graphene { namespace chain {
address key_object::key_address()const
{
typedef static_variant<address,public_key_type> address_or_key;
#include <fc/reflect/reflect.hpp>
switch( key_data.which() )
{
case address_or_key::tag<address>::value:
return key_data.get<address>();
case address_or_key::tag<public_key_type>::value:
default:
return key_data.get<public_key_type>();
}
}
} }
#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>
@ -24,7 +25,6 @@
namespace graphene { namespace app {
namespace detail { class application_impl; }
namespace bpo = boost::program_options;
using std::string;
class abstract_plugin;
@ -35,10 +35,10 @@ namespace graphene { namespace app {
application();
~application();
void set_program_options( bpo::options_description& command_line_options,
bpo::options_description& configuration_file_options )const;
void initialize(const fc::path& data_dir, const bpo::variables_map&options);
void initialize_plugins( const bpo::variables_map& options );
void set_program_options( boost::program_options::options_description& command_line_options,
boost::program_options::options_description& configuration_file_options )const;
void initialize(const fc::path& data_dir, const boost::program_options::variables_map&options);
void initialize_plugins( const boost::program_options::variables_map& options );
void startup();
void shutdown();
void startup_plugins();
@ -50,7 +50,7 @@ namespace graphene { namespace app {
auto plug = std::make_shared<PluginType>();
plug->plugin_set_app(this);
bpo::options_description plugin_cli_options("Options for plugin " + plug->plugin_name()), plugin_cfg_options;
boost::program_options::options_description plugin_cli_options("Options for plugin " + plug->plugin_name()), plugin_cfg_options;
plug->plugin_set_program_options(plugin_cli_options, plugin_cfg_options);
if( !plugin_cli_options.options().empty() )
_cli_options.add(plugin_cli_options);
@ -75,13 +75,18 @@ 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;
bool is_finished_syncing()const;
/// Emitted when syncing finishes (is_finished_syncing will return true)
boost::signals2::signal<void()> syncing_finished;
private:
void add_plugin( const string& name, std::shared_ptr<abstract_plugin> p );
std::shared_ptr<detail::application_impl> my;
bpo::options_description _cli_options;
bpo::options_description _cfg_options;
boost::program_options::options_description _cli_options;
boost::program_options::options_description _cfg_options;
};
} }

View file

@ -20,9 +20,9 @@
#include <graphene/app/application.hpp>
#include <boost/program_options.hpp>
#include <fc/io/json.hpp>
namespace graphene { namespace app {
namespace bpo = boost::program_options;
class abstract_plugin
{
@ -42,7 +42,7 @@ class abstract_plugin
*
* @param options The options passed to the application, via configuration files or command line
*/
virtual void plugin_initialize( const bpo::variables_map& options ) = 0;
virtual void plugin_initialize( const boost::program_options::variables_map& options ) = 0;
/**
* @brief Begin normal runtime operations
@ -78,8 +78,8 @@ class abstract_plugin
* may simply provide an empty implementation of this method.
*/
virtual void plugin_set_program_options(
bpo::options_description& command_line_options,
bpo::options_description& config_file_options
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options
) = 0;
};
@ -94,18 +94,18 @@ class plugin : public abstract_plugin
virtual ~plugin() override;
virtual std::string plugin_name()const override;
virtual void plugin_initialize( const bpo::variables_map& options ) override;
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
virtual void plugin_startup() override;
virtual void plugin_shutdown() override;
virtual void plugin_set_app( application* app ) override;
virtual void plugin_set_program_options(
bpo::options_description& command_line_options,
bpo::options_description& config_file_options
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options
) override;
protected:
chain::database& database() { return *app().chain_database(); }
application& app()const { assert(_app); return *_app; }
protected:
net::node& p2p_node() { return *app().p2p_node(); }
private:

View file

@ -17,6 +17,7 @@
*/
#include <graphene/app/plugin.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
namespace graphene { namespace app {
@ -36,7 +37,7 @@ std::string plugin::plugin_name()const
return "<unknown plugin>";
}
void plugin::plugin_initialize( const bpo::variables_map& options )
void plugin::plugin_initialize( const boost::program_options::variables_map& options )
{
return;
}
@ -58,8 +59,8 @@ void plugin::plugin_set_app( application* app )
}
void plugin::plugin_set_program_options(
bpo::options_description& command_line_options,
bpo::options_description& config_file_options
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options
)
{
return;

View file

@ -2,57 +2,71 @@ file(GLOB HEADERS "include/graphene/chain/*.hpp")
## SORT .cpp by most likely to change / break compile
add_library( graphene_chain
types.cpp
address.cpp
asset.cpp
protocol/types.cpp
protocol/address.cpp
protocol/asset.cpp
protocol/assert.cpp
protocol/account.cpp
protocol/transfer.cpp
protocol/committee_member.cpp
protocol/witness.cpp
protocol/market.cpp
protocol/proposal.cpp
protocol/withdraw_permission.cpp
protocol/asset_ops.cpp
protocol/memo.cpp
protocol/worker.cpp
protocol/custom.cpp
protocol/operations.cpp
protocol/transaction.cpp
protocol/block.cpp
protocol/fee_schedule.cpp
operations.cpp
pts_address.cpp
evaluator.cpp
global_parameters_evaluator.cpp
balance_evaluator.cpp
account_evaluator.cpp
assert_evaluator.cpp
witness_evaluator.cpp
delegate_evaluator.cpp
committee_member_evaluator.cpp
asset_evaluator.cpp
transfer_evaluator.cpp
proposal_evaluator.cpp
short_order_evaluator.cpp
limit_order_evaluator.cpp
market_evaluator.cpp
vesting_balance_evaluator.cpp
withdraw_permission_evaluator.cpp
worker_evaluator.cpp
key_object.cpp
account_object.cpp
asset_object.cpp
proposal_object.cpp
vesting_balance_object.cpp
worker_object.cpp
transaction.cpp
block.cpp
transaction_evaluation_state.cpp
fork_database.cpp
block_database.cpp
db_balance.cpp
db_block.cpp
db_debug.cpp
db_getter.cpp
db_init.cpp
db_maint.cpp
db_management.cpp
db_market.cpp
db_update.cpp
db_witness_schedule.cpp
database.cpp
# db_balance.cpp
# db_block.cpp
# db_debug.cpp
# db_getter.cpp
# db_init.cpp
# db_maint.cpp
# db_management.cpp
# db_market.cpp
# db_update.cpp
# db_witness_schedule.cpp
${HEADERS}
)
target_link_libraries( graphene_chain fc graphene_db leveldb )
target_link_libraries( graphene_chain fc graphene_db )
target_include_directories( graphene_chain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
if(MSVC)
set_source_files_properties( db_init.cpp db_block.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
set_source_files_properties( db_init.cpp db_block.cpp database.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)

View file

@ -17,7 +17,6 @@
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/key_object.hpp>
#include <algorithm>
namespace graphene { namespace chain {
@ -25,38 +24,31 @@ namespace graphene { namespace chain {
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try {
database& d = db();
FC_ASSERT( d.find_object(op.voting_account) );
FC_ASSERT( is_relative(op.memo_key) || d.find_object(op.memo_key) );
FC_ASSERT( d.find_object(op.options.voting_account) );
FC_ASSERT( fee_paying_account->is_lifetime_member() );
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()) );
const auto& global_props = d.get_global_properties();
uint32_t max_vote_id = global_props.next_available_vote_id;
const auto& chain_params = global_props.parameters;
FC_ASSERT( op.num_witness <= chain_params.maximum_witness_count );
FC_ASSERT( op.num_committee <= chain_params.maximum_committee_count );
FC_ASSERT( op.owner.auths.size() <= chain_params.maximum_authority_membership );
FC_ASSERT( op.active.auths.size() <= chain_params.maximum_authority_membership );
for( auto id : op.owner.auths )
{
FC_ASSERT( is_relative(id.first) || d.find_object(id.first) );
}
for( auto id : op.active.auths )
{
FC_ASSERT( is_relative(id.first) || d.find_object(id.first) );
}
verify_authority_accounts( op.owner );
verify_authority_accounts( op.active );
uint32_t max_vote_id = global_props.next_available_vote_id;
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count );
FC_ASSERT( op.options.num_committee <= chain_params.maximum_committee_count );
safe<uint32_t> counts[vote_id_type::VOTE_TYPE_COUNT];
for( auto id : op.vote )
for( auto id : op.options.votes )
{
FC_ASSERT( id < max_vote_id );
counts[id.type()]++;
}
FC_ASSERT(counts[vote_id_type::witness] <= op.num_witness,
FC_ASSERT(counts[vote_id_type::witness] <= op.options.num_witness,
"",
("count", counts[vote_id_type::witness])("num", op.num_witness));
FC_ASSERT(counts[vote_id_type::committee] <= op.num_committee,
("count", counts[vote_id_type::witness])("num", op.options.num_witness));
FC_ASSERT(counts[vote_id_type::committee] <= op.options.num_committee,
"",
("count", counts[vote_id_type::committee])("num", op.num_committee));
("count", counts[vote_id_type::committee])("num", op.options.num_committee));
auto& acnt_indx = d.get_index_type<account_index>();
if( op.name.size() )
@ -65,37 +57,11 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
FC_ASSERT( current_account_itr == acnt_indx.indices().get<by_name>().end() );
}
// TODO: this check can be removed after GRAPHENE_LEGACY_NAME_IMPORT_PERIOD
// legacy account check
if( d.get_dynamic_global_properties().head_block_number < GRAPHENE_LEGACY_NAME_IMPORT_PERIOD )
{
auto legacy_account_itr = acnt_indx.indices().get<by_name>().find( "bts-"+op.name );
if( legacy_account_itr != acnt_indx.indices().get<by_name>().end() )
{
FC_ASSERT( fee_paying_account->id == legacy_account_itr->id );
}
}
// verify child account authority
auto pos = op.name.find( '/' );
if( pos != string::npos )
{
// TODO: lookup account by op.owner.auths[0] and verify the name
// this should be a constant time lookup rather than log(N)
auto parent_account_itr = acnt_indx.indices().get<by_name>().find( op.name.substr(0,pos) );
FC_ASSERT( parent_account_itr != acnt_indx.indices().get<by_name>().end() );
FC_ASSERT( verify_authority( *parent_account_itr, authority::owner ) );
FC_ASSERT( op.owner.auths.find( parent_account_itr->id ) != op.owner.auths.end() );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type account_create_evaluator::do_apply( const account_create_operation& o )
{ try {
auto owner = resolve_relative_ids( o.owner );
auto active = resolve_relative_ids( o.active );
const auto& stats_obj = db().create<account_statistics_object>( [&]( account_statistics_object& ){
});
@ -110,72 +76,63 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
obj.referrer_rewards_percentage = o.referrer_percent;
obj.name = o.name;
obj.owner = owner;
obj.active = active;
obj.owner = o.owner;
obj.active = o.active;
obj.statistics = stats_obj.id;
obj.memo_key = get_relative_id(o.memo_key);
obj.voting_account = o.voting_account;
obj.votes = o.vote;
obj.num_witness = o.num_witness;
obj.num_committee = o.num_committee;
obj.options = o.options;
});
const auto& dynamic_properties = db().get_dynamic_global_properties();
db().modify(dynamic_properties, [](dynamic_global_property_object& p) {
++p.accounts_registered_this_interval;
});
/** TODO: update fee scaling for account creation...
if( dynamic_properties.accounts_registered_this_interval %
global_properties.parameters.accounts_per_fee_scale == 0 )
db().modify(global_properties, [&dynamic_properties](global_property_object& p) {
p.parameters.current_fees.account_create_fee <<= p.parameters.account_fee_scale_bitshifts;
});
*/
return new_acnt_object.id;
} FC_CAPTURE_AND_RETHROW((o)) }
void_result account_update_evaluator::do_evaluate( const account_update_operation& o )
{
{ try {
database& d = db();
FC_ASSERT( !o.memo_key || is_relative(*o.memo_key) || db().find_object(*o.memo_key) );
const auto& chain_params = db().get_global_properties().parameters;
FC_ASSERT( o.num_witness <= chain_params.maximum_witness_count );
FC_ASSERT( o.num_committee <= chain_params.maximum_committee_count );
if( o.owner )
{
FC_ASSERT( o.owner->auths.size() <= chain_params.maximum_authority_membership );
for( auto id : o.owner->auths )
{
FC_ASSERT( is_relative(id.first) || db().find<object>(id.first) );
}
}
if( o.active )
{
FC_ASSERT( o.active->auths.size() <= chain_params.maximum_authority_membership );
for( auto id : o.active->auths )
{
FC_ASSERT( is_relative(id.first) || db().find<object>(id.first) );
}
}
if( o.owner ) verify_authority_accounts( *o.owner );
if( o.active ) verify_authority_accounts( *o.active );
acnt = &o.account(d);
if( o.vote )
if( o.new_options )
{
FC_ASSERT( o.new_options->num_witness <= chain_params.maximum_witness_count );
FC_ASSERT( o.new_options->num_committee <= chain_params.maximum_committee_count );
uint32_t max_vote_id = d.get_global_properties().next_available_vote_id;
for( auto id : *o.vote )
for( auto id : o.new_options->votes )
{
FC_ASSERT( id < max_vote_id );
}
}
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result account_update_evaluator::do_apply( const account_update_operation& o )
{
db().modify( *acnt, [&]( account_object& a ){
if( o.owner ) a.owner = *o.owner;
if( o.active ) a.active = *o.active;
if( o.voting_account ) a.voting_account = *o.voting_account;
if( o.memo_key ) a.memo_key = *o.memo_key;
if( o.vote ) a.votes = *o.vote;
a.num_witness = o.num_witness;
a.num_committee = o.num_committee;
});
{ try {
db().modify( *acnt, [&](account_object& a){
if( o.owner ) a.owner = *o.owner;
if( o.active ) a.active = *o.active;
if( o.new_options ) a.options = *o.new_options;
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_operation& o)
{ try {
@ -189,7 +146,7 @@ void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_ope
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result account_whitelist_evaluator::do_apply(const account_whitelist_operation& o)
{
{ try {
database& d = db();
d.modify(*listed_account, [&o](account_object& a) {
@ -204,26 +161,28 @@ void_result account_whitelist_evaluator::do_apply(const account_whitelist_operat
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluator::operation_type& o)
{
{ try {
database& d = db();
account = &d.get(o.account_to_upgrade);
FC_ASSERT(!account->is_lifetime_member());
return {};
}
//} FC_CAPTURE_AND_RETHROW( (o) ) }
} FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o)
{
{ try {
database& d = db();
d.modify(*account, [&](account_object& a) {
if( o.upgrade_to_lifetime_member )
{
// Upgrade to lifetime member. I don't care what the account was before.
a.statistics(d).process_fees(a, d);
a.membership_expiration_date = time_point_sec::maximum();
a.referrer = a.registrar = a.lifetime_referrer = a.get_id();
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;
@ -234,12 +193,13 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator:
a.membership_expiration_date += fc::days(365);
} else {
// Upgrade from basic account.
a.statistics(d).process_fees(a, d);
assert(a.is_basic_account(d.head_block_time()));
a.membership_expiration_date = d.head_block_time() + fc::days(365);
}
});
return {};
}
} FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
} } // graphene::chain

View file

@ -17,10 +17,24 @@
*/
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
share_type cut_fee(share_type a, uint16_t p)
{
if( a == 0 || p == 0 )
return 0;
if( p == GRAPHENE_100_PERCENT )
return a;
fc::uint128 r(a.value);
r *= p;
r /= GRAPHENE_100_PERCENT;
return r.to_uint64();
}
bool account_object::is_authorized_asset(const asset_object& asset_obj) const {
for( const auto id : blacklisting_accounts )
if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) return false;
@ -56,4 +70,204 @@ uint16_t account_statistics_object::calculate_bulk_discount_percent(const chain_
return bulk_discount_percent;
}
void account_statistics_object::process_fees(const account_object& a, database& d) const
{
if( pending_fees > 0 || pending_vested_fees > 0 )
{
const auto& props = d.get_global_properties();
auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting)
{
// Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead.
// No need to check the registrar; registrars are required to be lifetime members.
if( account.referrer(d).is_basic_account(d.head_block_time()) )
d.modify(account, [](account_object& a) {
a.referrer = a.lifetime_referrer;
});
share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage);
assert( network_cut <= core_fee_total );
#ifndef NDEBUG
share_type reserveed = cut_fee(network_cut, props.parameters.reserve_percent_of_fee);
share_type accumulated = network_cut - reserveed;
assert( accumulated + reserveed == network_cut );
#endif
share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage);
share_type referral = core_fee_total - network_cut - lifetime_cut;
d.modify(asset_dynamic_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) {
d.accumulated_fees += network_cut;
});
// Potential optimization: Skip some of this math and object lookups by special casing on the account type.
// For example, if the account is a lifetime member, we can skip all this and just deposit the referral to
// it directly.
share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage);
share_type registrar_cut = referral - referrer_cut;
d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting);
d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting);
d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting);
assert( referrer_cut + registrar_cut + accumulated + reserveed + lifetime_cut == core_fee_total );
};
share_type vesting_fee_subtotal(pending_fees);
share_type vested_fee_subtotal(pending_vested_fees);
share_type vesting_cashback, vested_cashback;
if( lifetime_fees_paid > props.parameters.bulk_discount_threshold_min &&
a.is_member(d.head_block_time()) )
{
auto bulk_discount_rate = calculate_bulk_discount_percent(props.parameters);
vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate);
vesting_fee_subtotal -= vesting_cashback;
vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate);
vested_fee_subtotal -= vested_cashback;
}
pay_out_fees(a, vesting_fee_subtotal, true);
d.deposit_cashback(a, vesting_cashback, true);
pay_out_fees(a, vested_fee_subtotal, false);
d.deposit_cashback(a, vested_cashback, false);
d.modify(*this, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) {
s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal;
s.pending_fees = 0;
s.pending_vested_fees = 0;
});
}
}
void account_object::options_type::validate() const
{
auto needed_witnesses = num_witness;
auto needed_committee = num_committee;
for( vote_id_type id : votes )
if( id.type() == vote_id_type::witness && needed_witnesses )
--needed_witnesses;
else if ( id.type() == vote_id_type::committee && needed_committee )
--needed_committee;
FC_ASSERT( needed_witnesses == 0 && needed_committee == 0,
"May not specify fewer witnesses or committee members than the number voted for.");
}
set<account_id_type> account_member_index::get_account_members(const account_object& a)const
{
set<account_id_type> result;
for( auto auth : a.owner.account_auths )
result.insert(auth.first);
return result;
}
set<public_key_type> account_member_index::get_key_members(const account_object& a)const
{
set<public_key_type> result;
for( auto auth : a.owner.key_auths )
result.insert(auth.first);
return result;
}
void account_member_index::object_inserted(const object& obj)
{
assert( dynamic_cast<const account_object*>(&obj) ); // for debug only
const account_object& a = static_cast<const account_object&>(obj);
auto account_members = get_account_members(a);
for( auto item : account_members )
account_to_account_memberships[item].insert(obj.id);
auto key_members = get_key_members(a);
for( auto item : key_members )
account_to_key_memberships[item].insert(obj.id);
}
void account_member_index::object_removed(const object& obj)
{
assert( dynamic_cast<const account_object*>(&obj) ); // for debug only
const account_object& a = static_cast<const account_object&>(obj);
auto key_members = get_key_members(a);
for( auto item : key_members )
account_to_key_memberships[item].erase( obj.id );
auto account_members = get_account_members(a);
for( auto item : account_members )
account_to_account_memberships[item].erase( obj.id );
}
void account_member_index::about_to_modify(const object& before)
{
before_key_members.clear();
before_account_members.clear();
assert( dynamic_cast<const account_object*>(&before) ); // for debug only
const account_object& a = static_cast<const account_object&>(before);
before_key_members = get_key_members(a);
before_account_members = get_account_members(a);
}
void account_member_index::object_modified(const object& after)
{
assert( dynamic_cast<const account_object*>(&after) ); // for debug only
const account_object& a = static_cast<const account_object&>(after);
set<account_id_type> after_account_members = get_account_members(a);
{
vector<account_id_type> removed; removed.reserve(before_account_members.size());
std::set_difference(before_account_members.begin(), before_account_members.end(),
after_account_members.begin(), after_account_members.end(),
std::inserter(removed, removed.end()));
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_account_memberships[*itr].erase(after.id);
vector<object_id_type> added; added.reserve(after_account_members.size());
std::set_difference(after_account_members.begin(), after_account_members.end(),
before_account_members.begin(), before_account_members.end(),
std::inserter(added, added.end()));
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_account_memberships[*itr].insert(after.id);
}
{
set<public_key_type> after_key_members = get_key_members(a);
vector<public_key_type> removed; removed.reserve(before_key_members.size());
std::set_difference(before_key_members.begin(), before_key_members.end(),
after_key_members.begin(), after_key_members.end(),
std::inserter(removed, removed.end()));
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_key_memberships[*itr].erase(after.id);
vector<public_key_type> added; added.reserve(after_key_members.size());
std::set_difference(after_key_members.begin(), after_key_members.end(),
before_key_members.begin(), before_key_members.end(),
std::inserter(added, added.end()));
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_key_memberships[*itr].insert(after.id);
}
}
void account_referrer_index::object_inserted( const object& obj )
{
}
void account_referrer_index::object_removed( const object& obj )
{
}
void account_referrer_index::about_to_modify( const object& before )
{
}
void account_referrer_index::object_modified( const object& after )
{
}
} } // graphene::chain

View file

@ -15,26 +15,53 @@
* 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/chain/global_parameters_evaluator.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/assert_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <sstream>
namespace graphene { namespace chain {
void_result global_parameters_update_evaluator::do_evaluate(const global_parameters_update_operation& o)
struct predicate_evaluator
{
FC_ASSERT(trx_state->_is_proposed_trx);
typedef void result_type;
const database& db;
predicate_evaluator( const database& d ):db(d){}
void operator()( const account_name_eq_lit_predicate& p )const
{
FC_ASSERT( p.account_id(db).name == p.name );
}
void operator()( const asset_symbol_eq_lit_predicate& p )const
{
FC_ASSERT( p.asset_id(db).symbol == p.symbol );
}
};
void_result assert_evaluator::do_evaluate( const assert_operation& o )
{ try {
const database& _db = db();
uint32_t skip = _db.get_node_properties().skip_flags;
auto max_predicate_opcode = _db.get_global_properties().parameters.max_predicate_opcode;
if( skip & database::skip_assert_evaluation )
return void_result();
for( const auto& p : o.predicates )
{
FC_ASSERT( p.which() >= 0 );
FC_ASSERT( unsigned(p.which()) < max_predicate_opcode );
p.visit( predicate_evaluator( _db ) );
}
return void_result();
}
void_result global_parameters_update_evaluator::do_apply(const global_parameters_update_operation& o)
{
db().modify(db().get_global_properties(), [&o](global_property_object& p) {
p.pending_parameters = o.new_parameters;
});
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result assert_evaluator::do_apply( const assert_operation& o )
{ try {
// assert_operation is always a no-op
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -18,13 +18,14 @@
#include <graphene/chain/asset_evaluator.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <functional>
namespace graphene { namespace chain {
object_id_type asset_create_evaluator::do_evaluate( const asset_create_operation& op )
void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op )
{ try {
database& d = db();
@ -42,38 +43,46 @@ object_id_type asset_create_evaluator::do_evaluate( const asset_create_operation
auto asset_symbol_itr = asset_indx.find( op.symbol );
FC_ASSERT( asset_symbol_itr == asset_indx.end() );
core_fee_paid -= op.calculate_fee(d.current_fee_schedule()).value/2;
assert( core_fee_paid >= 0 );
core_fee_paid -= core_fee_paid.value/2;
if( op.bitasset_options )
if( op.bitasset_opts )
{
const asset_object& backing = op.bitasset_options->short_backing_asset(d);
const asset_object& backing = op.bitasset_opts->short_backing_asset(d);
if( backing.is_market_issued() )
{
const asset_bitasset_data_object& backing_bitasset_data = backing.bitasset_data(d);
const asset_object& backing_backing = backing_bitasset_data.options.short_backing_asset(d);
FC_ASSERT( !backing_backing.is_market_issued(),
"May not create a bitasset backed by a bitasset backed by a bitasset." );
}
FC_ASSERT( op.bitasset_options->feed_lifetime_sec > chain_parameters.block_interval &&
op.bitasset_options->force_settlement_delay_sec > chain_parameters.block_interval );
FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing_backing.get_id() == asset_id_type(),
"May not create a blockchain-controlled market asset which is not backed by CORE.");
} else
FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing.get_id() == asset_id_type(),
"May not create a blockchain-controlled market asset which is not backed by CORE.");
FC_ASSERT( op.bitasset_opts->feed_lifetime_sec > chain_parameters.block_interval &&
op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval );
}
if( op.is_prediction_market )
{
FC_ASSERT( op.bitasset_opts );
FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision );
}
return object_id_type();
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type asset_create_evaluator::do_apply( const asset_create_operation& op )
{
{ try {
const asset_dynamic_data_object& dyn_asset =
db().create<asset_dynamic_data_object>( [&]( asset_dynamic_data_object& a ) {
a.current_supply = 0;
a.fee_pool = op.calculate_fee(db().current_fee_schedule()).value / 2;
a.fee_pool = core_fee_paid; //op.calculate_fee(db().current_fee_schedule()).value / 2;
});
asset_bitasset_data_id_type bit_asset_id;
if( op.bitasset_options.valid() )
if( op.bitasset_opts.valid() )
bit_asset_id = db().create<asset_bitasset_data_object>( [&]( asset_bitasset_data_object& a ) {
a.options = *op.bitasset_options;
a.options = *op.bitasset_opts;
a.is_prediction_market = op.is_prediction_market;
}).id;
@ -90,13 +99,13 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o
else
a.options.core_exchange_rate.base.asset_id = next_asset_id;
a.dynamic_asset_data_id = dyn_asset.id;
if( op.bitasset_options.valid() )
if( op.bitasset_opts.valid() )
a.bitasset_data_id = bit_asset_id;
});
assert( new_asset.id == next_asset_id );
return next_asset_id;
}
return new_asset.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
{ try {
@ -120,7 +129,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o )
{
{ try {
db().adjust_balance( o.issue_to_account, o.asset_to_issue );
db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){
@ -128,13 +137,13 @@ void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o )
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_burn_evaluator::do_evaluate( const asset_burn_operation& o )
void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& o )
{ try {
database& d = db();
const asset_object& a = o.amount_to_burn.asset_id(d);
const asset_object& a = o.amount_to_reserve.asset_id(d);
FC_ASSERT( !a.is_market_issued() );
from_account = &o.payer(d);
@ -145,21 +154,21 @@ void_result asset_burn_evaluator::do_evaluate( const asset_burn_operation& o )
}
asset_dyn_data = &a.dynamic_asset_data_id(d);
FC_ASSERT( (asset_dyn_data->current_supply - o.amount_to_burn.amount) >= 0 );
FC_ASSERT( (asset_dyn_data->current_supply - o.amount_to_reserve.amount) >= 0 );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_burn_evaluator::do_apply( const asset_burn_operation& o )
{
db().adjust_balance( o.payer, -o.amount_to_burn );
void_result asset_reserve_evaluator::do_apply( const asset_reserve_operation& o )
{ try {
db().adjust_balance( o.payer, -o.amount_to_reserve );
db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){
data.current_supply -= o.amount_to_burn.amount;
data.current_supply -= o.amount_to_reserve.amount;
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_fund_fee_pool_evaluator::do_evaluate(const asset_fund_fee_pool_operation& o)
{ try {
@ -173,7 +182,7 @@ void_result asset_fund_fee_pool_evaluator::do_evaluate(const asset_fund_fee_pool
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_operation& o)
{
{ try {
db().adjust_balance(o.from_account, -o.amount);
db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ) {
@ -181,19 +190,34 @@ void_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_op
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
{ try {
database& d = db();
if( o.new_issuer ) FC_ASSERT(d.find_object(*o.new_issuer));
const asset_object& a = o.asset_to_update(d);
auto a_copy = a;
a_copy.options = o.new_options;
a_copy.validate();
if( o.new_issuer )
{
FC_ASSERT(d.find_object(*o.new_issuer));
if( a.is_market_issued() && *o.new_issuer == GRAPHENE_COMMITTEE_ACCOUNT )
{
const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d);
if( backing.is_market_issued() )
{
const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d);
FC_ASSERT( backing_backing.get_id() == asset_id_type(),
"May not create a blockchain-controlled market asset which is not backed by CORE.");
} else
FC_ASSERT( backing.get_id() == asset_id_type(),
"May not create a blockchain-controlled market asset which is not backed by CORE.");
}
}
//There must be no bits set in o.permissions which are unset in a.issuer_permissions.
FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions),
"Cannot reinstate previously revoked issuer permissions on an asset.");
@ -214,7 +238,7 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
} FC_CAPTURE_AND_RETHROW((o)) }
void_result asset_update_evaluator::do_apply(const asset_update_operation& o)
{
{ try {
database& d = db();
// If we are now disabling force settlements, cancel all open force settlement orders
@ -236,10 +260,10 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o)
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bitasset_operation& o)
{
{ try {
database& d = db();
const asset_object& a = o.asset_to_update(d);
@ -247,29 +271,51 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita
FC_ASSERT(a.is_market_issued(), "Cannot update BitAsset-specific settings on a non-BitAsset.");
const asset_bitasset_data_object& b = a.bitasset_data(d);
FC_ASSERT( !b.has_settlement(), "Cannot update a bitasset after a settlement has executed" );
if( o.new_options.short_backing_asset != b.options.short_backing_asset )
{
FC_ASSERT(a.dynamic_asset_data_id(d).current_supply == 0);
FC_ASSERT(d.find_object(o.new_options.short_backing_asset));
if( a.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
{
const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d);
if( backing.is_market_issued() )
{
const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d);
FC_ASSERT( backing_backing.get_id() == asset_id_type(),
"May not create a blockchain-controlled market asset which is not backed by CORE.");
} else
FC_ASSERT( backing.get_id() == asset_id_type(),
"May not create a blockchain-controlled market asset which is not backed by CORE.");
}
}
bitasset_to_update = &b;
FC_ASSERT( o.issuer == a.issuer, "", ("o.issuer", o.issuer)("a.issuer", a.issuer) );
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_bitasset_evaluator::do_apply(const asset_update_bitasset_operation& o)
{
db().modify(*bitasset_to_update, [&o](asset_bitasset_data_object& b) {
{ try {
bool should_update_feeds = false;
// If the minimum number of feeds to calculate a median has changed, we need to recalculate the median
if( o.new_options.minimum_feeds != bitasset_to_update->options.minimum_feeds )
should_update_feeds = true;
db().modify(*bitasset_to_update, [&](asset_bitasset_data_object& b) {
b.options = o.new_options;
if( should_update_feeds )
b.update_median_feeds(db().head_block_time());
});
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_feed_producers_evaluator::do_evaluate(const asset_update_feed_producers_evaluator::operation_type& o)
{
{ try {
database& d = db();
FC_ASSERT( o.new_feed_producers.size() <= d.get_global_properties().parameters.maximum_asset_feed_publishers );
@ -279,16 +325,16 @@ void_result asset_update_feed_producers_evaluator::do_evaluate(const asset_updat
const asset_object& a = o.asset_to_update(d);
FC_ASSERT(a.is_market_issued(), "Cannot update feed producers on a non-BitAsset.");
FC_ASSERT(a.issuer != account_id_type(), "Cannot set feed producers on a genesis-issued asset.");
FC_ASSERT(a.issuer != GRAPHENE_COMMITTEE_ACCOUNT, "Cannot set feed producers on a committee-issued asset.");
const asset_bitasset_data_object& b = a.bitasset_data(d);
bitasset_to_update = &b;
FC_ASSERT( a.issuer == o.issuer );
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_feed_producers_evaluator::operation_type& o)
{
{ try {
db().modify(*bitasset_to_update, [&](asset_bitasset_data_object& a) {
//This is tricky because I have a set of publishers coming in, but a map of publisher to feed is stored.
//I need to update the map such that the keys match the new publishers, but not munge the old price feeds from
@ -307,13 +353,13 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f
a.feeds[*itr];
a.update_median_feeds(db().head_block_time());
});
db().check_call_orders( o.asset_to_update(db()) );
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_global_settle_evaluator::do_evaluate(const asset_global_settle_evaluator::operation_type& op)
{
{ try {
const database& d = db();
asset_to_settle = &op.asset_to_settle(d);
FC_ASSERT(asset_to_settle->is_market_issued());
@ -330,52 +376,82 @@ void_result asset_global_settle_evaluator::do_evaluate(const asset_global_settle
"Cannot force settle at supplied price: least collateralized short lacks sufficient collateral to settle.");
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result asset_global_settle_evaluator::do_apply(const asset_global_settle_evaluator::operation_type& op)
{
{ try {
database& d = db();
d.globally_settle_asset( op.asset_to_settle(db()), op.settle_price );
return void_result();
}
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type asset_settle_evaluator::do_evaluate(const asset_settle_evaluator::operation_type& op)
{
void_result asset_settle_evaluator::do_evaluate(const asset_settle_evaluator::operation_type& op)
{ try {
const database& d = db();
asset_to_settle = &op.amount.asset_id(d);
FC_ASSERT(asset_to_settle->is_market_issued());
FC_ASSERT(asset_to_settle->can_force_settle());
const auto& bitasset = asset_to_settle->bitasset_data(d);
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 d.get_index_type<force_settlement_index>().get_next_id();
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op)
{
operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op)
{ try {
database& d = db();
d.adjust_balance(op.account, -op.amount);
return d.create<force_settlement_object>([&](force_settlement_object& s) {
s.owner = op.account;
s.balance = op.amount;
s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec;
}).id;
}
const auto& bitasset = asset_to_settle->bitasset_data(d);
if( bitasset.has_settlement() )
{
auto settled_amount = op.amount * bitasset.settlement_price;
FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund );
d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
obj.settlement_fund -= settled_amount.amount;
});
d.adjust_balance(op.account, settled_amount);
const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d);
d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
obj.current_supply -= op.amount.amount;
});
return settled_amount;
}
else
{
return d.create<force_settlement_object>([&](force_settlement_object& s) {
s.owner = op.account;
s.balance = op.amount;
s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec;
}).id;
}
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_operation& o)
{ try {
database& d = db();
const asset_object& quote = o.asset_id(d);
const asset_object& base = o.asset_id(d);
//Verify that this feed is for a market-issued asset and that asset is backed by the base
FC_ASSERT(quote.is_market_issued());
FC_ASSERT(base.is_market_issued());
const asset_bitasset_data_object& bitasset = quote.bitasset_data(d);
FC_ASSERT(bitasset.options.short_backing_asset == o.feed.call_limit.base.asset_id);
const asset_bitasset_data_object& bitasset = base.bitasset_data(d);
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
FC_ASSERT(o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset);
//Verify that the publisher is authoritative to publish a feed
if( quote.issuer == account_id_type() )
if( base.issuer == account_id_type() )
{
//It's a delegate-fed asset. Verify that publisher is an active delegate or witness.
FC_ASSERT(d.get(account_id_type()).active.auths.count(o.publisher) ||
//It's a committee_member-fed asset. Verify that publisher is an active committee_member or witness.
FC_ASSERT(d.get(GRAPHENE_COMMITTEE_ACCOUNT).active.account_auths.count(o.publisher) ||
d.get_global_properties().witness_accounts.count(o.publisher));
} else {
FC_ASSERT(bitasset.feeds.count(o.publisher));
@ -388,14 +464,20 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
{ try {
database& d = db();
const asset_object& quote = o.asset_id(d);
const asset_object& base = o.asset_id(d);
const asset_bitasset_data_object& bad = base.bitasset_data(d);
auto old_feed = bad.current_feed;
// Store medians for this asset
d.modify(quote.bitasset_data(d), [&o,&d](asset_bitasset_data_object& a) {
d.modify(bad , [&o,&d](asset_bitasset_data_object& a) {
a.feeds[o.publisher] = make_pair(d.head_block_time(), o.feed);
a.update_median_feeds(d.head_block_time());
});
if( !(old_feed == bad.current_feed) )
db().check_call_orders(base);
return void_result();
} FC_CAPTURE_AND_RETHROW((o)) }
} FC_CAPTURE_AND_RETHROW((o)) }
} } // graphene::chain

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <fc/uint128.hpp>
@ -50,8 +51,10 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point
}
}
if( current_feeds.empty() )
// If there are no valid feeds, or the number available is less than the minimum to calculate a median...
if( current_feeds.size() < options.minimum_feeds )
{
//... don't calculate a median, and set a null feed
current_feed_publication_time = current_time;
current_feed = price_feed();
return;
@ -79,40 +82,6 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point
current_feed = median_feed;
}
void asset_object::asset_options::validate()const
{
FC_ASSERT( max_supply > 0 );
FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY );
FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT );
FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY );
FC_ASSERT( min_market_fee >= 0 && min_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY );
// There must be no high bits in permissions whose meaning is not known.
FC_ASSERT( !(issuer_permissions & ~ASSET_ISSUER_PERMISSION_MASK) );
// There must be no high bits in flags which are not also high in permissions.
FC_ASSERT( !(flags & ~issuer_permissions ) );
// The global_settle flag may never be set (this is a permission only)
FC_ASSERT( !(flags & global_settle) );
core_exchange_rate.validate();
FC_ASSERT( core_exchange_rate.base.asset_id.instance.value == 0 ||
core_exchange_rate.quote.asset_id.instance.value == 0 );
if(!whitelist_authorities.empty() || !blacklist_authorities.empty())
FC_ASSERT( flags & white_list );
for( auto item : whitelist_markets )
{
FC_ASSERT( blacklist_markets.find(item) == blacklist_markets.end() );
}
for( auto item : blacklist_markets )
{
FC_ASSERT( whitelist_markets.find(item) == whitelist_markets.end() );
}
}
void asset_object::bitasset_options::validate() const
{
FC_ASSERT(force_settlement_offset_percent <= GRAPHENE_100_PERCENT);
FC_ASSERT(maximum_force_settlement_volume <= GRAPHENE_100_PERCENT);
}
asset asset_object::amount_from_string(string amount_string) const
@ -142,7 +111,7 @@ asset asset_object::amount_from_string(string amount_string) const
share_type satoshis = 0;
share_type scaled_precision = 1;
for( short i = 0; i < precision; ++i )
for( uint8_t i = 0; i < precision; ++i )
scaled_precision *= 10;
const auto decimal_pos = amount_string.find( '.' );
@ -164,7 +133,7 @@ asset asset_object::amount_from_string(string amount_string) const
satoshis += std::stoll( rhs );
}
FC_ASSERT( satoshis <= GRAPHENE_BLOCKCHAIN_MAX_SHARES );
FC_ASSERT( satoshis <= GRAPHENE_MAX_SHARE_SUPPLY );
if( negative_found )
satoshis *= -1;
@ -175,7 +144,7 @@ asset asset_object::amount_from_string(string amount_string) const
string asset_object::amount_to_string(share_type amount) const
{
share_type scaled_precision = 1;
for( short i = 0; i < precision; ++i )
for( uint8_t i = 0; i < precision; ++i )
scaled_precision *= 10;
assert(scaled_precision > 0);

View file

@ -0,0 +1,69 @@
#include <graphene/chain/balance_evaluator.hpp>
namespace graphene { namespace chain {
void_result balance_claim_evaluator::do_evaluate(const balance_claim_operation& op)
{
database& d = db();
balance = &op.balance_to_claim(d);
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_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() )
{
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 {};
}
FC_ASSERT(op.total_claimed == balance->balance);
return {};
}
/**
* @note the fee is always 0 for this particular operation because once the
* balance is claimed it frees up memory and it cannot be used to spam the network
*/
void_result balance_claim_evaluator::do_apply(const balance_claim_operation& op)
{
database& d = db();
if( balance->is_vesting_balance() && op.total_claimed < balance->balance )
d.modify(*balance, [&](balance_object& b) {
b.vesting_policy->on_withdraw({b.balance, d.head_block_time(), op.total_claimed});
b.balance -= op.total_claimed;
b.last_claim_date = d.head_block_time();
});
else
d.remove(*balance);
d.adjust_balance(op.deposit_to_account, op.total_claimed);
return {};
}
} } // namespace graphene::chain

View file

@ -0,0 +1,225 @@
/*
* 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.
*/
#include <graphene/chain/block_database.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/io/raw.hpp>
namespace graphene { namespace chain {
struct index_entry
{
uint64_t block_pos = 0;
uint32_t block_size = 0;
block_id_type block_id;
};
}}
FC_REFLECT( graphene::chain::index_entry, (block_pos)(block_size)(block_id) );
namespace graphene { namespace chain {
void block_database::open( const fc::path& dbdir )
{ try {
fc::create_directories(dbdir);
_block_num_to_pos.exceptions(std::ios_base::failbit | std::ios_base::badbit);
_blocks.exceptions(std::ios_base::failbit | std::ios_base::badbit);
if( !fc::exists( dbdir/"index" ) )
{
_block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
_blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
}
else
{
_block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
_blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
}
} FC_CAPTURE_AND_RETHROW( (dbdir) ) }
bool block_database::is_open()const
{
return _blocks.is_open();
}
void block_database::close()
{
_blocks.close();
_block_num_to_pos.close();
}
void block_database::flush()
{
_blocks.flush();
_block_num_to_pos.flush();
}
void block_database::store( const block_id_type& id, const signed_block& b )
{
auto num = block_header::num_from_id(id);
_block_num_to_pos.seekp( sizeof( index_entry ) * num );
index_entry e;
_blocks.seekp( 0, _blocks.end );
auto vec = fc::raw::pack( b );
e.block_pos = _blocks.tellp();
e.block_size = vec.size();
e.block_id = id;
_blocks.write( vec.data(), vec.size() );
_block_num_to_pos.write( (char*)&e, sizeof(e) );
}
void block_database::remove( const block_id_type& id )
{ try {
index_entry e;
auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos )
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id));
_block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
if( e.block_id == id )
{
e.block_size = 0;
_block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) );
_block_num_to_pos.write( (char*)&e, sizeof(e) );
}
} FC_CAPTURE_AND_RETHROW( (id) ) }
bool block_database::contains( const block_id_type& id )const
{
index_entry e;
auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos )
return false;
_block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
return e.block_id == id;
}
block_id_type block_database::fetch_block_id( uint32_t block_num )const
{
index_entry e;
auto index_pos = sizeof(e)*block_num;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos )
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block number ${block_num} not contained in block database", ("block_num", block_num));
_block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
return e.block_id;
}
optional<signed_block> block_database::fetch_optional( const block_id_type& id )const
{
try
{
index_entry e;
auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos )
return {};
_block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
if( e.block_id != id ) return optional<signed_block>();
vector<char> data( e.block_size );
_blocks.seekg( e.block_pos );
_blocks.read( data.data(), e.block_size );
auto result = fc::raw::unpack<signed_block>(data);
FC_ASSERT( result.id() == e.block_id );
return result;
}
catch (const fc::exception&)
{
}
catch (const std::exception&)
{
}
return optional<signed_block>();
}
optional<signed_block> block_database::fetch_by_number( uint32_t block_num )const
{
try
{
index_entry e;
auto index_pos = sizeof(e)*block_num;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos )
return {};
_block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
vector<char> data( e.block_size );
_blocks.seekg( e.block_pos );
_blocks.read( data.data(), e.block_size );
auto result = fc::raw::unpack<signed_block>(data);
FC_ASSERT( result.id() == e.block_id );
return result;
}
catch (const fc::exception&)
{
}
catch (const std::exception&)
{
}
return optional<signed_block>();
}
optional<signed_block> block_database::last()const
{
try
{
index_entry e;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if( _block_num_to_pos.tellp() < sizeof(index_entry) )
return optional<signed_block>();
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
while( e.block_size == 0 && _blocks.tellg() > 0 )
{
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.cur );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
}
if( e.block_size == 0 )
return optional<signed_block>();
vector<char> data( e.block_size );
_blocks.seekg( e.block_pos );
_blocks.read( data.data(), e.block_size );
auto result = fc::raw::unpack<signed_block>(data);
return result;
}
catch (const fc::exception&)
{
}
catch (const std::exception&)
{
}
return optional<signed_block>();
}
} }

View file

@ -15,31 +15,52 @@
* 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/chain/delegate_evaluator.hpp>
#include <graphene/chain/delegate_object.hpp>
#include <graphene/chain/key_object.hpp>
#include <graphene/chain/committee_member_evaluator.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
namespace graphene { namespace chain {
object_id_type delegate_create_evaluator::do_evaluate( const delegate_create_operation& op )
{
FC_ASSERT(db().get(op.delegate_account).is_lifetime_member());
return object_id_type();
}
object_id_type delegate_create_evaluator::do_apply( const delegate_create_operation& op )
{
void_result committee_member_create_evaluator::do_evaluate( const committee_member_create_operation& op )
{ try {
FC_ASSERT(db().get(op.committee_member_account).is_lifetime_member());
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type committee_member_create_evaluator::do_apply( const committee_member_create_operation& op )
{ try {
vote_id_type vote_id;
db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) {
vote_id = p.get_next_vote_id(vote_id_type::committee);
});
const auto& new_del_object = db().create<delegate_object>( [&]( delegate_object& obj ){
obj.delegate_account = op.delegate_account;
const auto& new_del_object = db().create<committee_member_object>( [&]( committee_member_object& obj ){
obj.committee_member_account = op.committee_member_account;
obj.vote_id = vote_id;
obj.url = op.url;
});
return new_del_object.id;
}
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result committee_member_update_global_parameters_evaluator::do_evaluate(const committee_member_update_global_parameters_operation& o)
{ try {
FC_ASSERT(trx_state->_is_proposed_trx);
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result committee_member_update_global_parameters_evaluator::do_apply(const committee_member_update_global_parameters_operation& o)
{ try {
db().modify(db().get_global_properties(), [&o](global_property_object& p) {
p.pending_parameters = o.new_parameters;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -0,0 +1,12 @@
/// This file combines these sources to perform a partial unity build
#include <fc/smart_ref_impl.hpp>
#include "db_balance.cpp"
#include "db_block.cpp"
#include "db_debug.cpp"
#include "db_getter.cpp"
#include "db_init.cpp"
#include "db_maint.cpp"
#include "db_management.cpp"
#include "db_market.cpp"
#include "db_update.cpp"
#include "db_witness_schedule.cpp"

View file

@ -38,10 +38,9 @@ asset database::get_balance(const account_object& owner, const asset_object& ass
return get_balance(owner.get_id(), asset_obj.get_id());
}
// TODO: this method should be removed
asset database::get_balance( const account_object* owner, const asset_object* asset_obj )const
string database::to_pretty_string( const asset& a )const
{
return get_balance(*owner, *asset_obj);
return a.asset_id(*this).amount_to_pretty_string(a.amount);
}
void database::adjust_balance(account_id_type account, asset delta )
@ -53,14 +52,18 @@ void database::adjust_balance(account_id_type account, asset delta )
auto itr = index.find(boost::make_tuple(account, delta.asset_id));
if(itr == index.end())
{
FC_ASSERT(delta.amount > 0);
FC_ASSERT( delta.amount > 0, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}",
("a",account(*this).name)
("b",to_pretty_string(asset(0,delta.asset_id)))
("r",to_pretty_string(-delta)));
create<account_balance_object>([account,&delta](account_balance_object& b) {
b.owner = account;
b.asset_type = delta.asset_id;
b.balance = delta.amount.value;
});
} else {
FC_ASSERT(delta.amount > 0 || itr->get_balance() >= -delta);
if( delta.amount < 0 )
FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta)));
modify(*itr, [delta](account_balance_object& b) {
b.adjust_balance(delta);
});
@ -73,12 +76,6 @@ void database::adjust_balance(const account_object& account, asset delta )
adjust_balance( account.id, delta);
}
// TODO: This method should be removed
void database::adjust_balance(const account_object* account, asset delta)
{
adjust_balance(*account, delta);
}
void database::adjust_core_in_orders( const account_object& acnt, asset delta )
{
if( delta.asset_id == asset_id_type(0) && delta.amount != 0 )
@ -97,6 +94,17 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b
if( amount == 0 )
return;
if( acct.get_id() == GRAPHENE_COMMITTEE_ACCOUNT || acct.get_id() == GRAPHENE_WITNESS_ACCOUNT ||
acct.get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT || acct.get_id() == GRAPHENE_NULL_ACCOUNT ||
acct.get_id() == GRAPHENE_TEMP_ACCOUNT )
{
// The blockchain's accounts do not get cashback; it simply goes to the reserve pool.
modify(get(asset_id_type()).dynamic_asset_data_id(*this), [amount](asset_dynamic_data_object& d) {
d.current_supply -= amount;
});
return;
}
uint32_t global_vesting_seconds = get_global_properties().parameters.cashback_vesting_period_seconds;
fc::time_point_sec now = head_block_time();

View file

@ -20,17 +20,17 @@
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/key_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
namespace graphene { namespace chain {
bool database::is_known_block( const block_id_type& id )const
{
return _fork_db.is_known_block(id) || _block_id_to_block.find(id).valid();
return _fork_db.is_known_block(id) || _block_id_to_block.contains(id);
}
/**
* Only return true *if* the transaction has not expired or been invalidated. If this
@ -45,10 +45,7 @@ bool database::is_known_transaction( const transaction_id_type& id )const
block_id_type database::get_block_id_for_num( uint32_t block_num )const
{ try {
block_id_type lb; lb._hash[0] = htonl(block_num);
auto itr = _block_id_to_block.lower_bound( lb );
FC_ASSERT( itr.valid() && itr.key()._hash[0] == lb._hash[0] );
return itr.key();
return _block_id_to_block.fetch_block_id( block_num );
} FC_CAPTURE_AND_RETHROW( (block_num) ) }
optional<signed_block> database::fetch_block_by_id( const block_id_type& id )const
@ -65,12 +62,7 @@ optional<signed_block> database::fetch_block_by_number( uint32_t num )const
if( results.size() == 1 )
return results[0]->data;
else
{
block_id_type lb; lb._hash[0] = htonl(num);
auto itr = _block_id_to_block.lower_bound( lb );
if( itr.valid() && itr.key()._hash[0] == lb._hash[0] )
return itr.value();
}
return _block_id_to_block.fetch_by_number(num);
return optional<signed_block>();
}
@ -88,29 +80,31 @@ const signed_transaction& database::get_recent_transaction(const transaction_id_
*
* @return true if we switched forks as a result of this push.
*/
bool database::push_block( const signed_block& new_block, uint32_t skip )
bool database::push_block(const signed_block& new_block, uint32_t skip)
{
bool result;
with_skip_flags( skip, [&]()
{
result = _push_block( new_block );
} );
return result;
}
bool database::_push_block(const signed_block& new_block)
{ try {
uint32_t skip = get_node_properties().skip_flags;
if( !(skip&skip_fork_db) )
{
wdump((new_block.id())(new_block.previous));
auto new_head = _fork_db.push_block( new_block );
auto new_head = _fork_db.push_block(new_block);
//If the head block from the longest chain does not build off of the current head, we need to switch forks.
if( new_head->data.previous != head_block_id() )
{
edump((new_head->data.previous));
//If the newly pushed block is the same height as head, we get head back in new_head
//Only switch forks if new_head is actually higher than head
if( new_head->data.block_num() > head_block_num() )
{
auto branches = _fork_db.fetch_branch_from( new_head->data.id(), _pending_block.previous );
for( auto item : branches.first )
{
wdump( ("new")(item->id)(item->data.previous) );
}
for( auto item : branches.second )
{
wdump( ("old")(item->id)(item->data.previous) );
}
auto branches = _fork_db.fetch_branch_from(new_head->data.id(), _pending_block.previous);
// pop blocks until we hit the forked block
while( head_block_id() != branches.second.back()->data.previous )
@ -123,14 +117,12 @@ bool database::push_block( const signed_block& new_block, uint32_t skip )
try {
auto session = _undo_db.start_undo_session();
apply_block( (*ritr)->data, skip );
_block_id_to_block.store( new_block.id(), (*ritr)->data );
_block_id_to_block.store( (*ritr)->id, (*ritr)->data );
session.commit();
}
catch ( const fc::exception& e ) { except = e; }
if( except )
{
elog( "Encountered error when switching to a longer fork at id ${id}. Going back.",
("id", (*ritr)->id) );
// remove the rest of branches.first from the fork_db, those blocks are invalid
while( ritr != branches.first.rend() )
{
@ -167,8 +159,8 @@ bool database::push_block( const signed_block& new_block, uint32_t skip )
try {
auto session = _undo_db.start_undo_session();
apply_block( new_block, skip );
_block_id_to_block.store( new_block.id(), new_block );
apply_block(new_block, skip);
_block_id_to_block.store(new_block.id(), new_block);
session.commit();
} catch ( const fc::exception& e ) {
elog("Failed to push new block:\n${e}", ("e", e.to_detail_string()));
@ -189,18 +181,29 @@ bool database::push_block( const signed_block& new_block, uint32_t skip )
* queues.
*/
processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip )
{ try {
processed_transaction result;
with_skip_flags( skip, [&]()
{
result = _push_transaction( trx );
} );
return result;
} FC_CAPTURE_AND_RETHROW( (trx) ) }
processed_transaction database::_push_transaction( const signed_transaction& trx )
{
//wdump((trx.digest())(trx.id()));
uint32_t skip = get_node_properties().skip_flags;
// If this is the first transaction pushed after applying a block, start a new undo session.
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
if( !_pending_block_session ) _pending_block_session = _undo_db.start_undo_session();
auto session = _undo_db.start_undo_session();
auto processed_trx = apply_transaction( trx, skip );
auto processed_trx = _apply_transaction( trx );
_pending_block.transactions.push_back(processed_trx);
FC_ASSERT( (skip & skip_block_size_check) ||
fc::raw::pack_size(_pending_block) <= get_global_properties().parameters.maximum_block_size );
notify_changed_objects();
// The transaction applied successfully. Merge its changes into the pending block session.
session.merge();
return processed_trx;
@ -225,9 +228,6 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
return std::make_pair(id, authority::owner);
});
ilog("Attempting to push proposal ${prop}", ("prop", proposal));
idump((eval_state.approved_by));
eval_state.operation_results.reserve(proposal.proposed_transaction.operations.size());
processed_transaction ptrx(proposal.proposed_transaction);
eval_state._trx = &ptrx;
@ -248,23 +248,46 @@ signed_block database::generate_block(
const fc::ecc::private_key& block_signing_private_key,
uint32_t skip /* = 0 */
)
{
signed_block result;
with_skip_flags( skip, [&]()
{
result = _generate_block( when, witness_id, block_signing_private_key );
} );
return result;
}
signed_block database::_generate_block(
fc::time_point_sec when,
witness_id_type witness_id,
const fc::ecc::private_key& block_signing_private_key
)
{
try {
uint32_t skip = get_node_properties().skip_flags;
uint32_t slot_num = get_slot_at_time( when );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num ).first;
FC_ASSERT( scheduled_witness == witness_id );
const auto& witness_obj = witness_id(*this);
if( !(skip & skip_delegate_signature) )
FC_ASSERT( witness_obj.signing_key(*this).key() == block_signing_private_key.get_public_key() );
if( !(skip & skip_witness_signature) )
FC_ASSERT( witness_obj.signing_key == block_signing_private_key.get_public_key() );
_pending_block.timestamp = when;
secret_hash_type::encoder last_enc;
fc::raw::pack( last_enc, block_signing_private_key );
fc::raw::pack( last_enc, witness_obj.last_secret );
_pending_block.previous_secret = last_enc.result();
// Genesis witnesses start with a default initial secret
if( witness_obj.next_secret_hash == secret_hash_type::hash( secret_hash_type() ) )
{
_pending_block.previous_secret = secret_hash_type();
}
else
{
secret_hash_type::encoder last_enc;
fc::raw::pack( last_enc, block_signing_private_key );
fc::raw::pack( last_enc, witness_obj.previous_secret );
_pending_block.previous_secret = last_enc.result();
}
secret_hash_type::encoder next_enc;
fc::raw::pack( next_enc, block_signing_private_key );
@ -274,15 +297,27 @@ signed_block database::generate_block(
_pending_block.transaction_merkle_root = _pending_block.calculate_merkle_root();
_pending_block.witness = witness_id;
if( !(skip & skip_delegate_signature) ) _pending_block.sign( block_signing_private_key );
if( !(skip & skip_witness_signature) ) _pending_block.sign( block_signing_private_key );
FC_ASSERT( fc::raw::pack_size(_pending_block) <= get_global_properties().parameters.maximum_block_size );
//This line used to std::move(_pending_block) but this is unsafe as _pending_block is later referenced without being
//reinitialized. Future optimization could be to move it, then reinitialize it with the values we need to preserve.
signed_block tmp = _pending_block;
tmp.transaction_merkle_root = tmp.calculate_merkle_root();
_pending_block.transactions.clear();
push_block( tmp, skip );
bool failed = false;
try { push_block( tmp, skip ); } catch ( const fc::exception& e ) { failed = true; }
if( failed )
{
for( const auto& trx : tmp.transactions )
{
try {
push_transaction( trx, skip );
} catch ( const fc::exception& e ) {
wlog( "Transaction is no longer valid: ${trx}", ("trx",trx) );
}
}
return _generate_block( when, witness_id, block_signing_private_key );
}
return tmp;
} FC_CAPTURE_AND_RETHROW( (witness_id) ) }
@ -330,10 +365,35 @@ const vector<operation_history_object>& database::get_applied_operations() const
//////////////////// private methods ////////////////////
void database::apply_block( const signed_block& next_block, uint32_t skip )
{
auto block_num = next_block.block_num();
if( _checkpoints.size() )
{
auto itr = _checkpoints.find( block_num );
if( itr != _checkpoints.end() )
FC_ASSERT( next_block.id() == itr->second, "Block did not match checkpoint", ("checkpoint",*itr)("block_id",next_block.id()) );
auto last = _checkpoints.rbegin();
if( last->first >= block_num )
{
// WE CAN SKIP ALMOST EVERYTHING
skip = ~0;
}
}
with_skip_flags( skip, [&]()
{
_apply_block( next_block );
} );
return;
}
void database::_apply_block( const signed_block& next_block )
{ try {
uint32_t skip = get_node_properties().skip_flags;
_applied_ops.clear();
FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root() );
FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) );
const witness_object& signing_witness = validate_block_header(skip, next_block);
const auto& global_props = get_global_properties();
@ -376,37 +436,51 @@ void database::apply_block( const signed_block& next_block, uint32_t skip )
applied_block( next_block ); //emit
_applied_ops.clear();
notify_changed_objects();
update_pending_block(next_block, current_block_interval);
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
void database::notify_changed_objects()
{
const auto& head_undo = _undo_db.head();
vector<object_id_type> changed_ids; changed_ids.reserve(head_undo.old_values.size());
for( const auto& item : head_undo.old_values ) changed_ids.push_back(item.first);
changed_objects(changed_ids);
}
processed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip)
{
processed_transaction result;
with_skip_flags(skip, [&]()
{
result = _apply_transaction(trx);
});
return result;
}
update_pending_block(next_block, current_block_interval);
} FC_CAPTURE_AND_RETHROW( (next_block.block_num())(skip) ) }
processed_transaction database::apply_transaction( const signed_transaction& trx, uint32_t skip )
processed_transaction database::_apply_transaction(const signed_transaction& trx)
{ try {
uint32_t skip = get_node_properties().skip_flags;
trx.validate();
auto& trx_idx = get_mutable_index_type<transaction_index>();
auto trx_id = trx.id();
FC_ASSERT( (skip & skip_transaction_dupe_check) ||
trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end() );
transaction_evaluation_state eval_state(this, skip&skip_authority_check );
transaction_evaluation_state eval_state(this);
const chain_parameters& chain_parameters = get_global_properties().parameters;
eval_state._trx = &trx;
//This check is used only if this transaction has an absolute expiration time.
if( !(skip & skip_transaction_signatures) && trx.relative_expiration == 0 )
{
eval_state._sigs.reserve(trx.signatures.size());
for( const auto& sig : trx.signatures )
{
FC_ASSERT( sig.first(*this).key_address() == fc::ecc::public_key( sig.second, trx.digest() ), "",
("trx",trx)
("digest",trx.digest())
("sig.first",sig.first)
("key_address",sig.first(*this).key_address())
("addr", address(fc::ecc::public_key( sig.second, trx.digest() ))) );
FC_ASSERT( eval_state._sigs.insert(std::make_pair(public_key_type(fc::ecc::public_key(sig, trx.digest())),
false)).second,
"Multiple signatures by same key detected" );
}
}
@ -422,22 +496,76 @@ processed_transaction database::apply_transaction( const signed_transaction& trx
//Check the TaPoS reference and expiration time
//Remember that the TaPoS block number is abbreviated; it contains only the lower 16 bits.
//Lookup TaPoS block summary by block number (remember block summary instances are the block numbers)
const block_summary_object& tapos_block_summary
= static_cast<const block_summary_object&>(get_index<block_summary_object>()
.get(block_summary_id_type((head_block_num() & ~0xffff)
+ trx.ref_block_num)));
// Let N = head_block_num(), a = N & 0xFFFF, and r = trx.ref_block_num
//
// We want to solve for the largest block height x such that
// these two conditions hold:
//
// (a) 0x10000 divides x-r
// (b) x <= N
//
// Let us define:
//
// x1 = N-a+r
// x0 = x1-2^16
// x2 = x1+2^16
//
// It is clear that x0, x1, x2 are consecutive solutions to (a).
//
// Since r < 2^16 and a < 2^16, it follows that
// -2^16 < r-a < 2^16. From this we know that x0 < N and x2 > N.
//
// Case (1): x1 <= N. In this case, x1 must be the greatest
// integer that satisfies (a) and (b); for x2, the next
// largest integer that satisfies (a), does not satisfy (b).
//
// Case (2): x1 > N. In this case, x0 must be the greatest
// integer that satisfies (a) and (b); for x1, the next
// largest integer that satisfies (a), does not satisfy (b).
//
int64_t N = head_block_num();
int64_t a = N & 0xFFFF;
int64_t r = trx.ref_block_num;
int64_t x1 = N-a+r;
int64_t x0 = x1 - 0x10000;
int64_t x2 = x1 + 0x10000;
assert( x0 < N );
assert( x1 >= 0 );
assert( x2 > N );
uint32_t ref_block_height;
if( x1 <= N )
{
FC_ASSERT( x1 > 0 );
ref_block_height = uint32_t( x1 );
}
else
{
ref_block_height = uint32_t( x0 );
}
const block_summary_object& tapos_block_summary =
static_cast<const block_summary_object&>(
get_index<block_summary_object>()
.get(block_summary_id_type(ref_block_height))
);
//This is the signature check for transactions with relative expiration.
if( !(skip & skip_transaction_signatures) )
{
eval_state._sigs.reserve(trx.signatures.size());
for( const auto& sig : trx.signatures )
{
address trx_addr = fc::ecc::public_key(sig.second, trx.digest(tapos_block_summary.block_id));
FC_ASSERT(sig.first(*this).key_address() == trx_addr,
"",
("sig.first",sig.first)
("key_address",sig.first(*this).key_address())
("addr", trx_addr));
FC_ASSERT(eval_state._sigs.insert(std::make_pair(
public_key_type(
fc::ecc::public_key(sig,
trx.digest(tapos_block_summary.block_id))),
false)).second,
"Multiple signatures by same key detected");
}
}
@ -445,10 +573,11 @@ processed_transaction database::apply_transaction( const signed_transaction& trx
FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1] );
trx_expiration = tapos_block_summary.timestamp + chain_parameters.block_interval*trx.relative_expiration;
} else if( trx.relative_expiration == 0 ) {
trx_expiration = fc::time_point_sec(trx.ref_block_prefix);
FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration );
trx_expiration = fc::time_point_sec() + fc::seconds(trx.ref_block_prefix);
FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration, "",
("trx_expiration",trx_expiration)("_pending_block.timestamp",_pending_block.timestamp)("max_til_exp",chain_parameters.maximum_time_until_expiration));
}
FC_ASSERT( _pending_block.timestamp <= trx_expiration );
FC_ASSERT( _pending_block.timestamp <= trx_expiration, "", ("pending.timestamp",_pending_block.timestamp)("trx_exp",trx_expiration) );
} else if( !(skip & skip_transaction_signatures) ) {
FC_ASSERT(trx.relative_expiration == 0, "May not use transactions with a reference block in block 1!");
}
@ -463,8 +592,9 @@ processed_transaction database::apply_transaction( const signed_transaction& trx
});
}
eval_state.operation_results.reserve( trx.operations.size() );
eval_state.operation_results.reserve(trx.operations.size());
//Finally process the operations
processed_transaction ptrx(trx);
_current_op_in_trx = 0;
for( const auto& op : ptrx.operations )
@ -472,13 +602,27 @@ processed_transaction database::apply_transaction( const signed_transaction& trx
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
++_current_op_in_trx;
}
ptrx.operation_results = std::move( eval_state.operation_results );
ptrx.operation_results = std::move(eval_state.operation_results);
//Make sure the temp account has no non-zero balances
const auto& index = get_index_type<account_balance_index>().indices().get<by_account>();
auto range = index.equal_range(GRAPHENE_TEMP_ACCOUNT);
std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); });
//Make sure all signatures were needed to validate the transaction
if( !(skip & (skip_transaction_signatures|skip_authority_check)) )
{
for( const auto& item : eval_state._sigs )
{
FC_ASSERT( item.second, "All signatures must be used", ("item",item) );
}
}
return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }
operation_result database::apply_operation(transaction_evaluation_state& eval_state, const operation& op)
{
{ try {
int i_which = op.which();
uint64_t u_which = uint64_t( i_which );
if( i_which < 0 )
@ -492,16 +636,16 @@ operation_result database::apply_operation(transaction_evaluation_state& eval_st
auto result = eval->evaluate( eval_state, op, true );
set_applied_operation_result( op_id, result );
return result;
}
} FC_CAPTURE_AND_RETHROW( (eval_state._sigs) ) }
const witness_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const
{
FC_ASSERT( _pending_block.previous == next_block.previous, "", ("pending.prev",_pending_block.previous)("next.prev",next_block.previous) );
FC_ASSERT( _pending_block.timestamp <= next_block.timestamp, "", ("_pending_block.timestamp",_pending_block.timestamp)("next",next_block.timestamp)("blocknum",next_block.block_num()) );
const witness_object& witness = next_block.witness(*this);
FC_ASSERT( secret_hash_type::hash(next_block.previous_secret) == witness.next_secret, "",
("previous_secret", next_block.previous_secret)("next_secret", witness.next_secret));
if( !(skip&skip_delegate_signature) ) FC_ASSERT( next_block.validate_signee( witness.signing_key(*this).key() ) );
FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "",
("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash));
if( !(skip&skip_witness_signature) ) FC_ASSERT( next_block.validate_signee( witness.signing_key ) );
uint32_t slot_num = get_slot_at_time( next_block.timestamp );
FC_ASSERT( slot_num > 0 );
@ -521,4 +665,10 @@ void database::create_block_summary(const signed_block& next_block)
FC_ASSERT( sum.id.instance() == next_block.block_num(), "", ("summary.id",sum.id)("next.block_num",next_block.block_num()) );
}
void database::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts )
{
for( const auto& i : checkpts )
_checkpoints[i.first] = i.second;
}
} }

View file

@ -20,8 +20,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
@ -60,13 +59,6 @@ void database::debug_dump()
if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount;
total_balances[for_sale.asset_id] += for_sale.amount;
}
for( const short_order_object& o : db.get_index_type<short_order_index>().indices() )
{
idump(("short_order")(o));
auto col = o.get_collateral();
if( col.asset_id == asset_id_type() ) core_in_orders += col.amount;
total_balances[col.asset_id] += col.amount;
}
for( const call_order_object& o : db.get_index_type<call_order_index>().indices() )
{
idump(("call_order")(o));
@ -80,7 +72,7 @@ void database::debug_dump()
total_balances[asset_obj.id] += asset_obj.dynamic_asset_data_id(db).accumulated_fees;
total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;
}
for( const witness_object& witness_obj : db.get_index_type<simple_index<witness_object>>() )
for( const witness_object& witness_obj : db.get_index_type<witness_index>().indices() )
{
//idump((witness_obj));
total_balances[asset_id_type()] += witness_obj.accumulated_income;
@ -89,7 +81,12 @@ void database::debug_dump()
{
edump( (total_balances[asset_id_type()].value)(core_asset_data.current_supply.value ));
}
// TODO: Add vesting_balance_object to this method
const auto& vbidx = db.get_index_type<simple_index<vesting_balance_object>>();
for( const auto& s : vbidx )
{
idump(("vesting_balance")(s));
}
}
} }

View file

@ -38,7 +38,7 @@ const dynamic_global_property_object&database::get_dynamic_global_properties() c
return get( dynamic_global_property_id_type() );
}
const fee_schedule_type& database::current_fee_schedule()const
const fee_schedule& database::current_fee_schedule()const
{
return get_global_properties().parameters.current_fees;
}
@ -63,4 +63,14 @@ decltype( chain_parameters::block_interval ) database::block_interval( )const
return get_global_properties().parameters.block_interval;
}
const node_property_object& database::get_node_properties()const
{
return _node_property_object;
}
node_property_object& database::node_properties()
{
return _node_property_object;
}
} }

View file

@ -21,70 +21,126 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/delegate_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/key_object.hpp>
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/asset_evaluator.hpp>
#include <graphene/chain/assert_evaluator.hpp>
#include <graphene/chain/custom_evaluator.hpp>
#include <graphene/chain/delegate_evaluator.hpp>
#include <graphene/chain/global_parameters_evaluator.hpp>
#include <graphene/chain/key_evaluator.hpp>
#include <graphene/chain/limit_order_evaluator.hpp>
#include <graphene/chain/committee_member_evaluator.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/proposal_evaluator.hpp>
#include <graphene/chain/short_order_evaluator.hpp>
#include <graphene/chain/transfer_evaluator.hpp>
#include <graphene/chain/vesting_balance_evaluator.hpp>
#include <graphene/chain/withdraw_permission_evaluator.hpp>
#include <graphene/chain/witness_evaluator.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/balance_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/uint128.hpp>
#include <fc/crypto/digest.hpp>
#include <boost/algorithm/string.hpp>
namespace graphene { namespace chain {
// C++ requires that static class variables declared and initialized
// in headers must also have a definition in a single source file,
// else linker errors will occur [1].
//
// The purpose of this source file is to collect such definitions in
// a single place.
//
// [1] http://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char
const uint8_t account_object::space_id;
const uint8_t account_object::type_id;
const uint8_t asset_object::space_id;
const uint8_t asset_object::type_id;
const uint8_t block_summary_object::space_id;
const uint8_t block_summary_object::type_id;
const uint8_t call_order_object::space_id;
const uint8_t call_order_object::type_id;
const uint8_t committee_member_object::space_id;
const uint8_t committee_member_object::type_id;
const uint8_t force_settlement_object::space_id;
const uint8_t force_settlement_object::type_id;
const uint8_t global_property_object::space_id;
const uint8_t global_property_object::type_id;
const uint8_t limit_order_object::space_id;
const uint8_t limit_order_object::type_id;
const uint8_t operation_history_object::space_id;
const uint8_t operation_history_object::type_id;
const uint8_t proposal_object::space_id;
const uint8_t proposal_object::type_id;
const uint8_t transaction_object::space_id;
const uint8_t transaction_object::type_id;
const uint8_t vesting_balance_object::space_id;
const uint8_t vesting_balance_object::type_id;
const uint8_t withdraw_permission_object::space_id;
const uint8_t withdraw_permission_object::type_id;
const uint8_t witness_object::space_id;
const uint8_t witness_object::type_id;
const uint8_t witness_schedule_object::space_id;
const uint8_t witness_schedule_object::type_id;
const uint8_t worker_object::space_id;
const uint8_t worker_object::type_id;
void database::initialize_evaluators()
{
_operation_evaluators.resize(255);
register_evaluator<key_create_evaluator>();
register_evaluator<account_create_evaluator>();
register_evaluator<account_update_evaluator>();
register_evaluator<account_upgrade_evaluator>();
register_evaluator<account_whitelist_evaluator>();
register_evaluator<delegate_create_evaluator>();
register_evaluator<committee_member_create_evaluator>();
register_evaluator<committee_member_update_global_parameters_evaluator>();
register_evaluator<custom_evaluator>();
register_evaluator<asset_create_evaluator>();
register_evaluator<asset_issue_evaluator>();
register_evaluator<asset_burn_evaluator>();
register_evaluator<asset_reserve_evaluator>();
register_evaluator<asset_update_evaluator>();
register_evaluator<asset_update_bitasset_evaluator>();
register_evaluator<asset_update_feed_producers_evaluator>();
register_evaluator<asset_settle_evaluator>();
register_evaluator<asset_global_settle_evaluator>();
register_evaluator<assert_evaluator>();
register_evaluator<limit_order_create_evaluator>();
register_evaluator<limit_order_cancel_evaluator>();
register_evaluator<short_order_create_evaluator>();
register_evaluator<short_order_cancel_evaluator>();
register_evaluator<call_order_update_evaluator>();
register_evaluator<transfer_evaluator>();
register_evaluator<override_transfer_evaluator>();
register_evaluator<asset_fund_fee_pool_evaluator>();
register_evaluator<asset_publish_feeds_evaluator>();
register_evaluator<proposal_create_evaluator>();
register_evaluator<proposal_update_evaluator>();
register_evaluator<proposal_delete_evaluator>();
register_evaluator<global_parameters_update_evaluator>();
register_evaluator<witness_create_evaluator>();
register_evaluator<witness_withdraw_pay_evaluator>();
register_evaluator<vesting_balance_create_evaluator>();
@ -94,6 +150,7 @@ void database::initialize_evaluators()
register_evaluator<withdraw_permission_update_evaluator>();
register_evaluator<withdraw_permission_delete_evaluator>();
register_evaluator<worker_create_evaluator>();
register_evaluator<balance_claim_evaluator>();
}
void database::initialize_indexes()
@ -103,146 +160,130 @@ void database::initialize_indexes()
//Protocol object indexes
add_index< primary_index<asset_index> >();
add_index< primary_index<force_settlement_index> >();
add_index< primary_index<account_index> >();
add_index< primary_index<simple_index<key_object>> >();
add_index< primary_index<simple_index<delegate_object>> >();
add_index< primary_index<simple_index<witness_object>> >();
auto acnt_index = add_index< primary_index<account_index> >();
acnt_index->add_secondary_index<account_member_index>();
acnt_index->add_secondary_index<account_referrer_index>();
add_index< primary_index<committee_member_index> >();
add_index< primary_index<witness_index> >();
add_index< primary_index<limit_order_index > >();
add_index< primary_index<short_order_index > >();
add_index< primary_index<call_order_index > >();
add_index< primary_index<proposal_index > >();
auto prop_index = add_index< primary_index<proposal_index > >();
prop_index->add_secondary_index<required_approval_index>();
add_index< primary_index<withdraw_permission_index > >();
add_index< primary_index<simple_index<vesting_balance_object> > >();
//add_index< primary_index<vesting_balance_index> >();
add_index< primary_index<simple_index<vesting_balance_object>> >();
add_index< primary_index<worker_index> >();
add_index< primary_index<balance_index> >();
//Implementation object indexes
add_index< primary_index<transaction_index > >();
add_index< primary_index<account_balance_index > >();
add_index< primary_index<asset_bitasset_data_index > >();
add_index< primary_index<simple_index< global_property_object >> >();
add_index< primary_index<simple_index< dynamic_global_property_object >> >();
add_index< primary_index<simple_index< account_statistics_object >> >();
add_index< primary_index<simple_index< asset_dynamic_data_object >> >();
add_index< primary_index<flat_index< block_summary_object >> >();
add_index< primary_index< simple_index< witness_schedule_object > > >();
add_index< primary_index<simple_index<global_property_object >> >();
add_index< primary_index<simple_index<dynamic_global_property_object >> >();
add_index< primary_index<simple_index<account_statistics_object >> >();
add_index< primary_index<simple_index<asset_dynamic_data_object >> >();
add_index< primary_index<flat_index< block_summary_object >> >();
add_index< primary_index<simple_index<witness_schedule_object >> >();
}
void database::init_genesis(const genesis_allocation& initial_allocation)
void database::init_genesis(const genesis_state_type& genesis_state)
{ try {
_undo_db.disable();
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(),
"initial_active_witnesses is larger than the number of candidate witnesses.");
fc::ecc::private_key genesis_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")));
const key_object& genesis_key =
create<key_object>( [&genesis_private_key](key_object& k) {
k.key_data = public_key_type(genesis_private_key.get_public_key());
});
const account_statistics_object& genesis_statistics =
create<account_statistics_object>( [&](account_statistics_object& b){
});
create<account_balance_object>( [](account_balance_object& b) {
b.balance = GRAPHENE_INITIAL_SUPPLY;
_undo_db.disable();
struct auth_inhibitor {
auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)
{ db.node_properties().skip_flags |= skip_authority_check; }
~auth_inhibitor()
{ db.node_properties().skip_flags = old_flags; }
private:
database& db;
uint32_t old_flags;
} inhibitor(*this);
transaction_evaluation_state genesis_eval_state(this);
// Create blockchain accounts
fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
create<account_balance_object>([](account_balance_object& b) {
b.balance = GRAPHENE_MAX_SHARE_SUPPLY;
});
const account_object& genesis_account =
const account_object& committee_account =
create<account_object>( [&](account_object& n) {
n.membership_expiration_date = time_point_sec::maximum();
n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
n.name = "genesis";
n.owner.add_authority(genesis_key.get_id(), 1);
n.owner.weight_threshold = 1;
n.active = n.owner;
n.memo_key = genesis_key.id;
n.statistics = genesis_statistics.id;
});
vector<delegate_id_type> init_delegates;
vector<witness_id_type> init_witnesses;
flat_set<witness_id_type> init_witness_set;
auto delegates_and_witnesses = std::max(GRAPHENE_MIN_WITNESS_COUNT, GRAPHENE_MIN_DELEGATE_COUNT);
for( int i = 0; i < delegates_and_witnesses; ++i )
{
const account_statistics_object& stats_obj =
create<account_statistics_object>( [&](account_statistics_object&){
});
const account_object& delegate_account =
create<account_object>( [&](account_object& a) {
a.active = a.owner = genesis_account.owner;
a.referrer = account_id_type(i);
a.registrar = account_id_type(i);
a.lifetime_referrer = account_id_type(i);
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.membership_expiration_date = fc::time_point_sec::maximum();
a.name = string("init") + fc::to_string(i);
a.statistics = stats_obj.id;
});
const delegate_object& init_delegate = create<delegate_object>( [&](delegate_object& d) {
d.delegate_account = delegate_account.id;
d.vote_id = i * 2;
});
init_delegates.push_back(init_delegate.id);
const witness_object& init_witness = create<witness_object>( [&](witness_object& d) {
d.witness_account = delegate_account.id;
d.vote_id = i * 2 + 1;
secret_hash_type::encoder enc;
fc::raw::pack( enc, genesis_private_key );
fc::raw::pack( enc, d.last_secret );
d.next_secret = secret_hash_type::hash(enc.result());
});
init_witnesses.push_back(init_witness.id);
init_witness_set.insert(init_witness.id);
}
create<block_summary_object>( [&](block_summary_object& p) {
});
const witness_schedule_object& wso =
create<witness_schedule_object>( [&]( witness_schedule_object& _wso )
{
memset( _wso.rng_seed.begin(), 0, _wso.rng_seed.size() );
witness_scheduler_rng rng( _wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV );
_wso.scheduler = witness_scheduler();
_wso.scheduler._min_token_count = init_witnesses.size() / 2;
_wso.scheduler.update( init_witness_set );
for( size_t i=0; i<init_witnesses.size(); i++ )
_wso.scheduler.produce_schedule( rng );
_wso.last_scheduling_block = 0;
} ) ;
assert( wso.id == witness_schedule_id_type() );
const global_property_object& properties =
create<global_property_object>( [&](global_property_object& p) {
p.active_delegates = init_delegates;
for( const witness_id_type& wit : init_witnesses )
p.active_witnesses.insert( wit );
p.next_available_vote_id = delegates_and_witnesses * 2;
p.chain_id = fc::digest(initial_allocation);
});
(void)properties;
create<dynamic_global_property_object>( [&](dynamic_global_property_object& p) {
p.time = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP );
n.active.weight_threshold = 1;
n.name = "committee-account";
n.statistics = create<account_statistics_object>( [&](account_statistics_object& b){}).id;
});
FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "witness-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
}).get_id() == GRAPHENE_WITNESS_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "relaxed-committee-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
}).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "null-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = 0;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
}).get_id() == GRAPHENE_NULL_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "temp-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.owner.weight_threshold = 0;
a.active.weight_threshold = 0;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
}).get_id() == GRAPHENE_TEMP_ACCOUNT);
// Create core asset
const asset_dynamic_data_object& dyn_asset =
create<asset_dynamic_data_object>( [&]( asset_dynamic_data_object& a ) {
a.current_supply = GRAPHENE_INITIAL_SUPPLY;
create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) {
a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY;
});
const asset_object& core_asset =
create<asset_object>( [&]( asset_object& a ) {
a.symbol = GRAPHENE_SYMBOL;
a.options.max_supply = GRAPHENE_INITIAL_SUPPLY;
a.options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 0;
a.issuer = genesis_account.id;
a.issuer = committee_account.id;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = 0;
a.options.core_exchange_rate.quote.amount = 1;
@ -253,82 +294,248 @@ void database::init_genesis(const genesis_allocation& initial_allocation)
assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) );
(void)core_asset;
if( !initial_allocation.empty() )
// Create global properties
create<global_property_object>([&](global_property_object& p) {
p.chain_id = fc::digest(genesis_state);
p.parameters = genesis_state.initial_parameters;
// Set fees to zero initially, so that genesis initialization needs not pay them
// We'll fix it at the end of the function
p.parameters.current_fees->zero_all_fees();
});
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&) {});
// Create initial accounts
for( const auto& account : genesis_state.initial_accounts )
{
share_type total_allocation = 0;
for( const auto& handout : initial_allocation )
total_allocation += handout.second;
auto mangle_to_name = [](const fc::static_variant<public_key_type, address>& key) {
string addr = string(key.which() == std::decay<decltype(key)>::type::tag<address>::value? key.get<address>()
: key.get<public_key_type>());
string result = "bts";
string key_string = string(addr).substr(sizeof(GRAPHENE_ADDRESS_PREFIX)-1);
for( char c : key_string )
{
if( isupper(c) )
result += string("-") + char(tolower(c));
else
result += c;
}
return result;
};
fc::time_point start_time = fc::time_point::now();
for( const auto& handout : initial_allocation )
account_create_operation cop;
cop.name = account.name;
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
cop.owner = authority(1, account.owner_key, 1);
if( account.active_key == public_key_type() )
{
asset amount(handout.second);
amount.amount = ((fc::uint128(amount.amount.value) * GRAPHENE_INITIAL_SUPPLY)/total_allocation.value).to_uint64();
if( amount.amount == 0 )
{
wlog("Skipping zero allocation to ${k}", ("k", handout.first));
continue;
}
signed_transaction trx;
trx.operations.emplace_back(key_create_operation({asset(), genesis_account.id, handout.first}));
relative_key_id_type key_id(0);
authority account_authority(1, key_id, 1);
account_create_operation cop;
cop.name = mangle_to_name(handout.first);
cop.registrar = account_id_type(1);
cop.active = account_authority;
cop.owner = account_authority;
cop.memo_key = key_id;
trx.operations.push_back(cop);
trx.validate();
auto ptrx = apply_transaction(trx, ~0);
trx = signed_transaction();
account_id_type account_id(ptrx.operation_results.back().get<object_id_type>());
trx.operations.emplace_back(transfer_operation({ asset(),
genesis_account.id,
account_id,
amount,
memo_data()//vector<char>()
}));
trx.validate();
apply_transaction(trx, ~0);
cop.active = cop.owner;
cop.options.memo_key = account.owner_key;
}
asset leftovers = get_balance(account_id_type(), asset_id_type());
if( leftovers.amount > 0 )
else
{
modify(*get_index_type<account_balance_index>().indices().get<by_balance>().find(boost::make_tuple(account_id_type(), asset_id_type())),
[](account_balance_object& b) {
b.adjust_balance(-b.get_balance());
});
modify(core_asset.dynamic_asset_data_id(*this), [&leftovers](asset_dynamic_data_object& d) {
d.accumulated_fees += leftovers.amount;
});
cop.active = authority(1, account.active_key, 1);
cop.options.memo_key = account.active_key;
}
account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());
fc::microseconds duration = fc::time_point::now() - start_time;
ilog("Finished allocating to ${n} accounts in ${t} milliseconds.",
("n", initial_allocation.size())("t", duration.count() / 1000));
if( account.is_lifetime_member )
{
account_upgrade_operation op;
op.account_to_upgrade = account_id;
op.upgrade_to_lifetime_member = true;
apply_operation(genesis_eval_state, op);
}
}
// Helper function to get account ID by name
const auto& accounts_by_name = get_index_type<account_index>().indices().get<by_name>();
auto get_account_id = [&accounts_by_name](const string& name) {
auto itr = accounts_by_name.find(name);
FC_ASSERT(itr != accounts_by_name.end(),
"Unable to find account '${acct}'. Did you forget to add a record for it to initial_accounts?",
("acct", name));
return itr->get_id();
};
// Helper function to get asset ID by symbol
const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();
auto get_asset_id = [&assets_by_symbol](const string& symbol) {
auto itr = assets_by_symbol.find(symbol);
FC_ASSERT(itr != assets_by_symbol.end(),
"Unable to find asset '${sym}'. Did you forget to add a record for it to initial_assets?",
("sym", symbol));
return itr->get_id();
};
// Create initial assets
for( const genesis_state_type::initial_asset_type& asset : genesis_state.initial_assets )
{
asset_dynamic_data_id_type dynamic_data_id;
optional<asset_bitasset_data_id_type> bitasset_data_id;
if( asset.bitasset_opts.valid() )
{
share_type total_allocated;
asset_id_type new_asset_id = get_index_type<asset_index>().get_next_id();
asset_id_type collateral_asset_id = get_asset_id(asset.bitasset_opts->backing_asset_symbol);
int collateral_holder_number = 0;
for( const auto& collateral_rec : asset.bitasset_opts->collateral_records )
{
account_create_operation cop;
cop.name = asset.symbol + "-collateral-holder-" + std::to_string(collateral_holder_number);
boost::algorithm::to_lower(cop.name);
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
cop.owner = authority(1, collateral_rec.owner, 1);
cop.active = cop.owner;
account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get<object_id_type>();
create<call_order_object>([&](call_order_object& c) {
c.borrower = owner_account_id;
c.collateral = collateral_rec.collateral;
c.debt = collateral_rec.debt;
c.call_price = price::call_price(chain::asset(c.debt, new_asset_id),
chain::asset(c.collateral, collateral_asset_id),
asset.bitasset_opts->maintenance_collateral_ratio);
});
total_allocated += collateral_rec.debt;
}
bitasset_data_id = create<asset_bitasset_data_object>([&](asset_bitasset_data_object& b) {
b.options.feed_lifetime_sec = asset.bitasset_opts->feed_lifetime_sec;
b.options.minimum_feeds = asset.bitasset_opts->minimum_feeds;
b.options.force_settlement_delay_sec = asset.bitasset_opts->force_settlement_delay_sec;
b.options.force_settlement_offset_percent = asset.bitasset_opts->force_settlement_offset_percent;
b.options.maximum_force_settlement_volume = asset.bitasset_opts->maximum_force_settlement_volume;
b.options.short_backing_asset = get_asset_id(asset.bitasset_opts->backing_asset_symbol);
}).id;
dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) {
d.current_supply = total_allocated;
d.accumulated_fees = asset.initial_accumulated_fees;
}).id;
} else
dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) {
d.accumulated_fees = asset.initial_accumulated_fees;
}).id;
create<asset_object>([&](asset_object& a) {
a.symbol = asset.symbol;
a.options.description = asset.description;
a.precision = asset.precision;
a.issuer = get_account_id(asset.issuer_name);
a.options.max_supply = asset.max_supply;
a.options.market_fee_percent = asset.market_fee_percent;
a.options.max_market_fee = asset.max_market_fee;
a.options.issuer_permissions = asset.issuer_permissions;
a.options.flags = asset.flags;
a.dynamic_asset_data_id = dynamic_data_id;
a.bitasset_data_id = bitasset_data_id;
});
}
// Create initial balances
share_type total_allocation;
for( const auto& handout : genesis_state.initial_balances )
{
create<balance_object>([&handout,&assets_by_symbol,total_allocation](balance_object& b) {
b.balance = asset(handout.amount, assets_by_symbol.find(handout.asset_symbol)->get_id());
b.owner = handout.owner;
});
total_allocation += handout.amount;
}
// Create initial vesting balances
for( const genesis_state_type::initial_vesting_balance_type& vest : genesis_state.initial_vesting_balances )
{
create<balance_object>([&](balance_object& b) {
b.owner = vest.owner;
b.balance = asset(vest.amount, assets_by_symbol.find(vest.asset_symbol)->get_id());
linear_vesting_policy policy;
policy.begin_timestamp = vest.begin_timestamp;
policy.vesting_cliff_seconds = 0;
policy.vesting_duration_seconds = vest.vesting_duration_seconds;
policy.begin_balance = vest.begin_balance;
b.vesting_policy = std::move(policy);
});
total_allocation += vest.amount;
}
// Set current supply based on allocations, if they happened
if( total_allocation > 0 )
{
modify(dyn_asset, [total_allocation](asset_dynamic_data_object& d) {
d.current_supply = total_allocation;
});
adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));
}
// 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() );
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) {
committee_member_create_operation op;
op.committee_member_account = get_account_id(member.owner_name);
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) {
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
{
p.active_witnesses.insert(i);
p.witness_accounts.insert(get(witness_id_type(i)).witness_account);
}
});
// Initialize witness schedule
#ifndef NDEBUG
const witness_schedule_object& wso =
#endif
create<witness_schedule_object>([&](witness_schedule_object& _wso)
{
memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size());
witness_scheduler_rng rng(_wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
auto init_witnesses = get_global_properties().active_witnesses;
_wso.scheduler = witness_scheduler();
_wso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
_wso.scheduler.update(init_witnesses);
for( size_t i=0; i<init_witnesses.size(); ++i )
_wso.scheduler.produce_schedule(rng);
_wso.last_scheduling_block = 0;
_wso.recent_slots_filled = fc::uint128::max_value();
});
assert( wso.id == witness_schedule_id_type() );
// Enable fees
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
});
_undo_db.enable();
} FC_LOG_AND_RETHROW() }
} FC_CAPTURE_AND_RETHROW() }
} }

View file

@ -20,39 +20,71 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/delegate_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
template<class ObjectType>
vector<std::reference_wrapper<const ObjectType>> database::sort_votable_objects(size_t count) const
template<class Index>
vector<std::reference_wrapper<const typename Index::object_type>> database::sort_votable_objects(size_t count) const
{
const auto& all_objects = dynamic_cast<const simple_index<ObjectType>&>(get_index<ObjectType>());
using ObjectType = typename Index::object_type;
const auto& all_objects = get_index_type<Index>().indices();
count = std::min(count, all_objects.size());
vector<std::reference_wrapper<const ObjectType>> refs;
refs.reserve(all_objects.size());
std::transform(all_objects.begin(), all_objects.end(),
std::back_inserter(refs),
[](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];
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
[this](const ObjectType& a, const ObjectType& b)->bool {
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());
return refs;
}
template<class... Types>
void database::perform_account_maintenance(std::tuple<Types...> helpers)
{
const auto& idx = get_index_type<account_index>().indices();
for( const account_object& a : idx )
detail::for_each(helpers, a, detail::gen_seq<sizeof...(Types)>());
}
/// @brief A visitor for @ref worker_type which calls pay_worker on the worker within
struct worker_pay_visitor
{
private:
share_type pay;
database& db;
public:
worker_pay_visitor(share_type pay, database& db)
: pay(pay), db(db) {}
typedef void result_type;
template<typename W>
void operator()(W& worker)const
{
worker.pay_worker(pay, db);
}
};
void database::pay_workers( share_type& budget )
{
ilog("Processing payroll! Available budget is ${b}", ("b", budget));
// ilog("Processing payroll! Available budget is ${b}", ("b", budget));
vector<std::reference_wrapper<const worker_object>> active_workers;
get_index_type<worker_index>().inspect_all_objects([this, &active_workers](const object& o) {
const worker_object& w = static_cast<const worker_object&>(o);
@ -61,8 +93,14 @@ void database::pay_workers( share_type& budget )
active_workers.emplace_back(w);
});
// worker with more votes is preferred
// if two workers exactly tie for votes, worker with lower ID is preferred
std::sort(active_workers.begin(), active_workers.end(), [this](const worker_object& wa, const worker_object& wb) {
return wa.approving_stake(_vote_tally_buffer) > wb.approving_stake(_vote_tally_buffer);
share_type wa_vote = wa.approving_stake(_vote_tally_buffer);
share_type wb_vote = wb.approving_stake(_vote_tally_buffer);
if( wa_vote != wb_vote )
return wa_vote > wb_vote;
return wa.id < wb.id;
});
for( int i = 0; i < active_workers.size() && budget > 0; ++i )
@ -78,7 +116,7 @@ void database::pay_workers( share_type& budget )
}
share_type actual_pay = std::min(budget, requested_pay);
ilog(" ==> Paying ${a} to worker ${w}", ("w", active_worker.id)("a", actual_pay));
//ilog(" ==> Paying ${a} to worker ${w}", ("w", active_worker.id)("a", actual_pay));
modify(active_worker, [&](worker_object& w) {
w.worker.visit(worker_pay_visitor(actual_pay, *this));
});
@ -92,24 +130,53 @@ void database::update_active_witnesses()
assert( _witness_count_histogram_buffer.size() > 0 );
share_type stake_target = _total_voting_stake / 2;
share_type stake_tally = _witness_count_histogram_buffer[0];
int witness_count = 0;
while( (size_t(witness_count) < _witness_count_histogram_buffer.size())
&& (stake_tally <= stake_target) )
stake_tally += _witness_count_histogram_buffer[++witness_count];
size_t witness_count = 0;
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_object>(std::max(witness_count*2+1, GRAPHENE_MIN_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();
modify( gpo, [&]( global_property_object& gp ){
// Update witness authority
modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a ) {
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0;
a.active.clear();
for( const witness_object& wit : wits )
{
weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]);
total_votes += _vote_tally_buffer[wit.vote_id];
}
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
// then I want to keep the most significant 16 bits of what's left.
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
for( const auto& weight : weights )
{
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );
a.active.account_auths[weight.first] += votes;
a.active.weight_threshold += votes;
}
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
});
modify(gpo, [&]( global_property_object& gp ){
gp.active_witnesses.clear();
gp.active_witnesses.reserve( wits.size() );
gp.active_witnesses.reserve(wits.size());
std::transform(wits.begin(), wits.end(),
std::inserter(gp.active_witnesses, gp.active_witnesses.end()),
[](const witness_object& w) {
return w.id;
});
gp.witness_accounts.clear();
gp.witness_accounts.reserve( wits.size() );
gp.witness_accounts.reserve(wits.size());
std::transform(wits.begin(), wits.end(),
std::inserter(gp.witness_accounts, gp.witness_accounts.end()),
[](const witness_object& w) {
@ -118,36 +185,37 @@ void database::update_active_witnesses()
});
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
modify( wso, [&]( witness_schedule_object& _wso )
modify(wso, [&](witness_schedule_object& _wso)
{
_wso.scheduler.update( gpo.active_witnesses );
} );
_wso.scheduler.update(gpo.active_witnesses);
});
} FC_CAPTURE_AND_RETHROW() }
void database::update_active_delegates()
void database::update_active_committee_members()
{ try {
assert( _committee_count_histogram_buffer.size() > 0 );
uint64_t stake_target = _total_voting_stake / 2;
uint64_t stake_tally = _committee_count_histogram_buffer[0];
int delegate_count = 0;
while( (size_t(delegate_count) < _committee_count_histogram_buffer.size())
&& (stake_tally <= stake_target) )
stake_tally += _committee_count_histogram_buffer[++delegate_count];
size_t committee_member_count = 0;
if( stake_target > 0 )
while( (committee_member_count < _committee_count_histogram_buffer.size() - 1)
&& (stake_tally <= stake_target) )
stake_tally += _committee_count_histogram_buffer[++committee_member_count];
auto delegates = sort_votable_objects<delegate_object>(std::max(delegate_count*2+1, GRAPHENE_MIN_DELEGATE_COUNT));
auto committee_members = sort_votable_objects<committee_member_index>(std::max(committee_member_count*2+1, (size_t)GRAPHENE_MIN_COMMITTEE_MEMBER_COUNT));
// Update genesis authorities
if( !delegates.empty() )
modify( get(account_id_type()), [&]( account_object& a ) {
// Update committee authorities
if( !committee_members.empty() )
{
modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a) {
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.owner.weight_threshold = 0;
a.owner.auths.clear();
a.active.weight_threshold = 0;
a.active.clear();
for( const delegate_object& del : delegates )
for( const committee_member_object& del : committee_members )
{
weights.emplace(del.delegate_account, _vote_tally_buffer[del.vote_id]);
weights.emplace(del.committee_member_account, _vote_tally_buffer[del.vote_id]);
total_votes += _vote_tally_buffer[del.vote_id];
}
@ -158,19 +226,22 @@ void database::update_active_delegates()
{
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );
a.owner.auths[weight.first] += votes;
a.owner.weight_threshold += votes;
a.active.account_auths[weight.first] += votes;
a.active.weight_threshold += votes;
}
a.owner.weight_threshold /= 2;
a.owner.weight_threshold += 1;
a.active = a.owner;
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
});
modify( get_global_properties(), [&]( global_property_object& gp ) {
gp.active_delegates.clear();
std::transform(delegates.begin(), delegates.end(),
std::back_inserter(gp.active_delegates),
[](const delegate_object& d) { return d.id; });
modify(get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) {
a.active = get(GRAPHENE_COMMITTEE_ACCOUNT).active;
});
}
modify(get_global_properties(), [&](global_property_object& gp) {
gp.active_committee_members.clear();
std::transform(committee_members.begin(), committee_members.end(),
std::inserter(gp.active_committee_members, gp.active_committee_members.begin()),
[](const committee_member_object& d) { return d.id; });
});
} FC_CAPTURE_AND_RETHROW() }
@ -186,13 +257,16 @@ share_type database::get_max_budget( fc::time_point_sec now )const
int64_t dt = (now - dpo.last_budget_time).to_seconds();
// We'll consider accumulated_fees to be burned at the BEGINNING
// We'll consider accumulated_fees to be reserved at the BEGINNING
// of the maintenance interval. However, for speed we only
// call modify() on the asset_dynamic_data_object once at the
// end of the maintenance interval. Thus the accumulated_fees
// are available for the budget at this point, but not included
// in core.burned().
share_type reserve = core.burned(*this) + core_dd.accumulated_fees;
// in core.reserved().
share_type reserve = core.reserved(*this) + core_dd.accumulated_fees;
// Similarly, we consider leftover witness_budget to be burned
// at the BEGINNING of the maintenance interval.
reserve += dpo.witness_budget;
fc::uint128_t budget_u128 = reserve.value;
budget_u128 *= uint64_t(dt);
@ -263,16 +337,23 @@ void database::process_budget()
pay_workers(leftover_worker_funds);
available_funds += leftover_worker_funds;
share_type unused_prev_witness_budget = dpo.witness_budget;
modify(core, [&]( asset_dynamic_data_object& _core )
{
_core.current_supply = (_core.current_supply + witness_budget +
worker_budget - leftover_worker_funds -
_core.accumulated_fees);
_core.current_supply = (_core.current_supply
+ witness_budget
+ worker_budget
- leftover_worker_funds
- _core.accumulated_fees
- unused_prev_witness_budget
);
_core.accumulated_fees = 0;
});
modify(dpo, [&]( dynamic_global_property_object& _dpo )
{
// Should this be +=?
// Since initial witness_budget was rolled into
// available_funds, we replace it with witness_budget
// instead of adding it.
_dpo.witness_budget = witness_budget;
_dpo.last_budget_time = now;
});
@ -307,25 +388,26 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// Usually they're the same, but if the stake account has specified a voting_account, that account is the one
// specifying the opinions.
const account_object& opinion_account =
(stake_account.voting_account == account_id_type())? stake_account
: d.get(stake_account.voting_account);
(stake_account.options.voting_account ==
account_id_type())? stake_account
: d.get(stake_account.options.voting_account);
const auto& stats = stake_account.statistics(d);
uint64_t voting_stake = stats.total_core_in_orders.value
+ (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)
+ d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;
for( vote_id_type id : opinion_account.votes )
for( vote_id_type id : opinion_account.options.votes )
{
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.num_witness <= props.parameters.maximum_witness_count )
if( opinion_account.options.num_witness <= props.parameters.maximum_witness_count )
{
uint16_t offset = std::min(size_t(opinion_account.num_witness/2),
uint16_t offset = std::min(size_t(opinion_account.options.num_witness/2),
d._witness_count_histogram_buffer.size() - 1);
// votes for a number greater than maximum_witness_count
// are turned into votes for maximum_witness_count.
@ -333,17 +415,17 @@ 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.num_committee <= props.parameters.maximum_committee_count )
if( opinion_account.options.num_committee <= props.parameters.maximum_committee_count )
{
uint16_t offset = std::min(size_t(opinion_account.num_committee/2),
uint16_t offset = std::min(size_t(opinion_account.options.num_committee/2),
d._committee_count_histogram_buffer.size() - 1);
// votes for a number greater than maximum_committee_count
// 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;
@ -357,77 +439,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
process_fees_helper(database& d, const global_property_object& gpo)
: d(d), props(gpo) {}
share_type cut_fee(share_type a, uint16_t p)const
{
if( a == 0 || p == 0 )
return 0;
if( p == GRAPHENE_100_PERCENT )
return a;
fc::uint128 r(a.value);
r *= p;
r /= GRAPHENE_100_PERCENT;
return r.to_uint64();
}
void pay_out_fees(const account_object& account, share_type core_fee_total, bool require_vesting)
{
share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage);
assert( network_cut <= core_fee_total );
share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee);
share_type accumulated = network_cut - burned;
assert( accumulated + burned == network_cut );
share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage);
share_type referral = core_fee_total - network_cut - lifetime_cut;
d.modify(dynamic_asset_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) {
d.accumulated_fees += network_cut;
});
// Potential optimization: Skip some of this math and object lookups by special casing on the account type.
// For example, if the account is a lifetime member, we can skip all this and just deposit the referral to
// it directly.
share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage);
share_type registrar_cut = referral - referrer_cut;
d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting);
d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting);
d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting);
assert( referrer_cut + registrar_cut + accumulated + burned + lifetime_cut == core_fee_total );
}
void operator()(const account_object& a) {
const account_statistics_object& stats = a.statistics(d);
if( stats.pending_fees > 0 )
{
share_type vesting_fee_subtotal(stats.pending_fees);
share_type vested_fee_subtotal(stats.pending_vested_fees);
share_type vesting_cashback, vested_cashback;
if( stats.lifetime_fees_paid > props.parameters.bulk_discount_threshold_min &&
a.is_member(d.head_block_time()) )
{
auto bulk_discount_rate = stats.calculate_bulk_discount_percent(props.parameters);
vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate);
vesting_fee_subtotal -= vesting_cashback;
vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate);
vested_fee_subtotal -= vested_cashback;
}
pay_out_fees(a, vesting_fee_subtotal, true);
d.deposit_cashback(a, vesting_cashback, true);
pay_out_fees(a, vested_fee_subtotal, false);
d.deposit_cashback(a, vested_cashback, false);
d.modify(stats, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) {
s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal;
s.pending_fees = 0;
s.pending_vested_fees = 0;
});
}
a.statistics(d).process_fees(a, d);
}
} fee_helper(*this, gpo);
@ -444,13 +457,23 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
c(_vote_tally_buffer);
update_active_witnesses();
update_active_delegates();
update_active_committee_members();
if( gpo.pending_parameters )
modify(gpo, [](global_property_object& p) {
modify(gpo, [this](global_property_object& p) {
// Remove scaling of account registration fee
/*
/// TODO reimplement this
const auto& dgpo = get_dynamic_global_properties();
p.parameters.current_fees.account_create_fee >>= p.parameters.account_fee_scale_bitshifts *
(dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale);
*/
if( p.pending_parameters )
{
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
});
}
});
auto new_block_interval = global_props.parameters.block_interval;
@ -462,7 +485,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
if( !r )
{
_pending_block.timestamp -= r;
assert( (_pending_block.timestamp.sec_since_epoch() % new_block_interval) == 0 );
assert( (_pending_block.timestamp.sec_since_epoch() % new_block_interval) == 0 );
}
auto next_maintenance_time = get<dynamic_global_property_object>(dynamic_global_property_id_type()).next_maintenance_time;
@ -474,12 +497,15 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
next_maintenance_time = time_point_sec() +
(((next_block.timestamp.sec_since_epoch() / maintenance_interval) + 1) * maintenance_interval);
else
next_maintenance_time += maintenance_interval;
assert( next_maintenance_time > next_block.timestamp );
// It's possible we have missed blocks for at least a maintenance interval.
// In this case, we'll need to bump the next maintenance time more than once.
do next_maintenance_time += maintenance_interval;
while( next_maintenance_time < head_block_time() );
}
modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) {
d.next_maintenance_time = next_maintenance_time;
d.accounts_registered_this_interval = 0;
});
// Reset all BitAsset force settlement volumes to zero

View file

@ -19,6 +19,9 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <functional>
namespace graphene { namespace chain {
@ -33,44 +36,28 @@ database::~database(){
_pending_block_session->commit();
}
void database::open( const fc::path& data_dir, const genesis_allocation& initial_allocation )
{ try {
ilog("Open database in ${d}", ("d", data_dir));
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(initial_allocation);
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
auto last_block_itr = _block_id_to_block.last();
if( last_block_itr.valid() )
_fork_db.start_block( last_block_itr.value() );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
void database::reindex(fc::path data_dir, const genesis_allocation& initial_allocation)
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 itr = _block_id_to_block.begin();
auto last_block = _block_id_to_block.last();
if( !last_block ) return;
const auto last_block_num = last_block->block_num();
// TODO: disable undo tracking durring reindex, this currently causes crashes in the benchmark test
//_undo_db.disable();
while( itr.valid() )
for( uint32_t i = 1; i <= last_block_num; ++i )
{
apply_block( itr.value(), skip_delegate_signature |
apply_block(*_block_id_to_block.fetch_by_number(i), skip_witness_signature |
skip_transaction_signatures |
skip_undo_block |
skip_undo_transaction |
skip_transaction_dupe_check |
skip_tapos_check |
skip_authority_check );
++itr;
skip_authority_check);
}
//_undo_db.enable();
auto end = fc::time_point::now();
@ -93,6 +80,7 @@ void database::close(uint32_t blocks_to_rewind)
for(uint32_t i = 0; i < blocks_to_rewind && head_block_num() > 0; ++i)
pop_block();
object_database::flush();
object_database::close();
if( _block_id_to_block.is_open() )

View file

@ -20,31 +20,31 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
/**
for each short order, fill it at settlement price and place funds received into a total
calculate the USD->CORE price and convert all USD balances to CORE at that price and subtract CORE from total
- any fees accumulated by the issuer in the bitasset are forfeit / not redeemed
- cancel all open orders with bitasset in it
- any bitassets that use this bitasset as collateral are immediately settled at their feed price
- convert all balances in bitasset to CORE and subtract from total
- any prediction markets with usd as the backing get converted to CORE as the backing
any CORE left over due to rounding errors is paid to accumulated fees
* All margin positions are force closed at the swan price
* Collateral received goes into a force-settlement fund
* No new margin positions can be created for this asset
* No more price feed updates
* Force settlement happens without delay at the swan price, deducting from force-settlement fund
* No more asset updates may be issued.
*/
void database::globally_settle_asset( const asset_object& mia, const price& settlement_price )
{ try {
/*
elog( "BLACK SWAN!" );
debug_dump();
edump( (mia.symbol)(settlement_price) );
*/
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" );
const asset_object& backing_asset = bitasset.options.short_backing_asset(*this);
asset collateral_gathered = backing_asset.amount(0);
@ -54,92 +54,37 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
const call_order_index& call_index = get_index_type<call_order_index>();
const auto& call_price_index = call_index.indices().get<by_price>();
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
while( call_itr != call_end )
{
auto pays = call_itr->get_debt() * settlement_price;
wdump( (call_itr->get_debt() ) );
collateral_gathered += pays;
const auto& order = *call_itr;
++call_itr;
FC_ASSERT( fill_order( order, pays, order.get_debt() ) );
}
const limit_order_index& limit_index = get_index_type<limit_order_index>();
const auto& limit_price_index = limit_index.indices().get<by_price>();
// cancel all orders selling the market issued asset
auto limit_itr = limit_price_index.lower_bound(price::max(mia.id, bitasset.options.short_backing_asset));
auto limit_end = limit_price_index.upper_bound(~bitasset.current_feed.call_limit);
while( limit_itr != limit_end )
{
const auto& order = *limit_itr;
ilog( "CANCEL LIMIT ORDER" );
idump((order));
++limit_itr;
cancel_order( order );
}
limit_itr = limit_price_index.begin();
while( limit_itr != limit_end )
{
if( limit_itr->amount_for_sale().asset_id == mia.id )
{
const auto& order = *limit_itr;
ilog( "CANCEL_AGAIN" );
edump((order));
++limit_itr;
cancel_order( order );
}
}
limit_itr = limit_price_index.begin();
while( limit_itr != limit_end )
// cancel all call orders and accumulate it into collateral_gathered
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
while( call_itr != call_end )
{
if( limit_itr->amount_for_sale().asset_id == mia.id )
{
const auto& order = *limit_itr;
edump((order));
++limit_itr;
cancel_order( order );
}
auto pays = call_itr->get_debt() * settlement_price;
if( pays > call_itr->get_collateral() )
pays = call_itr->get_collateral();
collateral_gathered += pays;
const auto& order = *call_itr;
++call_itr;
FC_ASSERT( fill_order( order, pays, order.get_debt() ) );
}
// settle all balances
asset total_mia_settled = mia.amount(0);
modify( bitasset, [&]( asset_bitasset_data_object& obj ){
assert( collateral_gathered.asset_id == settlement_price.quote.asset_id );
obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered; //settlement_price;
obj.settlement_fund = collateral_gathered.amount;
});
const auto& index = get_index_type<account_balance_index>().indices().get<by_asset>();
auto range = index.equal_range(mia.get_id());
for( auto itr = range.first; itr != range.second; ++itr )
{
auto mia_balance = itr->get_balance();
if( mia_balance.amount > 0 )
{
adjust_balance(itr->owner, -mia_balance);
auto settled_amount = mia_balance * settlement_price;
idump( (mia_balance)(settled_amount)(settlement_price) );
adjust_balance(itr->owner, settled_amount);
total_mia_settled += mia_balance;
collateral_gathered -= settled_amount;
}
}
/// After all margin positions are closed, the current supply will be reported as 0, but
/// that is a lie, the supply didn't change. We need to capture the current supply before
/// filling all call orders and then restore it afterward. Then in the force settlement
/// evaluator reduce the supply
modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
obj.current_supply = original_mia_supply;
});
// TODO: convert payments held in escrow
modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
total_mia_settled.amount += obj.accumulated_fees;
obj.accumulated_fees = 0;
});
wlog( "====================== AFTER SETTLE BLACK SWAN UNCLAIMED SETTLEMENT FUNDS ==============\n" );
wdump((collateral_gathered)(total_mia_settled)(original_mia_supply)(mia_dyn.current_supply));
modify( bitasset.options.short_backing_asset(*this).dynamic_asset_data_id(*this), [&]( asset_dynamic_data_object& obj ){
obj.accumulated_fees += collateral_gathered.amount;
});
FC_ASSERT( total_mia_settled.amount == original_mia_supply, "", ("total_settled",total_mia_settled)("original",original_mia_supply) );
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
void database::cancel_order(const force_settlement_object& order, bool create_virtual_op)
{
@ -167,7 +112,49 @@ void database::cancel_order( const limit_order_object& order, bool create_virtua
// TODO: create a virtual cancel operation
}
remove( order );
remove(order);
}
bool database::apply_order(const limit_order_object& new_order_object, bool allow_black_swan)
{
auto order_id = new_order_object.id;
const asset_object& sell_asset = get(new_order_object.amount_for_sale().asset_id);
const asset_object& receive_asset = get(new_order_object.amount_to_receive().asset_id);
// Possible optimization: We only need to check calls if both are true:
// - The new order is at the front of the book
// - The new order is below the call limit price
bool called_some = check_call_orders(sell_asset, allow_black_swan);
called_some |= check_call_orders(receive_asset, allow_black_swan);
if( called_some && !find_object(order_id) ) // then we were filled by call order
return true;
const auto& limit_price_idx = get_index_type<limit_order_index>().indices().get<by_price>();
// TODO: it should be possible to simply check the NEXT/PREV iterator after new_order_object to
// determine whether or not this order has "changed the book" in a way that requires us to
// check orders. For now I just lookup the lower bound and check for equality... this is log(n) vs
// constant time check. Potential optimization.
auto max_price = ~new_order_object.sell_price;
auto limit_itr = limit_price_idx.lower_bound(max_price.max());
auto limit_end = limit_price_idx.upper_bound(max_price);
bool finished = false;
while( !finished && limit_itr != limit_end )
{
auto old_limit_itr = limit_itr;
++limit_itr;
// match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop.
finished = (match(new_order_object, *old_limit_itr, old_limit_itr->sell_price) != 2);
}
//Possible optimization: only check calls if the new order completely filled some old order
//Do I need to check both assets?
check_call_orders(sell_asset, allow_black_swan);
check_call_orders(receive_asset, allow_black_swan);
return find_object(order_id) == nullptr;
}
/**
@ -225,10 +212,6 @@ int database::match( const limit_order_object& bid, const limit_order_object& as
return match<limit_order_object>( bid, ask, match_price );
}
int database::match( const limit_order_object& bid, const short_order_object& ask, const price& match_price )
{
return match<short_order_object>( bid, ask, match_price );
}
asset database::match( const call_order_object& call, const force_settlement_object& settle, const price& match_price,
asset max_settlement )
@ -263,7 +246,8 @@ bool database::fill_order( const limit_order_object& order, const asset& pays, c
auto issuer_fees = pay_market_fees( recv_asset, receives );
pay_order( seller, receives - issuer_fees, pays );
push_applied_operation( fill_order_operation{ order.id, order.seller, pays, receives, issuer_fees } );
assert( pays.asset_id != receives.asset_id );
push_applied_operation( fill_order_operation( order.id, order.seller, pays, receives, issuer_fees ) );
if( pays == order.amount_for_sale() )
{
@ -290,111 +274,10 @@ bool database::fill_order( const limit_order_object& order, const asset& pays, c
}
}
bool database::fill_order( const short_order_object& order, const asset& pays, const asset& receives )
{ try {
assert( order.amount_for_sale().asset_id == pays.asset_id );
assert( pays.asset_id != receives.asset_id );
const call_order_index& call_index = get_index_type<call_order_index>();
const account_object& seller = order.seller(*this);
const asset_object& recv_asset = receives.asset_id(*this);
const asset_object& pays_asset = pays.asset_id(*this);
assert( pays_asset.is_market_issued() );
auto issuer_fees = pay_market_fees( recv_asset, receives );
bool filled = pays == order.amount_for_sale();
asset seller_to_collateral;
if( (*pays_asset.bitasset_data_id)(*this).is_prediction_market )
{
assert( pays.amount >= receives.amount );
seller_to_collateral = pays.amount - receives.amount;
}
else
{
seller_to_collateral = filled ? order.get_collateral() : pays * order.sell_price;
}
auto buyer_to_collateral = receives - issuer_fees;
if( receives.asset_id == asset_id_type() )
{
const auto& statistics = seller.statistics(*this);
modify( statistics, [&]( account_statistics_object& b ){
b.total_core_in_orders += buyer_to_collateral.amount;
});
}
modify( pays_asset.dynamic_asset_data_id(*this), [&]( asset_dynamic_data_object& obj ){
obj.current_supply += pays.amount;
});
const auto& call_account_index = call_index.indices().get<by_account>();
auto call_itr = call_account_index.find( boost::make_tuple(order.seller, pays.asset_id) );
if( call_itr == call_account_index.end() )
{
create<call_order_object>( [&]( call_order_object& c ){
c.borrower = seller.id;
c.collateral = seller_to_collateral.amount + buyer_to_collateral.amount;
c.debt = pays.amount;
c.maintenance_collateral_ratio = order.maintenance_collateral_ratio;
c.call_price = price::max(seller_to_collateral.asset_id, pays.asset_id);
c.update_call_price();
});
}
else
{
modify( *call_itr, [&]( call_order_object& c ){
c.debt += pays.amount;
c.collateral += seller_to_collateral.amount + buyer_to_collateral.amount;
c.maintenance_collateral_ratio = order.maintenance_collateral_ratio;
c.update_call_price();
});
}
if( filled )
{
remove( order );
}
else
{
modify( order, [&]( short_order_object& b ) {
b.for_sale -= pays.amount;
b.available_collateral -= seller_to_collateral.amount;
assert( b.available_collateral > 0 );
assert( b.for_sale > 0 );
});
/**
* There are times when the AMOUNT_FOR_SALE * SALE_PRICE == 0 which means that we
* have hit the limit where the seller is asking for nothing in return. When this
* happens we must refund any balance back to the seller, it is too small to be
* sold at the sale price.
*/
if( order.amount_to_receive().amount == 0 )
{
adjust_balance(seller.get_id(), order.get_collateral());
if( order.get_collateral().asset_id == asset_id_type() )
{
const auto& statistics = seller.statistics(*this);
modify( statistics, [&]( account_statistics_object& b ){
b.total_core_in_orders -= order.available_collateral;
});
}
remove( order );
filled = true;
}
}
push_applied_operation( fill_order_operation{ order.id, order.seller, pays, receives, issuer_fees } );
return filled;
} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }
bool database::fill_order( const call_order_object& order, const asset& pays, const asset& receives )
{ try {
idump((pays)(receives)(order));
//idump((pays)(receives)(order));
assert( order.get_debt().asset_id == receives.asset_id );
assert( order.get_collateral().asset_id == pays.asset_id );
assert( order.get_collateral() >= pays );
@ -415,7 +298,7 @@ bool database::fill_order( const call_order_object& order, const asset& pays, co
const asset_dynamic_data_object& mia_ddo = mia.dynamic_asset_data_id(*this);
modify( mia_ddo, [&]( asset_dynamic_data_object& ao ){
idump((receives));
//idump((receives));
ao.current_supply -= receives.amount;
});
@ -439,6 +322,7 @@ bool database::fill_order( const call_order_object& order, const asset& pays, co
remove( order );
}
assert( pays.asset_id != receives.asset_id );
push_applied_operation( fill_order_operation{ order.id, order.borrower, pays, receives, asset(0, pays.asset_id) } );
return collateral_freed.valid();
@ -462,20 +346,30 @@ bool database::fill_order(const force_settlement_object& settle, const asset& pa
}
adjust_balance(settle.owner, receives - issuer_fees);
assert( pays.asset_id != receives.asset_id );
push_applied_operation( fill_order_operation{ settle.id, settle.owner, pays, receives, issuer_fees } );
return filled;
} FC_CAPTURE_AND_RETHROW( (settle)(pays)(receives) ) }
/**
* Starting with the least collateralized orders, fill them if their
* call price is above the max(lowest bid,call_limit).
*
* This method will return true if it filled a short or limit
*
* @param mia - the market issued asset that should be called.
* @param enable_black_swan - when adjusting collateral, triggering a black swan is invalid and will throw
* if enable_black_swan is not set to true.
*
* @return true if a margin call was executed.
*/
bool database::check_call_orders( const asset_object& mia )
bool database::check_call_orders(const asset_object& mia, bool enable_black_swan)
{ try {
if( !mia.is_market_issued() ) return false;
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
if( bitasset.current_feed.call_limit.is_null() ) return false;
if( bitasset.is_prediction_market ) return false;
if( bitasset.current_feed.settlement_price.is_null() ) return false;
const call_order_index& call_index = get_index_type<call_order_index>();
const auto& call_price_index = call_index.indices().get<by_price>();
@ -483,65 +377,52 @@ bool database::check_call_orders( const asset_object& mia )
const limit_order_index& limit_index = get_index_type<limit_order_index>();
const auto& limit_price_index = limit_index.indices().get<by_price>();
const short_order_index& short_index = get_index_type<short_order_index>();
const auto& short_price_index = short_index.indices().get<by_price>();
// looking for limit orders selling the most USD for the least CORE
auto max_price = price::max( mia.id, bitasset.options.short_backing_asset );
// stop when limit orders are selling too little USD for too much CORE
auto min_price = bitasset.current_feed.max_short_squeeze_price();
auto short_itr = short_price_index.lower_bound( price::max( mia.id, bitasset.options.short_backing_asset ) );
auto short_end = short_price_index.upper_bound( ~bitasset.current_feed.call_limit );
assert( max_price.base.asset_id == min_price.base.asset_id );
// NOTE limit_price_index is sorted from greatest to least
auto limit_itr = limit_price_index.lower_bound( max_price );
auto limit_end = limit_price_index.upper_bound( min_price );
auto limit_itr = limit_price_index.lower_bound( price::max( mia.id, bitasset.options.short_backing_asset ) );
auto limit_end = limit_price_index.upper_bound( ~bitasset.current_feed.call_limit );
if( limit_itr == limit_end ) {
return false;
}
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
bool filled_short_or_limit = false;
bool filled_limit = false;
while( call_itr != call_end )
{
bool current_is_limit = true;
bool filled_call = false;
price match_price;
asset usd_for_sale;
if( limit_itr != limit_end )
{
assert( limit_itr != limit_price_index.end() );
if( short_itr != short_end && limit_itr->sell_price < short_itr->sell_price )
{
assert( short_itr != short_price_index.end() );
current_is_limit = false;
match_price = short_itr->sell_price;
usd_for_sale = short_itr->amount_for_sale();
}
else
{
current_is_limit = true;
match_price = limit_itr->sell_price;
usd_for_sale = limit_itr->amount_for_sale();
}
match_price = limit_itr->sell_price;
usd_for_sale = limit_itr->amount_for_sale();
}
else if( short_itr != short_end )
{
assert( short_itr != short_price_index.end() );
current_is_limit = false;
match_price = short_itr->sell_price;
usd_for_sale = short_itr->amount_for_sale();
}
else return filled_short_or_limit;
else return filled_limit;
match_price.validate();
if( match_price > ~call_itr->call_price )
{
return filled_short_or_limit;
return filled_limit;
}
auto usd_to_buy = call_itr->get_debt();
if( usd_to_buy * match_price > call_itr->get_collateral() )
{
elog( "black swan, we do not have enough collateral to cover at this price" );
globally_settle_asset( mia, call_itr->get_debt() / call_itr->get_collateral() );
FC_ASSERT( enable_black_swan );
//globally_settle_asset(mia, call_itr->get_debt() / call_itr->get_collateral());
globally_settle_asset(mia, bitasset.current_feed.settlement_price );// call_itr->get_debt() / call_itr->get_collateral());
return true;
}
@ -553,11 +434,9 @@ bool database::check_call_orders( const asset_object& mia )
call_pays = order_receives;
order_pays = usd_for_sale;
filled_short_or_limit = true;
filled_limit = true;
filled_call = (usd_to_buy == usd_for_sale);
}
else // fill call
{
} else { // fill call
call_receives = usd_to_buy;
order_receives = usd_to_buy * match_price;
call_pays = order_receives;
@ -568,20 +447,13 @@ bool database::check_call_orders( const asset_object& mia )
auto old_call_itr = call_itr;
if( filled_call ) ++call_itr;
fill_order( *old_call_itr, call_pays, call_receives );
if( current_is_limit )
{
auto old_limit_itr = !filled_call ? limit_itr++ : limit_itr;
fill_order( *old_limit_itr, order_pays, order_receives );
}
else
{
auto old_short_itr = !filled_call ? short_itr++ : short_itr;
fill_order( *old_short_itr, order_pays, order_receives );
}
fill_order(*old_call_itr, call_pays, call_receives);
auto old_limit_itr = filled_limit ? limit_itr++ : limit_itr;
fill_order(*old_limit_itr, order_pays, order_receives);
} // whlie call_itr != call_end
return filled_short_or_limit;
return filled_limit;
} FC_CAPTURE_AND_RETHROW() }
void database::pay_order( const account_object& receiver, const asset& receives, const asset& pays )
@ -594,16 +466,6 @@ void database::pay_order( const account_object& receiver, const asset& receives,
adjust_balance(receiver.get_id(), receives);
}
/**
* For Market Issued assets Managed by Delegates, any fees collected in the MIA need
* to be sold and converted into CORE by accepting the best offer on the table.
*/
bool database::convert_fees( const asset_object& mia )
{
if( mia.issuer != account_id_type() ) return false;
return false;
}
asset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount )
{
assert( trade_asset.id == trade_amount.asset_id );
@ -611,7 +473,7 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass
if( !trade_asset.charges_market_fees() )
return trade_asset.amount(0);
if( trade_asset.options.market_fee_percent == 0 )
return trade_asset.amount(trade_asset.options.min_market_fee);
return trade_asset.amount(0);
fc::uint128 a(trade_amount.amount.value);
a *= trade_asset.options.market_fee_percent;
@ -620,8 +482,6 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass
if( percent_fee.amount > trade_asset.options.max_market_fee )
percent_fee.amount = trade_asset.options.max_market_fee;
else if( percent_fee.amount < trade_asset.options.min_market_fee )
percent_fee.amount = trade_asset.options.min_market_fee;
return percent_fee;
}
@ -636,7 +496,7 @@ asset database::pay_market_fees( const asset_object& recv_asset, const asset& re
{
const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this);
modify( recv_dyn_data, [&]( asset_dynamic_data_object& obj ){
idump((issuer_fees));
//idump((issuer_fees));
obj.accumulated_fees += issuer_fees.amount;
});
}

View file

@ -20,12 +20,12 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/uint128.hpp>
@ -65,8 +65,8 @@ void database::update_signing_witness(const witness_object& signing_witness, con
modify( signing_witness, [&]( witness_object& _wit )
{
_wit.last_secret = new_block.previous_secret;
_wit.next_secret = new_block.next_secret_hash;
_wit.previous_secret = new_block.previous_secret;
_wit.next_secret_hash = new_block.next_secret_hash;
_wit.accumulated_income += witness_pay;
} );
}
@ -102,7 +102,7 @@ void database::clear_expired_proposals()
const proposal_object& proposal = *proposal_expiration_index.begin();
processed_transaction result;
try {
if( proposal.is_authorized_to_execute(this) )
if( proposal.is_authorized_to_execute(*this) )
{
result = push_proposal(proposal);
//TODO: Do something with result so plugins can process it.
@ -118,29 +118,22 @@ void database::clear_expired_proposals()
void database::clear_expired_orders()
{
transaction_evaluation_state cancel_context(this, true);
with_skip_flags(
get_node_properties().skip_flags | skip_authority_check, [&](){
transaction_evaluation_state cancel_context(this);
//Cancel expired limit orders
auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>();
while( !limit_index.empty() && limit_index.begin()->expiration <= head_block_time() )
{
limit_order_cancel_operation canceler;
const limit_order_object& order = *limit_index.begin();
canceler.fee_paying_account = order.seller;
canceler.order = order.id;
apply_operation(cancel_context, canceler);
}
//Cancel expired limit orders
auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>();
while( !limit_index.empty() && limit_index.begin()->expiration <= head_block_time() )
{
limit_order_cancel_operation canceler;
const limit_order_object& order = *limit_index.begin();
canceler.fee_paying_account = order.seller;
canceler.order = order.id;
apply_operation(cancel_context, canceler);
}
});
//Cancel expired short orders
auto& short_index = get_index_type<short_order_index>().indices().get<by_expiration>();
while( !short_index.empty() && short_index.begin()->expiration <= head_block_time() )
{
const short_order_object& order = *short_index.begin();
short_order_cancel_operation canceler;
canceler.fee_paying_account = order.seller;
canceler.order = order.id;
apply_operation(cancel_context, canceler);
}
//Process expired force settlement orders
auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>();
@ -187,9 +180,11 @@ void database::clear_expired_orders()
max_settlement_volume = mia_object.amount(mia.max_force_settlement_volume(mia_object.dynamic_data(*this).current_supply));
if( mia.force_settled_volume >= max_settlement_volume.amount )
{
/*
ilog("Skipping force settlement in ${asset}; settled ${settled_volume} / ${max_volume}",
("asset", mia_object.symbol)("settlement_price_null",mia.current_feed.settlement_price.is_null())
("settled_volume", mia.force_settled_volume)("max_volume", max_settlement_volume));
*/
if( next_asset() )
continue;
break;
@ -224,12 +219,26 @@ void database::clear_expired_orders()
void database::update_expired_feeds()
{
auto& asset_idx = get_index_type<asset_bitasset_data_index>();
for( const asset_bitasset_data_object* b : asset_idx )
if( b->feed_is_expired(head_block_time()) )
modify(*b, [this](asset_bitasset_data_object& a) {
auto& asset_idx = get_index_type<asset_index>().indices();
for( const asset_object& a : asset_idx )
{
if( !a.is_market_issued() )
continue;
const asset_bitasset_data_object& b = a.bitasset_data(*this);
if( b.feed_is_expired(head_block_time()) )
{
modify(b, [this](asset_bitasset_data_object& a) {
a.update_median_feeds(head_block_time());
});
check_call_orders(b.current_feed.settlement_price.base.asset_id(*this));
}
if( !b.current_feed.core_exchange_rate.is_null() &&
a.options.core_exchange_rate != b.current_feed.core_exchange_rate )
modify(a, [&b](asset_object& a) {
a.options.core_exchange_rate = b.current_feed.core_exchange_rate;
});
}
}
void database::update_withdraw_permissions()

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 )
@ -93,7 +90,7 @@ vector<witness_id_type> database::get_near_witness_schedule()const
return result;
}
void database::update_witness_schedule(signed_block next_block)
void database::update_witness_schedule(const signed_block& next_block)
{
const global_property_object& gpo = get_global_properties();
const witness_schedule_object& wso = get(witness_schedule_id_type());
@ -106,10 +103,13 @@ void database::update_witness_schedule(signed_block next_block)
// triggering FC_ASSERT elsewhere
assert( schedule_slot > 0 );
witness_id_type first_witness;
bool slot_is_near = wso.scheduler.get_slot( schedule_slot-1, first_witness );
witness_id_type wit;
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
assert( witness_scheduler_rng::seed_length == wso.rng_seed.size() );
@ -118,14 +118,22 @@ void database::update_witness_schedule(signed_block next_block)
_wso.slots_since_genesis += schedule_slot;
witness_scheduler_rng rng(wso.rng_seed.data, _wso.slots_since_genesis);
_wso.scheduler._min_token_count = gpo.active_witnesses.size() / 2;
uint32_t drain = schedule_slot;
while( drain > 0 )
_wso.scheduler._min_token_count = std::max(int(gpo.active_witnesses.size()) / 2, 1);
if( slot_is_near )
{
if( _wso.scheduler.size() == 0 )
break;
_wso.scheduler.consume_schedule();
--drain;
uint32_t drain = schedule_slot;
while( drain > 0 )
{
if( _wso.scheduler.size() == 0 )
break;
_wso.scheduler.consume_schedule();
--drain;
}
}
else
{
_wso.scheduler.reset_schedule( first_witness );
}
while( !_wso.scheduler.get_slot(schedule_needs_filled, wit) )
{
@ -133,6 +141,16 @@ void database::update_witness_schedule(signed_block next_block)
memcpy(_wso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
}
_wso.last_scheduling_block = next_block.block_num();
_wso.recent_slots_filled = (
(_wso.recent_slots_filled << 1)
+ 1) << (schedule_slot - 1);
});
}
uint32_t database::witness_participation_rate()const
{
const witness_schedule_object& wso = get(witness_schedule_id_type());
return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128;
}
} }

View file

@ -17,27 +17,29 @@
*/
#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/key_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/delegate_object.hpp>
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/short_order_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
database& generic_evaluator::db()const { return trx_state->db(); }
database& generic_evaluator::db()const { return trx_state->db(); }
operation_result generic_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply )
{
{ try {
trx_state = &eval_state;
check_required_authorities(op);
auto result = evaluate( op );
if( apply ) result = this->apply( op );
return result;
}
} FC_CAPTURE_AND_RETHROW() }
void generic_evaluator::prepare_fee(account_id_type account_id, asset fee)
{
@ -55,7 +57,8 @@ namespace graphene { namespace chain {
asset fee_from_pool = fee_from_account * fee_asset->options.core_exchange_rate;
FC_ASSERT( fee_from_pool.asset_id == asset_id_type() );
core_fee_paid = fee_from_pool.amount;
FC_ASSERT( core_fee_paid <= fee_asset_dyn_data->fee_pool );
FC_ASSERT( core_fee_paid <= fee_asset_dyn_data->fee_pool, "Fee pool balance of '${b}' is less than the ${r} required to convert ${c}",
("r", db().to_pretty_string( fee_from_pool))("b",db().to_pretty_string(fee_asset_dyn_data->fee_pool))("c",db().to_pretty_string(fee)) );
}
}
@ -75,60 +78,52 @@ namespace graphene { namespace chain {
} FC_CAPTURE_AND_RETHROW() }
bool generic_evaluator::verify_authority( const account_object& a, authority::classification c )
{
{ try {
return trx_state->check_authority( a, c );
}
} FC_CAPTURE_AND_RETHROW( (a)(c) ) }
void generic_evaluator::check_required_authorities(const operation& op)
{
{ try {
flat_set<account_id_type> active_auths;
flat_set<account_id_type> owner_auths;
op.visit(operation_get_required_auths(active_auths, owner_auths));
vector<authority> other_auths;
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));
}
}
/*
bool generic_evaluator::verify_signature( const key_object& k )
{
return trx_state->_skip_signature_check || trx_state->signed_by( k.id );
}
*/
object_id_type generic_evaluator::get_relative_id( object_id_type rel_id )const
{
if( rel_id.space() == relative_protocol_ids )
for( const auto& auth : other_auths )
{
FC_ASSERT( rel_id.instance() < trx_state->operation_results.size() );
// fetch the object just to make sure it exists.
auto r = trx_state->operation_results[rel_id.instance()].get<object_id_type>();
db().get_object( r ); // make sure it exists.
return r;
GRAPHENE_ASSERT(
trx_state->check_authority(auth),
tx_missing_other_auth,
"missing required authority ${auth}",
("auth",auth)("sigs",trx_state->_sigs));
}
return rel_id;
}
authority generic_evaluator::resolve_relative_ids( const authority& a )const
} FC_CAPTURE_AND_RETHROW( (op) ) }
void generic_evaluator::verify_authority_accounts( const authority& a )const
{
authority result;
result.auths.reserve( a.auths.size() );
result.weight_threshold = a.weight_threshold;
for( const auto& item : a.auths )
{
auto id = get_relative_id( item.first );
FC_ASSERT( id.type() == key_object_type || id.type() == account_object_type );
result.auths[id] = item.second;
}
return result;
const auto& chain_params = db().get_global_properties().parameters;
FC_ASSERT( a.num_auths() <= chain_params.maximum_authority_membership );
for( const auto& acnt : a.account_auths )
acnt.first(db());
}
} }

View file

@ -40,10 +40,9 @@ void fork_database::start_block( signed_block b )
_head = item;
}
shared_ptr<fork_item> fork_database::push_block( signed_block b )
shared_ptr<fork_item> fork_database::push_block( const signed_block& b )
{
auto item = std::make_shared<fork_item>( std::move(b) );
//wdump((item->num)(_head?_head->num:0));
auto item = std::make_shared<fork_item>( b );
if( _head && b.previous != block_id_type() )
{

View file

@ -16,12 +16,12 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/authority.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/db/generic_index.hpp>
#include <boost/multi_index/composite_key.hpp>
namespace graphene { namespace chain {
class database;
/**
* @class account_statistics_object
@ -64,8 +64,8 @@ namespace graphene { namespace chain {
* them yet (registrar, referrer, lifetime referrer, network, etc). This is used as an optimization to avoid
* doing massive amounts of uint128 arithmetic on each and every operation.
*
*These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance
*interval.
* These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance
* interval.
*/
share_type pending_fees;
/**
@ -76,6 +76,8 @@ namespace graphene { namespace chain {
/// @brief Calculate the percentage discount this user receives on his fees
uint16_t calculate_bulk_discount_percent(const chain_parameters& params)const;
/// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees
void process_fees(const account_object& a, database& d) const;
};
/**
@ -106,7 +108,7 @@ namespace graphene { namespace chain {
* @ingroup protocol
*
* Accounts are the primary unit of authority on the graphene system. Users must have an account in order to use
* assets, trade in the markets, vote for delegates, etc.
* assets, trade in the markets, vote for committee_members, etc.
*/
class account_object : public graphene::db::annotated_object<account_object>
{
@ -141,6 +143,7 @@ namespace graphene { namespace chain {
/// The account's name. This name must be unique among all account names on the graph. May not be empty.
string name;
/**
* The owner authority represents absolute control over the account. Usually the keys in this authority will
* be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes
@ -151,19 +154,9 @@ namespace graphene { namespace chain {
/// The owner authority contains the hot keys of the account. This authority has control over nearly all
/// operations the account may perform.
authority active;
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
/// multiple keys in it.
key_id_type memo_key;
/// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake
/// will be counted as voting for the referenced account's selected votes instead.
account_id_type voting_account;
uint16_t num_witness = 0;
uint16_t num_committee = 0;
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
/// account's balance of core asset.
flat_set<vote_id_type> votes;
typedef account_options options_type;
account_options options;
/// The reference implementation records the account's statistics in a separate object. This field contains the
/// ID of that object.
@ -189,6 +182,12 @@ namespace graphene { namespace chain {
* Vesting balance which receives cashback_reward deposits.
*/
optional<vesting_balance_id_type> cashback_vb;
template<typename DB>
const vesting_balance_object& cashback_balance(const DB& db)const
{
FC_ASSERT(cashback_vb);
return db.get(*cashback_vb);
}
/// @return true if this is a lifetime member account; false otherwise.
bool is_lifetime_member()const
@ -231,8 +230,50 @@ namespace graphene { namespace chain {
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = meta_account_object_type;
key_id_type memo_key;
delegate_id_type delegate_id; // optional
public_key_type memo_key;
committee_member_id_type committee_member_id; // optional
};
/**
* @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account
* is an potential signing authority.
*/
class account_member_index : public secondary_index
{
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_removed( const object& obj ) override;
virtual void about_to_modify( const object& before ) override;
virtual void object_modified( const object& after ) override;
/** given an account or key, map it to the set of accounts that reference it in an active or owner authority */
map< account_id_type, set<account_id_type> > account_to_account_memberships;
map< public_key_type, set<account_id_type> > account_to_key_memberships;
protected:
set<account_id_type> get_account_members( const account_object& a )const;
set<public_key_type> get_key_members( const account_object& a )const;
set<account_id_type> before_account_members;
set<public_key_type> before_key_members;
};
/**
* @brief This secondary index will allow a reverse lookup of all accounts that have been referred by
* a particular account.
*/
class account_referrer_index : public secondary_index
{
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_removed( const object& obj ) override;
virtual void about_to_modify( const object& before ) override;
virtual void object_modified( const object& after ) override;
/** maps the referrer to the set of accounts that they have referred */
map< account_id_type, set<account_id_type> > referred_by;
};
struct by_asset;
@ -269,14 +310,14 @@ namespace graphene { namespace chain {
account_object,
indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_name>, member<account_object, string, &account_object::name> >
ordered_unique< tag<by_name>, member<account_object, string, &account_object::name> >
>
> account_object_multi_index_type;
> account_multi_index_type;
/**
* @ingroup object_index
*/
typedef generic_index<account_object, account_object_multi_index_type> account_index;
typedef generic_index<account_object, account_multi_index_type> account_index;
}}
@ -284,8 +325,8 @@ FC_REFLECT_DERIVED( graphene::chain::account_object,
(graphene::db::annotated_object<graphene::chain::account_object>),
(membership_expiration_date)(registrar)(referrer)(lifetime_referrer)
(network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)
(name)(owner)(active)(memo_key)(voting_account)(num_witness)(num_committee)(votes)
(statistics)(whitelisting_accounts)(blacklisting_accounts)(cashback_vb) )
(name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts)
(cashback_vb) )
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
(graphene::db::object),
@ -293,7 +334,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
FC_REFLECT_DERIVED( graphene::chain::meta_account_object,
(graphene::db::object),
(memo_key)(delegate_id) )
(memo_key)(committee_member_id) )
FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object),
(most_recent_op)

View file

@ -16,18 +16,19 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/delegate_object.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class delegate_create_evaluator : public evaluator<delegate_create_evaluator>
class assert_evaluator : public evaluator<assert_evaluator>
{
public:
typedef delegate_create_operation operation_type;
typedef assert_operation operation_type;
object_id_type do_evaluate( const delegate_create_operation& o );
object_id_type do_apply( const delegate_create_operation& o );
void_result do_evaluate( const assert_operation& o );
void_result do_apply( const assert_operation& o );
};
} } // graphene::chain

View file

@ -16,8 +16,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
@ -27,7 +27,7 @@ namespace graphene { namespace chain {
public:
typedef asset_create_operation operation_type;
object_id_type do_evaluate( const asset_create_operation& o );
void_result do_evaluate( const asset_create_operation& o );
object_id_type do_apply( const asset_create_operation& o );
};
@ -42,12 +42,12 @@ namespace graphene { namespace chain {
const account_object* to_account = nullptr;
};
class asset_burn_evaluator : public evaluator<asset_burn_evaluator>
class asset_reserve_evaluator : public evaluator<asset_reserve_evaluator>
{
public:
typedef asset_burn_operation operation_type;
void_result do_evaluate( const asset_burn_operation& o );
void_result do_apply( const asset_burn_operation& o );
typedef asset_reserve_operation operation_type;
void_result do_evaluate( const asset_reserve_operation& o );
void_result do_apply( const asset_reserve_operation& o );
const asset_dynamic_data_object* asset_dyn_data = nullptr;
const account_object* from_account = nullptr;
@ -113,8 +113,8 @@ namespace graphene { namespace chain {
public:
typedef asset_settle_operation operation_type;
object_id_type do_evaluate(const operation_type& op);
object_id_type do_apply(const operation_type& op);
void_result do_evaluate(const operation_type& op);
operation_result do_apply(const operation_type& op);
const asset_object* asset_to_settle = nullptr;
};

View file

@ -16,8 +16,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/asset_ops.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/db/flat_index.hpp>
#include <graphene/db/generic_index.hpp>
@ -33,6 +33,7 @@
namespace graphene { namespace chain {
class account_object;
class database;
using namespace graphene::db;
/**
@ -89,6 +90,7 @@ namespace graphene { namespace chain {
bool charges_market_fees()const { return options.flags & charge_market_fee; }
/// @return true if this asset may only be transferred to/from the issuer or market orders
bool is_transfer_restricted()const { return options.flags & transfer_restricted; }
bool can_override()const { return options.flags & override_authority; }
/// Helper function to get an asset object with the given amount in this asset's type
asset amount(share_type a)const { return asset(a, id); }
@ -108,87 +110,17 @@ namespace graphene { namespace chain {
{ FC_ASSERT(amount.asset_id == id); return amount_to_pretty_string(amount.amount); }
/// Ticker symbol for this asset, i.e. "USD"
string symbol;
/// Maximum number of digits after the decimal point
uint64_t precision;
string symbol;
/// Maximum number of digits after the decimal point (must be <= 12)
uint8_t precision;
/// ID of the account which issued this asset.
account_id_type issuer;
account_id_type issuer;
/**
* @brief The asset_options struct contains options available on all assets in the network
*
* @note Changes to this struct will break protocol compatibility
*/
struct asset_options {
/// The maximum supply of this asset which may exist at any given time. This can be as large as
/// GRAPHENE_MAX_SHARE_SUPPLY
share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
/// When this asset is traded on the markets, this percentage of the total traded will be exacted and paid
/// to the issuer. This is a fixed point value, representing hundredths of a percent, i.e. a value of 100
/// in this field means a 1% fee is charged on market trades of this asset.
uint16_t market_fee_percent = 0;
share_type max_market_fee = GRAPHENE_MAX_SHARE_SUPPLY;
share_type min_market_fee;
asset_options options;
/// The flags which the issuer has permission to update. See @ref asset_issuer_permission_flags
uint16_t issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
/// The currently active flags on this permission. See @ref asset_issuer_permission_flags
uint16_t flags = 0;
/// When a non-core asset is used to pay a fee, the blockchain must convert that asset to core asset in
/// order to accept the fee. If this asset's fee pool is funded, the chain will automatically deposite fees
/// in this asset to its accumulated fees, and withdraw from the fee pool the same amount as converted at
/// the core exchange rate.
price core_exchange_rate;
/// A set of accounts which maintain whitelists to consult for this asset. If enforce_white_list() returns
/// true, an account may only send, receive, trade, etc. in this asset if one of these accounts appears in
/// its account_object::whitelisting_accounts field.
flat_set<account_id_type> whitelist_authorities;
/// A set of accounts which maintain blacklists to consult for this asset. If enforce_white_list() returns
/// true, an account may only send, receive, trade, etc. in this asset if none of these accounts appears in
/// its account_object::blacklisting_accounts field. If the account is blacklisted, it may not transact in
/// this asset even if it is also whitelisted.
flat_set<account_id_type> blacklist_authorities;
/** defines the assets that this asset may be traded against in the market */
flat_set<asset_id_type> whitelist_markets;
/** defines the assets that this asset may not be traded against in the market, must not overlap whitelist */
flat_set<asset_id_type> blacklist_markets;
/// Perform internal consistency checks.
/// @throws fc::exception if any check fails
void validate()const;
} options;
/**
* @brief The bitasset_options struct contains configurable options available only to BitAssets.
*
* @note Changes to this struct will break protocol compatibility
*/
struct bitasset_options {
/// Time before a price feed expires
uint32_t feed_lifetime_sec = GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME;
/// This is the delay between the time a long requests settlement and the chain evaluates the settlement
uint32_t force_settlement_delay_sec = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY;
/// This is the percent to adjust the feed price in the short's favor in the event of a forced settlement
uint16_t force_settlement_offset_percent = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET;
/// Force settlement volume can be limited such that only a certain percentage of the total existing supply
/// of the asset may be force-settled within any given chain maintenance interval. This field stores the
/// percentage of the current supply which may be force settled within the current maintenance interval. If
/// force settlements come due in an interval in which the maximum volume has already been settled, the new
/// settlements will be enqueued and processed at the beginning of the next maintenance interval.
uint16_t maximum_force_settlement_volume = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME;
/// This speicifies which asset type is used to collateralize short sales
/// This field may only be updated if the current supply of the asset is zero.
asset_id_type short_backing_asset;
/// Perform internal consistency checks.
/// @throws fc::exception if any check fails
void validate()const;
};
/// Current supply, fee pool, and collected fees are stored in a separate object as they change frequently.
dynamic_asset_data_id_type dynamic_asset_data_id;
asset_dynamic_data_id_type dynamic_asset_data_id;
/// Extra data associated with BitAssets. This field is non-null if and only if is_market_issued() returns true
optional<asset_bitasset_data_id_type> bitasset_data_id;
@ -212,8 +144,11 @@ namespace graphene { namespace chain {
const asset_dynamic_data_object& dynamic_data(const DB& db)const
{ return db.get(dynamic_asset_data_id); }
/**
* The total amount of an asset that is reserved for future issuance.
*/
template<class DB>
share_type burned( const DB& db )const
share_type reserved( const DB& db )const
{ return options.max_supply - dynamic_data(db).current_supply; }
};
@ -230,10 +165,10 @@ namespace graphene { namespace chain {
static const uint8_t type_id = impl_asset_bitasset_data_type;
/// The tunable options for BitAssets are stored in this field.
asset_object::bitasset_options options;
bitasset_options options;
/// Feeds published for this asset. If issuer is not genesis, the keys in this map are the feed publishing
/// accounts; otherwise, the feed publishers are the currently active delegates and witnesses and this map
/// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing
/// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map
/// should be treated as an implementation detail. The timestamp on each feed is the time it was published.
flat_map<account_id_type, pair<time_point_sec,price_feed>> feeds;
/// This is the currently active price feed, calculated as the median of values from the currently active
@ -250,6 +185,22 @@ namespace graphene { namespace chain {
/// Calculate the maximum force settlement volume per maintenance interval, given the current share supply
share_type max_force_settlement_volume(share_type current_supply)const;
/** return true if there has been a black swan, false otherwise */
bool has_settlement()const { return !settlement_price.is_null(); }
/**
* In the event of a black swan, the swan price is saved in the settlement price, and all margin positions
* are settled at the same price with the siezed collateral being moved into the settlement fund. From this
* point on no further updates to the asset are permitted (no feeds, etc) and forced settlement occurs
* immediately when requested, using the settlement price and fund.
*/
///@{
/// Price at which force settlements of a black swanned asset will occur
price settlement_price;
/// Amount of collateral which is available for force settlement
share_type settlement_fund;
///@}
time_point_sec feed_expiration_time()const
{ return current_feed_publication_time + options.feed_lifetime_sec; }
bool feed_is_expired(time_point_sec current_time)const
@ -292,29 +243,10 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::
(options)
(force_settled_volume)
(is_prediction_market)
(settlement_price)
(settlement_fund)
)
FC_REFLECT( graphene::chain::asset_object::asset_options,
(max_supply)
(market_fee_percent)
(max_market_fee)
(min_market_fee)
(issuer_permissions)
(flags)
(core_exchange_rate)
(whitelist_authorities)
(blacklist_authorities)
(whitelist_markets)
(blacklist_markets)
)
FC_REFLECT( graphene::chain::asset_object::bitasset_options,
(feed_lifetime_sec)
(force_settlement_delay_sec)
(force_settlement_offset_percent)
(maximum_force_settlement_volume)
(short_backing_asset)
)
FC_REFLECT_DERIVED( graphene::chain::asset_object,
(graphene::db::annotated_object<graphene::chain::asset_object>),
(symbol)

View file

@ -0,0 +1,27 @@
#pragma once
#include <graphene/chain/protocol/transaction.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
class balance_claim_evaluator : public evaluator<balance_claim_evaluator>
{
public:
typedef balance_claim_operation operation_type;
const balance_object* balance = nullptr;
void_result do_evaluate(const balance_claim_operation& op);
/**
* @note the fee is always 0 for this particular operation because once the
* balance is claimed it frees up memory and it cannot be used to spam the network
*/
void_result do_apply(const balance_claim_operation& op);
};
} } // graphene::chain

View file

@ -0,0 +1,52 @@
#pragma once
#include <graphene/chain/vesting_balance_object.hpp>
namespace graphene { namespace chain {
class balance_object : public abstract_object<balance_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = balance_object_type;
bool is_vesting_balance()const
{ return vesting_policy.valid(); }
asset available(fc::time_point_sec now)const
{
return is_vesting_balance()? vesting_policy->get_allowed_withdraw({balance, now, {}})
: balance;
}
address owner;
asset balance;
optional<linear_vesting_policy> vesting_policy;
time_point_sec last_claim_date;
asset_id_type asset_type()const { return balance.asset_id; }
};
struct by_owner;
/**
* @ingroup object_index
*/
using balance_multi_index_type = multi_index_container<
balance_object,
indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_owner>, composite_key<
balance_object,
member<balance_object, address, &balance_object::owner>,
const_mem_fun<balance_object, asset_id_type, &balance_object::asset_type>
> >
>
>;
/**
* @ingroup object_index
*/
using balance_index = generic_index<balance_object, balance_multi_index_type>;
} }
FC_REFLECT_DERIVED( graphene::chain::balance_object, (graphene::db::object),
(owner)(balance)(vesting_policy)(last_claim_date) )

View file

@ -16,30 +16,28 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/db/object.hpp>
#include <graphene/chain/address.hpp>
#include <fc/static_variant.hpp>
#include <graphene/chain/types.hpp>
#include <fstream>
#include <graphene/chain/protocol/block.hpp>
namespace graphene { namespace chain {
/**
* @class key_object
* @brief maps an ID to a public key or address
* @ingroup object
* @ingroup protocol
*/
class key_object : public graphene::db::abstract_object<key_object>
class block_database
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = key_object_type;
void open( const fc::path& dbdir );
bool is_open()const;
void flush();
void close();
key_id_type get_id()const { return key_id_type( id.instance() ); }
address key_address()const;
const public_key_type& key()const { return key_data.get<public_key_type>(); }
void store( const block_id_type& id, const signed_block& b );
void remove( const block_id_type& id );
static_variant<address,public_key_type> key_data;
bool contains( const block_id_type& id )const;
block_id_type fetch_block_id( uint32_t block_num )const;
optional<signed_block> fetch_optional( const block_id_type& id )const;
optional<signed_block> fetch_by_number( uint32_t block_num )const;
optional<signed_block> last()const;
private:
mutable std::fstream _blocks;
mutable std::fstream _block_num_to_pos;
};
} }
FC_REFLECT_DERIVED( graphene::chain::key_object, (graphene::db::object), (key_data) )

View file

@ -0,0 +1,43 @@
/*
* 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 <graphene/chain/evaluator.hpp>
#include <graphene/chain/committee_member_object.hpp>
namespace graphene { namespace chain {
class committee_member_create_evaluator : public evaluator<committee_member_create_evaluator>
{
public:
typedef committee_member_create_operation operation_type;
void_result do_evaluate( const committee_member_create_operation& o );
object_id_type do_apply( const committee_member_create_operation& o );
};
class committee_member_update_global_parameters_evaluator : public evaluator<committee_member_update_global_parameters_evaluator>
{
public:
typedef committee_member_update_global_parameters_operation operation_type;
void_result do_evaluate( const committee_member_update_global_parameters_operation& o );
void_result do_apply( const committee_member_update_global_parameters_operation& o );
};
} } // graphene::chain

View file

@ -16,8 +16,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/asset.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
@ -25,28 +26,41 @@ namespace graphene { namespace chain {
class account_object;
/**
* @brief tracks information about a delegate account.
* @brief tracks information about a committee_member account.
* @ingroup object
*
* A delegate is responsible for setting blockchain parameters and has
* dynamic multi-sig control over the genesis account. The current set of
* active delegates has control.
* A committee_member is responsible for setting blockchain parameters and has
* dynamic multi-sig control over the committee account. The current set of
* active committee_members has control.
*
* Delegates were separated into a separate object to make iterating over
* the set of delegate easy.
* committee_members were separated into a separate object to make iterating over
* the set of committee_member easy.
*/
class delegate_object : public abstract_object<delegate_object>
class committee_member_object : public abstract_object<committee_member_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = delegate_object_type;
static const uint8_t type_id = committee_member_object_type;
account_id_type delegate_account;
vote_id_type vote_id;
account_id_type committee_member_account;
vote_id_type vote_id;
string url;
};
struct by_account;
using committee_member_multi_index_type = multi_index_container<
committee_member_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
hashed_unique< tag<by_account>,
member<committee_member_object, account_id_type, &committee_member_object::committee_member_account>
>
>
>;
using committee_member_index = generic_index<committee_member_object, committee_member_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::delegate_object, (graphene::db::object),
(delegate_account)
(vote_id) )
FC_REFLECT_DERIVED( graphene::chain::committee_member_object, (graphene::db::object),
(committee_member_account)(vote_id)(url) )

View file

@ -19,18 +19,26 @@
#define GRAPHENE_SYMBOL "CORE"
#define GRAPHENE_ADDRESS_PREFIX "GPH"
#define GRAPHENE_MAX_SYMBOL_NAME_LENGTH 16
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1
#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63
#define GRAPHENE_MIN_ASSET_SYMBOL_LENGTH 3
#define GRAPHENE_MAX_ASSET_SYMBOL_LENGTH 16
#define GRAPHENE_MAX_ASSET_NAME_LENGTH 127
#define GRAPHENE_MAX_SHARE_SUPPLY int64_t(1000000000000ll)
#define GRAPHENE_MAX_SHARE_SUPPLY int64_t(1000000000000000ll)
#define GRAPHENE_MAX_PAY_RATE 10000 /* 100% */
#define GRAPHENE_MAX_SIG_CHECK_DEPTH 2
#define GRAPHENE_MIN_WITNESS_COUNT 10
#define GRAPHENE_MIN_DELEGATE_COUNT 10
#define GRAPHENE_MIN_COMMITTEE_MEMBER_COUNT 10
/**
* Don't allow the delegates to publish a limit that would
* Don't allow the committee_members to publish a limit that would
* make the network unable to operate.
*/
#define GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT 1024
#define GRAPHENE_MIN_BLOCK_INTERVAL 1 /* seconds */
#define GRAPHENE_MAX_BLOCK_INTERVAL 30 /* seconds */
#define GRAPHENE_DEFAULT_BLOCK_INTERVAL 5 /* seconds */
@ -42,29 +50,38 @@
#define GRAPHENE_MIN_BLOCK_SIZE_LIMIT (GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT*5) // 5 transactions per block
#define GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT (GRAPHENE_MAX_BLOCK_INTERVAL * 5) // 5 transactions per block
#define GRAPHENE_BLOCKCHAIN_MAX_SHARES (1000*1000*int64_t(1000)*1000*int64_t(1000))
#define GRAPHENE_BLOCKCHAIN_PRECISION 100000
#define GRAPHENE_BLOCKCHAIN_PRECISION uint64_t( 100000 )
#define CORE GRAPHENE_BLOCKCHAIN_PRECISION
#define GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS 5
#define GRAPHENE_INITIAL_SUPPLY GRAPHENE_BLOCKCHAIN_MAX_SHARES
#define GRAPHENE_DEFAULT_TRANSFER_FEE (1*GRAPHENE_BLOCKCHAIN_PRECISION)
#define GRAPHENE_MAX_INSTANCE_ID (uint64_t(-1)>>16)
/** percentage fields are fixed point with a denominator of 10,000 */
#define GRAPHENE_100_PERCENT 10000
#define GRAPHENE_1_PERCENT (GRAPHENE_100_PERCENT/100)
/** NOTE: making this a power of 2 (say 2^15) would greatly accelerate fee calcs */
#define GRAPHENE_MAX_MARKET_FEE_PERCENT GRAPHENE_100_PERCENT
#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY (60*60*24) ///< 1 day
#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET 0 ///< 1%
#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME 2000 ///< 20%
#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME (20* GRAPHENE_1_PERCENT) ///< 20%
#define GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME (60*60*24) ///< 1 day
#define GRAPHENE_MAX_FEED_PRODUCERS 200
#define GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP 10
#define GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES 10
#define GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS 10
#define GRAPHENE_MIN_COLLATERAL_RATIO 1001 // lower than this could result in divide by 0
#define GRAPHENE_MAX_COLLATERAL_RATIO 32000 // higher than this is unnecessary and may exceed int16 storage
#define GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO 2000
#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750
/**
* These ratios are fixed point numbers with a denominator of GRAPHENE_COLLATERAL_RATIO_DENOM, the
* minimum maitenance collateral is therefore 1.001x and the default
* maintenance ratio is 1.75x
*/
///@{
#define GRAPHENE_COLLATERAL_RATIO_DENOM 1000
#define GRAPHENE_MIN_COLLATERAL_RATIO 1001 ///< lower than this could result in divide by 0
#define GRAPHENE_MAX_COLLATERAL_RATIO 32000 ///< higher than this is unnecessary and may exceed int16 storage
#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 ///< Call when collateral only pays off 175% the debt
#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1500 ///< Stop calling when collateral only pays off 150% of the debt
///@}
#define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24)
#define GRAPHENE_DEFAULT_NUM_WITNESSES (101)
@ -72,7 +89,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)
@ -82,7 +99,14 @@
#define GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD (GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(100))
#define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
#define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000)
#define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL
#define GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE 1
#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_MAX_WORKER_NAME_LENGTH 63
#define GRAPHENE_MAX_URL_LENGTH 127
// counter initialization values used to derive near and far future seeds for shuffling witnesses
// we use the fractional bits of sqrt(2) in hex
@ -99,7 +123,7 @@
// counter used to determine bits of entropy
// must be less than or equal to secret_hash_type::data_length()
#define GRAPHENE_RNG_SEED_LENGTH (224 / 8)
#define GRAPHENE_RNG_SEED_LENGTH (160 / 8)
/**
* every second, the fraction of burned core asset which cycles is
@ -112,15 +136,19 @@
#define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 )
#define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 )
#define GRAPHENE_LEGACY_NAME_IMPORT_PERIOD 3000000 /** 3 million blocks */
/**
* Reserved Account IDs with special meaning
*/
///@{
#define GRAPHENE_GENESIS_ACCOUNT (graphene::chain::account_id_type(0))
/// 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))
#define GRAPHENE_DELEGATE_ACCOUNT (graphene::chain::account_id_type(2))
/// Represents the current committee members
#define GRAPHENE_RELAXED_COMMITTEE_ACCOUNT (graphene::chain::account_id_type(2))
/// Represents the canonical account with NO authority (nobody can access funds in null account)
#define GRAPHENE_NULL_ACCOUNT (graphene::chain::account_id_type(3))
/// Represents the canonical account with WILDCARD authority (anybody can access funds in temp account)
#define GRAPHENE_TEMP_ACCOUNT (graphene::chain::account_id_type(4))
///@}

View file

@ -16,8 +16,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {

View file

@ -17,20 +17,21 @@
*/
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/block.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/node_property_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/fork_database.hpp>
#include <graphene/chain/block_database.hpp>
#include <graphene/chain/genesis_state.hpp>
#include <graphene/db/object_database.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/level_map.hpp>
#include <graphene/db/level_pod_map.hpp>
#include <graphene/db/simple_index.hpp>
#include <fc/signals.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <fc/log/logger.hpp>
#include <map>
@ -39,13 +40,36 @@ namespace graphene { namespace chain {
using graphene::db::abstract_object;
using graphene::db::object;
typedef vector<std::pair<fc::static_variant<address, public_key_type>, share_type >> genesis_allocation;
namespace detail
{
/**
* Class used to help the with_skip_flags implementation.
* It must be defined in this header because it must be
* available to the with_skip_flags implementation,
* which is a template and therefore must also be defined
* in this header.
*/
struct skip_flags_restorer
{
skip_flags_restorer( node_property_object& npo, uint32_t old_skip_flags )
: _npo( npo ), _old_skip_flags( old_skip_flags )
{}
~skip_flags_restorer()
{
_npo.skip_flags = _old_skip_flags;
}
node_property_object& _npo;
uint32_t _old_skip_flags;
};
}
/**
* @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 ////////////////////
@ -55,27 +79,40 @@ namespace graphene { namespace chain {
enum validation_steps
{
skip_nothing = 0x00,
skip_delegate_signature = 0x01, ///< used while reindexing
skip_transaction_signatures = 0x02, ///< used by non delegate nodes
skip_undo_block = 0x04, ///< used while reindexing
skip_undo_transaction = 0x08, ///< used while applying block
skip_transaction_dupe_check = 0x10, ///< used while reindexing
skip_fork_db = 0x20, ///< used while reindexing
skip_block_size_check = 0x40, ///< used when applying locally generated transactions
skip_tapos_check = 0x80, ///< used while reindexing -- note this skips expiration check as well
skip_authority_check = 0x100, ///< used while reindexing -- disables any checking of authority on transactions
skip_merkle_check = 0x200 ///< used while reindexing
skip_nothing = 0,
skip_witness_signature = 1 << 0, ///< used while reindexing
skip_transaction_signatures = 1 << 1, ///< used by non-witness nodes
skip_undo_block = 1 << 2, ///< used while reindexing
skip_undo_transaction = 1 << 3, ///< used while applying block
skip_transaction_dupe_check = 1 << 4, ///< used while reindexing
skip_fork_db = 1 << 5, ///< used while reindexing
skip_block_size_check = 1 << 6, ///< used when applying locally generated transactions
skip_tapos_check = 1 << 7, ///< used while reindexing -- note this skips expiration check as well
skip_authority_check = 1 << 8, ///< used while reindexing -- disables any checking of authority on transactions
skip_merkle_check = 1 << 9, ///< used while reindexing
skip_assert_evaluation = 1 << 10 ///< used while reindexing
};
void open(const fc::path& data_dir, const genesis_allocation& initial_allocation = genesis_allocation());
/**
* @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
*
* This method may be called after or instead of @ref database::open, and will rebuild the object graph by
* replaying blockchain history. When this method exits successfully, the database will be open.
*/
void reindex(fc::path data_dir, const genesis_allocation& initial_allocation = genesis_allocation());
void reindex(fc::path data_dir, const genesis_state_type& initial_allocation = genesis_state_type());
/**
* @brief wipe Delete database from disk, and potentially the raw chain as well.
@ -99,8 +136,20 @@ namespace graphene { namespace chain {
optional<signed_block> fetch_block_by_number( uint32_t num )const;
const signed_transaction& get_recent_transaction( const transaction_id_type& trx_id )const;
/**
* Calculate the percent of block production slots that were missed in the
* past 128 blocks, not including the current block.
*/
uint32_t witness_participation_rate()const;
void add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts );
const flat_map<uint32_t,block_id_type> get_checkpoints()const { return _checkpoints; }
bool push_block( const signed_block& b, uint32_t skip = skip_nothing );
processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing );
bool _push_block( const signed_block& b );
processed_transaction _push_transaction( const signed_transaction& trx );
///@throws fc::exception if the proposed transaction fails to apply.
processed_transaction push_proposal( const proposal_object& proposal );
@ -108,7 +157,12 @@ namespace graphene { namespace chain {
const fc::time_point_sec when,
witness_id_type witness_id,
const fc::ecc::private_key& block_signing_private_key,
uint32_t skip = 0
uint32_t skip
);
signed_block _generate_block(
const fc::time_point_sec when,
witness_id_type witness_id,
const fc::ecc::private_key& block_signing_private_key
);
void pop_block();
@ -127,6 +181,8 @@ namespace graphene { namespace chain {
void set_applied_operation_result( uint32_t op_id, const operation_result& r );
const vector<operation_history_object>& get_applied_operations()const;
string to_pretty_string( const asset& a )const;
/**
* This signal is emitted after all operations and virtual operation for a
* block have been applied but before the get_applied_operations() are cleared.
@ -138,7 +194,7 @@ namespace graphene { namespace chain {
fc::signal<void(const signed_block&)> applied_block;
/**
* After a block has been applied and committed. The callback
* Emitted After a block has been applied and committed. The callback
* should not yield and should execute quickly.
*/
fc::signal<void(const vector<object_id_type>&)> changed_objects;
@ -193,20 +249,41 @@ namespace graphene { namespace chain {
const asset_object& get_core_asset()const;
const global_property_object& get_global_properties()const;
const dynamic_global_property_object& get_dynamic_global_properties()const;
const fee_schedule_type& current_fee_schedule()const;
const node_property_object& get_node_properties()const;
const fee_schedule& current_fee_schedule()const;
time_point_sec head_block_time()const;
uint32_t head_block_num()const;
block_id_type head_block_id()const;
time_point_sec head_block_time()const;
uint32_t head_block_num()const;
block_id_type head_block_id()const;
witness_id_type head_block_witness()const;
decltype( chain_parameters::block_interval ) block_interval( )const;
node_property_object& node_properties();
/**
* Set the skip_flags to the given value, call callback,
* then reset skip_flags to their previous value after
* callback is done.
*/
template< typename Lambda >
void with_skip_flags(
uint32_t skip_flags,
Lambda callback )
{
node_property_object& npo = node_properties();
detail::skip_flags_restorer restorer( npo, npo.skip_flags );
npo.skip_flags = skip_flags;
callback();
return;
}
//////////////////// db_init.cpp ////////////////////
void initialize_evaluators();
/// Reset the object graph in-memory
void initialize_indexes();
void init_genesis(const genesis_allocation& initial_allocation = genesis_allocation());
void init_genesis(const genesis_state_type& genesis_state = genesis_state_type());
template<typename EvaluatorType>
void register_evaluator()
@ -234,8 +311,6 @@ namespace graphene { namespace chain {
asset get_balance(account_id_type owner, asset_id_type asset_id)const;
/// This is an overloaded method.
asset get_balance(const account_object& owner, const asset_object& asset_obj)const;
/// This is an overloaded method.
asset get_balance(const account_object* owner, const asset_object* asset_obj)const;
/**
* @brief Adjust a particular account's balance in a given asset by a delta
@ -245,8 +320,6 @@ namespace graphene { namespace chain {
void adjust_balance(account_id_type account, asset delta);
/// This is an overloaded method.
void adjust_balance(const account_object& account, asset delta);
/// This is an overloaded method.
void adjust_balance(const account_object* account, asset delta);
/**
* If delta.asset_id is a core asset, adjusts account statistics
@ -267,6 +340,16 @@ namespace graphene { namespace chain {
void cancel_order(const force_settlement_object& order, bool create_virtual_op = true);
void cancel_order(const limit_order_object& order, bool create_virtual_op = true);
/**
* @brief Process a new limit order through the markets
* @param order The new order to process
* @return true if order was completely filled; false otherwise
*
* This function takes a new limit order, and runs the markets attempting to match it with existing orders
* already on the books.
*/
bool apply_order(const limit_order_object& new_order_object, bool allow_black_swan = true);
/**
* Matches the two orders,
*
@ -281,7 +364,6 @@ namespace graphene { namespace chain {
template<typename OrderType>
int match( const limit_order_object& bid, const OrderType& ask, const price& match_price );
int match( const limit_order_object& bid, const limit_order_object& ask, const price& trade_price );
int match( const limit_order_object& bid, const short_order_object& ask, const price& trade_price );
/// @return the amount of asset settled
asset match(const call_order_object& call,
const force_settlement_object& settle,
@ -293,16 +375,14 @@ namespace graphene { namespace chain {
* @return true if the order was completely filled and thus freed.
*/
bool fill_order( const limit_order_object& order, const asset& pays, const asset& receives );
bool fill_order( const short_order_object& order, const asset& pays, const asset& receives );
bool fill_order( const call_order_object& order, const asset& pays, const asset& receives );
bool fill_order( const force_settlement_object& settle, const asset& pays, const asset& receives );
bool check_call_orders( const asset_object& mia );
bool check_call_orders( const asset_object& mia, bool enable_black_swan = true );
// helpers to fill_order
void pay_order( const account_object& receiver, const asset& receives, const asset& pays );
bool convert_fees( const asset_object& mia );
asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount);
asset pay_market_fees( const asset_object& recv_asset, const asset& receives );
@ -314,24 +394,28 @@ namespace graphene { namespace chain {
protected:
//Mark pop_undo() as protected -- we do not want outside calling pop_undo(); it should call pop_block() instead
void pop_undo() { object_database::pop_undo(); }
void notify_changed_objects();
private:
optional<undo_database::session> _pending_block_session;
vector< unique_ptr<op_evaluator> > _operation_evaluators;
template<class ObjectType>
vector<std::reference_wrapper<const ObjectType>> sort_votable_objects(size_t count)const;
template<class Index>
vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(size_t count)const;
//////////////////// db_block.cpp ////////////////////
void apply_block( const signed_block& next_block, uint32_t skip = skip_nothing );
processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing );
void _apply_block( const signed_block& next_block );
processed_transaction _apply_transaction( const signed_transaction& trx );
operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op );
///Steps involved in applying a new block
///@{
const witness_object& validate_block_header( uint32_t skip, const signed_block& next_block )const;
const witness_object& _validate_block_header( const signed_block& next_block )const;
void create_block_summary(const signed_block& next_block);
//////////////////// db_update.cpp ////////////////////
@ -345,7 +429,7 @@ namespace graphene { namespace chain {
void update_withdraw_permissions();
//////////////////// db_witness_schedule.cpp ////////////////////
void update_witness_schedule(signed_block next_block); /// no-op except for scheduling blocks
void update_witness_schedule(const signed_block& next_block); /// no-op except for scheduling blocks
///Steps performed only at maintenance intervals
///@{
@ -357,7 +441,7 @@ namespace graphene { namespace chain {
void pay_workers( share_type& budget );
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
void update_active_witnesses();
void update_active_delegates();
void update_active_committee_members();
template<class... Types>
void perform_account_maintenance(std::tuple<Types...> helpers);
@ -376,7 +460,7 @@ namespace graphene { namespace chain {
* until the fork is resolved. This should make maintaining
* the fork tree relatively simple.
*/
graphene::db::level_map<block_id_type, signed_block> _block_id_to_block;
block_database _block_id_to_block;
/**
* Contains the set of ops that are in the process of being applied from
@ -395,6 +479,10 @@ namespace graphene { namespace chain {
vector<uint64_t> _witness_count_histogram_buffer;
vector<uint64_t> _committee_count_histogram_buffer;
uint64_t _total_voting_stake;
flat_map<uint32_t,block_id_type> _checkpoints;
node_property_object _node_property_object;
};
namespace detail
@ -415,12 +503,24 @@ namespace graphene { namespace chain {
(void)l;
}
}
template<class... Types>
void database::perform_account_maintenance(std::tuple<Types...> helpers)
{
const auto& idx = get_index_type<account_index>().indices();
for( const account_object& a : idx )
detail::for_each(helpers, a, detail::gen_seq<sizeof...(Types)>());
}
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

@ -16,12 +16,12 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/operations.hpp>
#include <graphene/chain/authority.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene { namespace chain {
class database;
struct signed_transaction;
class generic_evaluator;
class transaction_evaluation_state;
@ -42,174 +42,175 @@ namespace graphene { namespace chain {
*/
class evaluation_observer
{
public:
virtual ~evaluation_observer(){}
public:
virtual ~evaluation_observer(){}
virtual void pre_evaluate(
const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge ) {}
virtual void pre_evaluate(const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge)
{}
virtual void post_evaluate(
const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result ) {}
virtual void post_evaluate(const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result)
{}
virtual void evaluation_failed(
const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result ) {}
virtual void evaluation_failed(const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result)
{}
};
class generic_evaluator
{
public:
virtual ~generic_evaluator(){}
public:
virtual ~generic_evaluator(){}
virtual int get_type()const = 0;
virtual operation_result start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply );
virtual int get_type()const = 0;
virtual operation_result start_evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply);
/** @note derived classes should ASSUME that the default validation that is
* indepenent of chain state should be performed by op.validate() and should
* not perform these extra checks.
*/
virtual operation_result evaluate( const operation& op ) = 0;
virtual operation_result apply( const operation& op ) = 0;
/**
* @note derived classes should ASSUME that the default validation that is
* indepenent of chain state should be performed by op.validate() and should
* not perform these extra checks.
*/
virtual operation_result evaluate(const operation& op) = 0;
virtual operation_result apply(const operation& op) = 0;
database& db()const;
database& db()const;
void check_required_authorities(const operation& op);
void check_required_authorities(const operation& op);
protected:
/**
* @brief Fetch objects relevant to fee payer and set pointer members
* @param account_id Account which is paying the fee
* @param fee The fee being paid. May be in assets other than core.
*
* This method verifies that the fee is valid and sets the object pointer members and the fee fields. It should
* be called during do_evaluate.
*/
void prepare_fee(account_id_type account_id, asset fee);
/// Pays the fee and returns the number of CORE asset that were paid.
void pay_fee();
/**
* @brief Fetch objects relevant to fee payer and set pointer members
* @param account_id Account which is paying the fee
* @param fee The fee being paid. May be in assets other than core.
*
* This method verifies that the fee is valid and sets the object pointer members and the fee fields. It should
* be called during do_evaluate.
*/
void prepare_fee(account_id_type account_id, asset fee);
/// Pays the fee and returns the number of CORE asset that were paid.
void pay_fee();
bool verify_authority( const account_object&, authority::classification );
//bool verify_signature( const key_object& );
bool verify_authority(const account_object&, authority::classification);
object_id_type get_relative_id( object_id_type rel_id )const;
object_id_type get_relative_id( object_id_type rel_id )const;
void verify_authority_accounts( const authority& a )const;
authority resolve_relative_ids( const authority& a )const;
asset fee_from_account;
share_type core_fee_paid;
const account_object* fee_paying_account = nullptr;
const account_statistics_object* fee_paying_account_statistics = nullptr;
const asset_object* fee_asset = nullptr;
const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;
transaction_evaluation_state* trx_state;
asset fee_from_account;
share_type core_fee_paid;
const account_object* fee_paying_account = nullptr;
const account_statistics_object* fee_paying_account_statistics = nullptr;
const asset_object* fee_asset = nullptr;
const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;
transaction_evaluation_state* trx_state;
};
class op_evaluator
{
public:
virtual ~op_evaluator(){}
virtual operation_result evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ) = 0;
public:
virtual ~op_evaluator(){}
virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply) = 0;
vector< evaluation_observer* > eval_observers;
vector<evaluation_observer*> eval_observers;
};
template<typename T>
class op_evaluator_impl : public op_evaluator
{
public:
virtual operation_result evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply = true ) override
public:
virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply = true) override
{
// fc::exception from observers are suppressed.
// fc::exception from evaluation is deferred (re-thrown
// after all observers receive evaluation_failed)
T eval;
shared_ptr<fc::exception> evaluation_exception;
size_t observer_count = 0;
operation_result result;
for( const auto& obs : eval_observers )
{
// fc::exception from observers are suppressed.
// fc::exception from evaluation is deferred (re-thrown
// after all observers receive evaluation_failed)
T eval;
optional< fc::exception > evaluation_exception;
size_t observer_count = 0;
operation_result result;
for( const auto& obs : eval_observers )
{
try
{
obs->pre_evaluate( eval_state, op, apply, &eval );
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer pre method:\n${e}", ( "e", e.to_detail_string() ) );
}
observer_count++;
}
try
{
result = eval.start_evaluate( eval_state, op, apply );
}
catch( const fc::exception& e )
{
evaluation_exception = e;
}
while( observer_count > 0 )
{
--observer_count;
const auto& obs = eval_observers[ observer_count ];
try
{
if( !evaluation_exception.valid() )
obs->post_evaluate( eval_state, op, apply, &eval, result );
else
obs->evaluation_failed( eval_state, op, apply, &eval, result );
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer post method:\n${e}", ( "e", e.to_detail_string() ) );
}
}
if( evaluation_exception.valid() )
throw *evaluation_exception;
return result;
try
{
obs->pre_evaluate(eval_state, op, apply, &eval);
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer pre method:\n${e}", ( "e", e.to_detail_string() ) );
}
observer_count++;
}
try
{
result = eval.start_evaluate(eval_state, op, apply);
}
catch( const fc::exception& e )
{
evaluation_exception = e.dynamic_copy_exception();
}
while( observer_count > 0 )
{
--observer_count;
const auto& obs = eval_observers[observer_count];
try
{
if( evaluation_exception )
obs->post_evaluate(eval_state, op, apply, &eval, result);
else
obs->evaluation_failed(eval_state, op, apply, &eval, result);
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer post method:\n${e}", ( "e", e.to_detail_string() ) );
}
}
if( evaluation_exception )
evaluation_exception->dynamic_rethrow_exception();
return result;
}
};
template<typename DerivedEvaluator>
class evaluator : public generic_evaluator
{
public:
virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; }
public:
virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; }
virtual operation_result evaluate( const operation& o ) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
virtual operation_result evaluate(const operation& o) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
prepare_fee(op.fee_payer(), op.fee);
FC_ASSERT( core_fee_paid >= op.calculate_fee(db().current_fee_schedule()) );
prepare_fee(op.fee_payer(), op.fee);
FC_ASSERT( core_fee_paid >= db().current_fee_schedule().calculate_fee( op ).amount,
"Insufficient Fee Paid",
("core_fee_paid",core_fee_paid)("required",db().current_fee_schedule().calculate_fee( op ).amount) );
return eval->do_evaluate( op );
}
virtual operation_result apply( const operation& o ) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
return eval->do_evaluate(op);
}
virtual operation_result apply(const operation& o) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
pay_fee();
pay_fee();
auto result = eval->do_apply( op );
auto result = eval->do_apply(op);
db().adjust_balance(op.fee_payer(), -fee_from_account);
db().adjust_balance(op.fee_payer(), -fee_from_account);
return result;
}
return result;
}
};
} }

View file

@ -18,96 +18,207 @@
#pragma once
#include <fc/exception/exception.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#define GRAPHENE_ASSERT( expr, exc_type, FORMAT, ... ) \
FC_MULTILINE_MACRO_BEGIN \
if( !(expr) ) \
FC_THROW_EXCEPTION( exc_type, FORMAT, __VA_ARGS__ ); \
FC_MULTILINE_MACRO_END
#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_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" );
FC_DECLARE_DERIVED_EXCEPTION( unsupported_chain_operation, graphene::chain::chain_exception, 30005, "unsupported chain operation" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction, graphene::chain::chain_exception, 30006, "unknown transaction" );
FC_DECLARE_DERIVED_EXCEPTION( duplicate_transaction, graphene::chain::chain_exception, 30007, "duplicate transaction" );
FC_DECLARE_DERIVED_EXCEPTION( zero_amount, graphene::chain::chain_exception, 30008, "zero amount" );
FC_DECLARE_DERIVED_EXCEPTION( zero_price, graphene::chain::chain_exception, 30009, "zero price" );
FC_DECLARE_DERIVED_EXCEPTION( asset_divide_by_self, graphene::chain::chain_exception, 30010, "asset divide by self" );
FC_DECLARE_DERIVED_EXCEPTION( asset_divide_by_zero, graphene::chain::chain_exception, 30011, "asset divide by zero" );
FC_DECLARE_DERIVED_EXCEPTION( new_database_version, graphene::chain::chain_exception, 30012, "new database version" );
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block, graphene::chain::chain_exception, 30013, "unlinkable block" );
FC_DECLARE_DERIVED_EXCEPTION( price_out_of_range, graphene::chain::chain_exception, 30014, "price out of range" );
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( block_numbers_not_sequential, graphene::chain::chain_exception, 30015, "block numbers not sequential" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_previous_block_id, graphene::chain::chain_exception, 30016, "invalid previous block" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_block_time, graphene::chain::chain_exception, 30017, "invalid block time" );
FC_DECLARE_DERIVED_EXCEPTION( time_in_past, graphene::chain::chain_exception, 30018, "time is in the past" );
FC_DECLARE_DERIVED_EXCEPTION( time_in_future, graphene::chain::chain_exception, 30019, "time is in the future" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_block_digest, graphene::chain::chain_exception, 30020, "invalid block digest" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_delegate_signee, graphene::chain::chain_exception, 30021, "invalid delegate signee" );
FC_DECLARE_DERIVED_EXCEPTION( failed_checkpoint_verification, graphene::chain::chain_exception, 30022, "failed checkpoint verification" );
FC_DECLARE_DERIVED_EXCEPTION( wrong_chain_id, graphene::chain::chain_exception, 30023, "wrong chain id" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_block, graphene::chain::chain_exception, 30024, "unknown block" );
FC_DECLARE_DERIVED_EXCEPTION( block_older_than_undo_history, graphene::chain::chain_exception, 30025, "block is older than our undo history allows us to process" );
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_EXCEPTION( evaluation_error, 31000, "Evaluation Error" );
FC_DECLARE_DERIVED_EXCEPTION( negative_deposit, graphene::chain::evaluation_error, 31001, "negative deposit" );
FC_DECLARE_DERIVED_EXCEPTION( not_a_delegate, graphene::chain::evaluation_error, 31002, "not a delegate" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_balance_record, graphene::chain::evaluation_error, 31003, "unknown balance record" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_funds, graphene::chain::evaluation_error, 31004, "insufficient funds" );
FC_DECLARE_DERIVED_EXCEPTION( missing_signature, graphene::chain::evaluation_error, 31005, "missing signature" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_claim_password, graphene::chain::evaluation_error, 31006, "invalid claim password" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_withdraw_condition, graphene::chain::evaluation_error, 31007, "invalid withdraw condition" );
FC_DECLARE_DERIVED_EXCEPTION( negative_withdraw, graphene::chain::evaluation_error, 31008, "negative withdraw" );
FC_DECLARE_DERIVED_EXCEPTION( not_an_active_delegate, graphene::chain::evaluation_error, 31009, "not an active delegate" );
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( invalid_pts_address, graphene::chain::utility_exception, 3060001, "invalid pts address" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, graphene::chain::chain_exception, 37006, "insufficient feeds" )
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" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_account_name, graphene::chain::evaluation_error, 32003, "unknown account name" );
FC_DECLARE_DERIVED_EXCEPTION( missing_parent_account_signature, graphene::chain::evaluation_error, 32004, "missing parent account signature" );
FC_DECLARE_DERIVED_EXCEPTION( parent_account_retracted, graphene::chain::evaluation_error, 32005, "parent account retracted" );
FC_DECLARE_DERIVED_EXCEPTION( account_expired, graphene::chain::evaluation_error, 32006, "account expired" );
FC_DECLARE_DERIVED_EXCEPTION( account_already_registered, graphene::chain::evaluation_error, 32007, "account already registered" );
FC_DECLARE_DERIVED_EXCEPTION( account_key_in_use, graphene::chain::evaluation_error, 32008, "account key already in use" );
FC_DECLARE_DERIVED_EXCEPTION( account_retracted, graphene::chain::evaluation_error, 32009, "account retracted" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_parent_account_name, graphene::chain::evaluation_error, 32010, "unknown parent account name" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_delegate_slate, graphene::chain::evaluation_error, 32011, "unknown delegate slate" );
FC_DECLARE_DERIVED_EXCEPTION( too_may_delegates_in_slate, graphene::chain::evaluation_error, 32012, "too many delegates in slate" );
FC_DECLARE_DERIVED_EXCEPTION( pay_balance_remaining, graphene::chain::evaluation_error, 32013, "pay balance remaining" );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer );
FC_DECLARE_DERIVED_EXCEPTION( not_a_delegate_signature, graphene::chain::evaluation_error, 33002, "not delegates signature" );
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" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_precision, graphene::chain::evaluation_error, 35001, "invalid precision" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_asset_symbol, graphene::chain::evaluation_error, 35002, "invalid asset symbol" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_asset_id, graphene::chain::evaluation_error, 35003, "unknown asset id" );
FC_DECLARE_DERIVED_EXCEPTION( asset_symbol_in_use, graphene::chain::evaluation_error, 35004, "asset symbol in use" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_asset_amount, graphene::chain::evaluation_error, 35005, "invalid asset amount" );
FC_DECLARE_DERIVED_EXCEPTION( negative_issue, graphene::chain::evaluation_error, 35006, "negative issue" );
FC_DECLARE_DERIVED_EXCEPTION( over_issue, graphene::chain::evaluation_error, 35007, "over issue" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_asset_symbol, graphene::chain::evaluation_error, 35008, "unknown asset symbol" );
FC_DECLARE_DERIVED_EXCEPTION( asset_id_in_use, graphene::chain::evaluation_error, 35009, "asset id in use" );
FC_DECLARE_DERIVED_EXCEPTION( not_user_issued, graphene::chain::evaluation_error, 35010, "not user issued" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_asset_name, graphene::chain::evaluation_error, 35011, "invalid asset name" );
//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( committee_member_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_withdraw_pay );
FC_DECLARE_DERIVED_EXCEPTION( delegate_vote_limit, graphene::chain::evaluation_error, 36001, "delegate_vote_limit" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_fee, graphene::chain::evaluation_error, 36002, "insufficient fee" );
FC_DECLARE_DERIVED_EXCEPTION( negative_fee, graphene::chain::evaluation_error, 36003, "negative fee" );
FC_DECLARE_DERIVED_EXCEPTION( missing_deposit, graphene::chain::evaluation_error, 36004, "missing deposit" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_relay_fee, graphene::chain::evaluation_error, 36005, "insufficient relay fee" );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_create );
FC_DECLARE_DERIVED_EXCEPTION( invalid_market, graphene::chain::evaluation_error, 37001, "invalid market" );
FC_DECLARE_DERIVED_EXCEPTION( unknown_market_order, graphene::chain::evaluation_error, 37002, "unknown market order" );
FC_DECLARE_DERIVED_EXCEPTION( shorting_base_shares, graphene::chain::evaluation_error, 37003, "shorting base shares" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_collateral, graphene::chain::evaluation_error, 37004, "insufficient collateral" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_depth, graphene::chain::evaluation_error, 37005, "insufficient depth" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, graphene::chain::evaluation_error, 37006, "insufficient feeds" );
FC_DECLARE_DERIVED_EXCEPTION( invalid_feed_price, graphene::chain::evaluation_error, 37007, "invalid feed price" );
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" )
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_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" )
FC_DECLARE_DERIVED_EXCEPTION( unsupported_chain_operation, graphene::chain::chain_exception, 30005, "unsupported chain operation" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction, graphene::chain::chain_exception, 30006, "unknown transaction" )
FC_DECLARE_DERIVED_EXCEPTION( duplicate_transaction, graphene::chain::chain_exception, 30007, "duplicate transaction" )
FC_DECLARE_DERIVED_EXCEPTION( zero_amount, graphene::chain::chain_exception, 30008, "zero amount" )
FC_DECLARE_DERIVED_EXCEPTION( zero_price, graphene::chain::chain_exception, 30009, "zero price" )
FC_DECLARE_DERIVED_EXCEPTION( asset_divide_by_self, graphene::chain::chain_exception, 30010, "asset divide by self" )
FC_DECLARE_DERIVED_EXCEPTION( asset_divide_by_zero, graphene::chain::chain_exception, 30011, "asset divide by zero" )
FC_DECLARE_DERIVED_EXCEPTION( new_database_version, graphene::chain::chain_exception, 30012, "new database version" )
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block, graphene::chain::chain_exception, 30013, "unlinkable block" )
FC_DECLARE_DERIVED_EXCEPTION( price_out_of_range, graphene::chain::chain_exception, 30014, "price out of range" )
FC_DECLARE_DERIVED_EXCEPTION( block_numbers_not_sequential, graphene::chain::chain_exception, 30015, "block numbers not sequential" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_previous_block_id, graphene::chain::chain_exception, 30016, "invalid previous block" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_block_time, graphene::chain::chain_exception, 30017, "invalid block time" )
FC_DECLARE_DERIVED_EXCEPTION( time_in_past, graphene::chain::chain_exception, 30018, "time is in the past" )
FC_DECLARE_DERIVED_EXCEPTION( time_in_future, graphene::chain::chain_exception, 30019, "time is in the future" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_block_digest, graphene::chain::chain_exception, 30020, "invalid block digest" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_committee_member_signee, graphene::chain::chain_exception, 30021, "invalid committee_member signee" )
FC_DECLARE_DERIVED_EXCEPTION( failed_checkpoint_verification, graphene::chain::chain_exception, 30022, "failed checkpoint verification" )
FC_DECLARE_DERIVED_EXCEPTION( wrong_chain_id, graphene::chain::chain_exception, 30023, "wrong chain id" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_block, graphene::chain::chain_exception, 30024, "unknown block" )
FC_DECLARE_DERIVED_EXCEPTION( block_older_than_undo_history, graphene::chain::chain_exception, 30025, "block is older than our undo history allows us to process" )
FC_DECLARE_EXCEPTION( evaluation_error, 31000, "Evaluation Error" )
FC_DECLARE_DERIVED_EXCEPTION( negative_deposit, graphene::chain::evaluation_error, 31001, "negative deposit" )
FC_DECLARE_DERIVED_EXCEPTION( not_a_committee_member, graphene::chain::evaluation_error, 31002, "not a committee_member" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_balance_record, graphene::chain::evaluation_error, 31003, "unknown balance record" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_funds, graphene::chain::evaluation_error, 31004, "insufficient funds" )
FC_DECLARE_DERIVED_EXCEPTION( missing_signature, graphene::chain::evaluation_error, 31005, "missing signature" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_claim_password, graphene::chain::evaluation_error, 31006, "invalid claim password" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_withdraw_condition, graphene::chain::evaluation_error, 31007, "invalid withdraw condition" )
FC_DECLARE_DERIVED_EXCEPTION( negative_withdraw, graphene::chain::evaluation_error, 31008, "negative withdraw" )
FC_DECLARE_DERIVED_EXCEPTION( not_an_active_committee_member, graphene::chain::evaluation_error, 31009, "not an active committee_member" )
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( 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" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_account_name, graphene::chain::evaluation_error, 32003, "unknown account name" )
FC_DECLARE_DERIVED_EXCEPTION( missing_parent_account_signature, graphene::chain::evaluation_error, 32004, "missing parent account signature" )
FC_DECLARE_DERIVED_EXCEPTION( parent_account_retracted, graphene::chain::evaluation_error, 32005, "parent account retracted" )
FC_DECLARE_DERIVED_EXCEPTION( account_expired, graphene::chain::evaluation_error, 32006, "account expired" )
FC_DECLARE_DERIVED_EXCEPTION( account_already_registered, graphene::chain::evaluation_error, 32007, "account already registered" )
FC_DECLARE_DERIVED_EXCEPTION( account_key_in_use, graphene::chain::evaluation_error, 32008, "account key already in use" )
FC_DECLARE_DERIVED_EXCEPTION( account_retracted, graphene::chain::evaluation_error, 32009, "account retracted" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_parent_account_name, graphene::chain::evaluation_error, 32010, "unknown parent account name" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_committee_member_slate, graphene::chain::evaluation_error, 32011, "unknown committee_member slate" )
FC_DECLARE_DERIVED_EXCEPTION( too_may_committee_members_in_slate, graphene::chain::evaluation_error, 32012, "too many committee_members in slate" )
FC_DECLARE_DERIVED_EXCEPTION( pay_balance_remaining, graphene::chain::evaluation_error, 32013, "pay balance remaining" )
FC_DECLARE_DERIVED_EXCEPTION( not_a_committee_member_signature, graphene::chain::evaluation_error, 33002, "not committee_members signature" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_precision, graphene::chain::evaluation_error, 35001, "invalid precision" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_asset_symbol, graphene::chain::evaluation_error, 35002, "invalid asset symbol" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_asset_id, graphene::chain::evaluation_error, 35003, "unknown asset id" )
FC_DECLARE_DERIVED_EXCEPTION( asset_symbol_in_use, graphene::chain::evaluation_error, 35004, "asset symbol in use" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_asset_amount, graphene::chain::evaluation_error, 35005, "invalid asset amount" )
FC_DECLARE_DERIVED_EXCEPTION( negative_issue, graphene::chain::evaluation_error, 35006, "negative issue" )
FC_DECLARE_DERIVED_EXCEPTION( over_issue, graphene::chain::evaluation_error, 35007, "over issue" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_asset_symbol, graphene::chain::evaluation_error, 35008, "unknown asset symbol" )
FC_DECLARE_DERIVED_EXCEPTION( asset_id_in_use, graphene::chain::evaluation_error, 35009, "asset id in use" )
FC_DECLARE_DERIVED_EXCEPTION( not_user_issued, graphene::chain::evaluation_error, 35010, "not user issued" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_asset_name, graphene::chain::evaluation_error, 35011, "invalid asset name" )
FC_DECLARE_DERIVED_EXCEPTION( committee_member_vote_limit, graphene::chain::evaluation_error, 36001, "committee_member_vote_limit" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_fee, graphene::chain::evaluation_error, 36002, "insufficient fee" )
FC_DECLARE_DERIVED_EXCEPTION( negative_fee, graphene::chain::evaluation_error, 36003, "negative fee" )
FC_DECLARE_DERIVED_EXCEPTION( missing_deposit, graphene::chain::evaluation_error, 36004, "missing deposit" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_relay_fee, graphene::chain::evaluation_error, 36005, "insufficient relay fee" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_market, graphene::chain::evaluation_error, 37001, "invalid market" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_market_order, graphene::chain::evaluation_error, 37002, "unknown market order" )
FC_DECLARE_DERIVED_EXCEPTION( shorting_base_shares, graphene::chain::evaluation_error, 37003, "shorting base shares" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_collateral, graphene::chain::evaluation_error, 37004, "insufficient collateral" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_depth, graphene::chain::evaluation_error, 37005, "insufficient depth" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, graphene::chain::evaluation_error, 37006, "insufficient feeds" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_feed_price, graphene::chain::evaluation_error, 37007, "invalid feed price" )
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

@ -16,8 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/block.hpp>
#include <graphene/chain/types.hpp>
#include <graphene/chain/protocol/block.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
@ -58,7 +57,7 @@ namespace graphene { namespace chain {
class fork_database
{
public:
typedef vector<item_ptr> branch_type;
typedef vector<item_ptr> branch_type;
fork_database();
void reset();
@ -69,7 +68,7 @@ namespace graphene { namespace chain {
bool is_known_block( const block_id_type& id )const;
shared_ptr<fork_item> fetch_block( const block_id_type& id )const;
vector<item_ptr> fetch_block_by_number( uint32_t n )const;
shared_ptr<fork_item> push_block( signed_block b );
shared_ptr<fork_item> push_block(const signed_block& b );
shared_ptr<fork_item> head()const { return _head; }
void pop_block();
@ -81,8 +80,8 @@ namespace graphene { namespace chain {
pair< branch_type, branch_type > fetch_branch_from( block_id_type first,
block_id_type second )const;
struct block_id{};
struct block_num{};
struct block_id;
struct block_num;
typedef multi_index_container<
item_ptr,
indexed_by<

View file

@ -0,0 +1,129 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <string>
#include <vector>
namespace graphene { namespace chain {
using std::string;
using std::vector;
struct genesis_state_type {
struct initial_account_type {
initial_account_type(const string& name = string(),
const public_key_type& owner_key = public_key_type(),
const public_key_type& active_key = public_key_type(),
bool is_lifetime_member = false)
: name(name),
owner_key(owner_key),
active_key(active_key == public_key_type()? owner_key : active_key),
is_lifetime_member(is_lifetime_member)
{}
string name;
public_key_type owner_key;
public_key_type active_key;
bool is_lifetime_member = false;
};
struct initial_asset_type {
string symbol;
string description;
uint8_t precision;
string issuer_name;
share_type max_supply;
uint16_t market_fee_percent;
share_type max_market_fee;
uint16_t issuer_permissions;
uint16_t flags;
struct initial_bitasset_options {
uint32_t feed_lifetime_sec;
uint8_t minimum_feeds;
uint32_t force_settlement_delay_sec;
uint16_t force_settlement_offset_percent;
uint16_t maximum_force_settlement_volume;
string backing_asset_symbol;
struct initial_collateral_position {
address owner;
share_type collateral;
share_type debt;
};
uint16_t maintenance_collateral_ratio;
vector<initial_collateral_position> collateral_records;
};
optional<initial_bitasset_options> bitasset_opts;
share_type initial_accumulated_fees;
};
struct initial_balance_type {
address owner;
string asset_symbol;
share_type amount;
};
struct initial_vesting_balance_type {
address owner;
string asset_symbol;
share_type amount;
time_point_sec begin_timestamp;
uint32_t vesting_duration_seconds = 0;
share_type begin_balance;
};
struct initial_witness_type {
/// Must correspond to one of the initial accounts
string owner_name;
public_key_type block_signing_key;
};
struct initial_committee_member_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;
};
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_asset_type,
(symbol)(description)(precision)(issuer_name)(max_supply)(market_fee_percent)
(issuer_permissions)(flags)(bitasset_opts)(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_timestamp)(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
(initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates)
(initial_committee_candidates)(initial_worker_candidates))

View file

@ -16,20 +16,21 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <fc/uint128.hpp>
#include <graphene/chain/protocol/chain_parameters.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/authority.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/db/object.hpp>
namespace graphene { namespace chain {
/**
* @class global_property_object
* @brief Maintains global state information (delegate list, current fees)
* @brief Maintains global state information (committee_member list, current fees)
* @ingroup object
* @ingroup implementation
*
* This is an implementation detail. The values here are set by delegates to tune the blockchain parameters.
* This is an implementation detail. The values here are set by committee_members to tune the blockchain parameters.
*/
class global_property_object : public graphene::db::abstract_object<global_property_object>
{
@ -41,7 +42,7 @@ namespace graphene { namespace chain {
optional<chain_parameters> pending_parameters;
uint32_t next_available_vote_id = 0;
vector<delegate_id_type> active_delegates; // updated once per maintenance interval
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
// n.b. witness scheduling is done by witness_schedule object
flat_set<account_id_type> witness_accounts; // updated once per maintenance interval
@ -55,7 +56,7 @@ namespace graphene { namespace chain {
/**
* @class dynamic_global_property_object
* @brief Maintains global state information (delegate list, current fees)
* @brief Maintains global state information (committee_member list, current fees)
* @ingroup object
* @ingroup implementation
*
@ -76,10 +77,17 @@ namespace graphene { namespace chain {
time_point_sec next_maintenance_time;
time_point_sec last_budget_time;
share_type witness_budget;
uint32_t accounts_registered_this_interval;
/** if the interval changes then how we calculate witness participation will
* also change. Normally witness participation is defined as % of blocks
* produced in the last round which is calculated by dividing the delta
* time between block N and N-NUM_WITNESSES by the block interval to calculate
* the number of blocks produced.
*/
uint32_t first_maintenance_block_with_current_interval = 0;
};
}}
FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object),
(random)
(head_block_number)
@ -88,13 +96,15 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
(current_witness)
(next_maintenance_time)
(witness_budget)
(accounts_registered_this_interval)
(first_maintenance_block_with_current_interval)
)
FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::object),
(parameters)
(pending_parameters)
(next_available_vote_id)
(active_delegates)
(active_committee_members)
(active_witnesses)
(chain_id)
)

View file

@ -1,52 +0,0 @@
/*
* 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 <graphene/chain/evaluator.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class limit_order_create_evaluator : public evaluator<limit_order_create_evaluator>
{
public:
typedef limit_order_create_operation operation_type;
object_id_type do_evaluate( const limit_order_create_operation& o );
object_id_type do_apply( const limit_order_create_operation& o );
asset calculate_market_fee( const asset_object* aobj, const asset& trade_amount );
const limit_order_create_operation* _op = nullptr;
const account_object* _seller = nullptr;
const asset_object* _sell_asset = nullptr;
const asset_object* _receive_asset = nullptr;
};
class limit_order_cancel_evaluator : public evaluator<limit_order_cancel_evaluator>
{
public:
typedef limit_order_cancel_operation operation_type;
asset do_evaluate( const limit_order_cancel_operation& o );
asset do_apply( const limit_order_cancel_operation& o );
const limit_order_object* _order;
};
} } // graphene::chain

View file

@ -1,78 +0,0 @@
/*
* 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 <graphene/db/object.hpp>
#include <graphene/chain/authority.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/db/generic_index.hpp>
#include <boost/multi_index/composite_key.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
/**
* @brief an offer to sell a amount of a asset at a specified exchange rate by a certain time
* @ingroup object
* @ingroup protocol
* @ingroup market
*
* This limit_order_objects are indexed by @ref expiration and is automatically deleted on the first block after expiration.
*/
class limit_order_object : public abstract_object<limit_order_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = limit_order_object_type;
time_point_sec expiration;
account_id_type seller;
share_type for_sale; ///< asset id is sell_price.base.asset_id
price sell_price;
asset amount_for_sale()const { return asset( for_sale, sell_price.base.asset_id ); }
asset amount_to_receive()const { return amount_for_sale() * sell_price; }
};
struct by_id;
struct by_price;
struct by_expiration;
typedef multi_index_container<
limit_order_object,
indexed_by<
hashed_unique< tag<by_id>,
member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_expiration>, member< limit_order_object, time_point_sec, &limit_order_object::expiration> >,
ordered_unique< tag<by_price>,
composite_key< limit_order_object,
member< limit_order_object, price, &limit_order_object::sell_price>,
member< object, object_id_type, &object::id>
>,
composite_key_compare< std::greater<price>, std::less<object_id_type> >
>
>
> limit_order_multi_index_type;
typedef generic_index<limit_order_object, limit_order_multi_index_type> limit_order_index;
} }
FC_REFLECT_DERIVED( graphene::chain::limit_order_object,
(graphene::db::object),
(expiration)(seller)(for_sale)(sell_price)
)

View file

@ -1,72 +1,57 @@
/*
* 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.
*/
/* Copyright (c) 2015, Cryptonomex, Inc. All rights reserved. */
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/types.hpp>
#include <graphene/chain/authority.hpp>
#include <graphene/chain/asset.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
using namespace graphene::db;
/**
* @class short_order_object
* @brief maintains state about requests to short an asset
* @brief an offer to sell a amount of a asset at a specified exchange rate by a certain time
* @ingroup object
* @ingroup protocol
* @ingroup market
*
* Short orders are only valid if their sell price is above the
* fair market value of the asset at the feed price. Users can
* place shorts at any price but their order will be ignored
* beyond the feed.
*
* All shorts have a minimial initial collateral ratio requirement that is
* defined by the network, but individuals may choose to have a higher
* initial collateral to avoid the risk of being margin called.
*
* All shorts have a maintenance collateral ratio that must be kept or
* the network will automatically cover the short order. Users can
* specify a higher maintenance collateral ratio as a form of "stop loss"
* and to potentially get ahead of a short squeeze.
* This limit_order_objects are indexed by @ref expiration and is automatically deleted on the first block after expiration.
*/
class short_order_object : public abstract_object<short_order_object>
class limit_order_object : public abstract_object<limit_order_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = short_order_object_type;
static const uint8_t type_id = limit_order_object_type;
time_point_sec expiration;
account_id_type seller;
share_type for_sale;
share_type available_collateral; ///< asset_id == sell_price.quote.asset_id
price sell_price; ///< the price the short is currently at = min(limit_price,feed)
price call_price; ///< the price that will be used to trigger margin calls after match, must be 1:1 if prediction market
uint16_t initial_collateral_ratio = 0; ///< may be higher than the network requires
uint16_t maintenance_collateral_ratio = 0; ///< may optionally be higher than the network requires
share_type for_sale; ///< asset id is sell_price.base.asset_id
price sell_price;
asset get_collateral()const { return asset( available_collateral, sell_price.quote.asset_id ); }
/** if the initial_collateral_ratio is 0, then this is a prediction market order which means the
* amount for sale depends upon price and available collateral.
*/
asset amount_for_sale()const { return asset( for_sale, sell_price.base.asset_id ); }
asset amount_to_receive()const { return amount_for_sale() * sell_price; }
};
struct by_id;
struct by_price;
struct by_expiration;
typedef multi_index_container<
limit_order_object,
indexed_by<
hashed_unique< tag<by_id>,
member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_expiration>, member< limit_order_object, time_point_sec, &limit_order_object::expiration> >,
ordered_unique< tag<by_price>,
composite_key< limit_order_object,
member< limit_order_object, price, &limit_order_object::sell_price>,
member< object, object_id_type, &object::id>
>,
composite_key_compare< std::greater<price>, std::less<object_id_type> >
>
>
> limit_order_multi_index_type;
typedef generic_index<limit_order_object, limit_order_multi_index_type> limit_order_index;
/**
* @class call_order_object
* @brief tracks debt and call price information
@ -86,13 +71,10 @@ namespace graphene { namespace chain {
asset_id_type debt_type()const { return call_price.quote.asset_id; }
price collateralization()const { return get_collateral() / get_debt(); }
void update_call_price() { call_price = price::call_price(get_debt(), get_collateral(), maintenance_collateral_ratio); }
account_id_type borrower;
share_type collateral; ///< call_price.base.asset_id, access via get_collateral
share_type debt; ///< call_price.quote.asset_id, access via get_collateral
price call_price;
uint16_t maintenance_collateral_ratio;
price call_price; ///< Debt / Collateral
};
/**
@ -115,27 +97,9 @@ namespace graphene { namespace chain {
{ return balance.asset_id; }
};
struct by_id;
struct by_price;
struct by_account;
struct by_expiration;
struct by_collateral;
typedef multi_index_container<
short_order_object,
indexed_by<
hashed_unique< tag<by_id>,
member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_expiration>, member< short_order_object, time_point_sec, &short_order_object::expiration> >,
ordered_unique< tag<by_price>,
composite_key< short_order_object,
member< short_order_object, price, &short_order_object::sell_price>,
member< object, object_id_type, &object::id>
>,
composite_key_compare< std::greater<price>, std::less<object_id_type> >
>
>
> short_order_multi_index_type;
struct by_collateral;
struct by_account;
struct by_price;
typedef multi_index_container<
call_order_object,
indexed_by<
@ -163,7 +127,6 @@ namespace graphene { namespace chain {
>
> call_order_multi_index_type;
struct by_account;
struct by_expiration;
typedef multi_index_container<
force_settlement_object,
@ -182,17 +145,62 @@ namespace graphene { namespace chain {
> force_settlement_object_multi_index_type;
typedef generic_index<short_order_object, short_order_multi_index_type> short_order_index;
typedef generic_index<call_order_object, call_order_multi_index_type> call_order_index;
typedef generic_index<force_settlement_object, force_settlement_object_multi_index_type> force_settlement_index;
class limit_order_create_evaluator : public evaluator<limit_order_create_evaluator>
{
public:
typedef limit_order_create_operation operation_type;
void_result do_evaluate( const limit_order_create_operation& o );
object_id_type do_apply( const limit_order_create_operation& o );
asset calculate_market_fee( const asset_object* aobj, const asset& trade_amount );
const limit_order_create_operation* _op = nullptr;
const account_object* _seller = nullptr;
const asset_object* _sell_asset = nullptr;
const asset_object* _receive_asset = nullptr;
};
class limit_order_cancel_evaluator : public evaluator<limit_order_cancel_evaluator>
{
public:
typedef limit_order_cancel_operation operation_type;
void_result do_evaluate( const limit_order_cancel_operation& o );
asset do_apply( const limit_order_cancel_operation& o );
const limit_order_object* _order;
};
class call_order_update_evaluator : public evaluator<call_order_update_evaluator>
{
public:
typedef call_order_update_operation operation_type;
void_result do_evaluate( const call_order_update_operation& o );
void_result do_apply( const call_order_update_operation& o );
bool _closing_order = false;
const asset_object* _debt_asset = nullptr;
const account_object* _paying_account = nullptr;
const call_order_object* _order = nullptr;
const asset_bitasset_data_object* _bitasset_data = nullptr;
};
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::short_order_object, (graphene::db::object),
(expiration)(seller)(for_sale)(available_collateral)(sell_price)
(call_price)(initial_collateral_ratio)(maintenance_collateral_ratio)
FC_REFLECT_DERIVED( graphene::chain::limit_order_object,
(graphene::db::object),
(expiration)(seller)(for_sale)(sell_price)
)
FC_REFLECT_DERIVED( graphene::chain::call_order_object, (graphene::db::object),
(borrower)(collateral)(debt)(call_price)(maintenance_collateral_ratio) )
(borrower)(collateral)(debt)(call_price) )
FC_REFLECT( graphene::chain::force_settlement_object, (owner)(balance)(settlement_date) )

View file

@ -16,30 +16,26 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/db/object.hpp>
namespace graphene { namespace chain {
class key_create_evaluator : public evaluator<key_create_evaluator>
/**
* @brief Contains per-node database configuration.
*
* Transactions are evaluated differently based on per-node state.
* Settings here may change based on whether the node is syncing or up-to-date.
* Or whether the node is a witness node. Or if we're processing a
* transaction in a witness-signed block vs. a fresh transaction
* from the p2p network. Or configuration-specified tradeoffs of
* performance/hardfork resilience vs. paranoia.
*/
class node_property_object
{
public:
typedef key_create_operation operation_type;
node_property_object() : skip_flags(0) {}
~node_property_object(){}
object_id_type do_evaluate( const key_create_operation& op )
{
return object_id_type();
}
object_id_type do_apply( const key_create_operation& op )
{
new_key_object = &db().create<key_object>( [&]( key_object& obj ){
obj.key_data = op.key_data;
});
return new_key_object->id;
}
const key_object* new_key_object = nullptr;
uint32_t skip_flags;
};
} } // graphene::chain

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/db/object.hpp>
namespace graphene { namespace chain {

File diff suppressed because it is too large Load diff

View file

@ -17,8 +17,8 @@
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
@ -29,7 +29,7 @@ namespace graphene { namespace chain {
public:
typedef proposal_create_operation operation_type;
object_id_type do_evaluate( const proposal_create_operation& o );
void_result do_evaluate( const proposal_create_operation& o );
object_id_type do_apply( const proposal_create_operation& o );
transaction _proposed_trx;

View file

@ -17,8 +17,7 @@
*/
#pragma once
#include <graphene/chain/types.hpp>
#include <graphene/chain/transaction.hpp>
#include <graphene/chain/protocol/transaction.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/db/generic_index.hpp>
@ -44,9 +43,33 @@ class proposal_object : public abstract_object<proposal_object>
flat_set<account_id_type> available_active_approvals;
flat_set<account_id_type> required_owner_approvals;
flat_set<account_id_type> available_owner_approvals;
flat_set<key_id_type> available_key_approvals;
flat_set<public_key_type> available_key_approvals;
bool is_authorized_to_execute(database* db)const;
bool is_authorized_to_execute(database& db)const;
};
/**
* @brief tracks all of the proposal objects that requrie approval of
* an individual account.
*
* @ingroup object
* @ingroup protocol
*
* This is a secondary index on the proposal_index
*
* @note the set of required approvals is constant
*/
class required_approval_index : public secondary_index
{
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_removed( const object& obj ) override;
virtual void about_to_modify( const object& before ) override{};
virtual void object_modified( const object& after ) override{};
void remove( account_id_type a, proposal_id_type p );
map<account_id_type, set<proposal_id_type> > _account_to_proposals;
};
struct by_expiration{};

View file

@ -0,0 +1,10 @@
Protocol Definition
--------------------
The classes declared in these headers provide the complete definition of the
Graphene protocol and are organized according to feature. Nothing in this
directory should depend upon anything other than fc or other types defined
in the protocol directory.
To be more specific, implementation details such as the objects defined in
the object database should not be required here.

View file

@ -0,0 +1,257 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
bool is_valid_name( const string& s );
bool is_cheap_name( const string& n );
/// These are the fields which can be updated by the active authority.
struct account_options
{
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
/// multiple keys in it.
public_key_type memo_key;
/// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake
/// will be counted as voting for the referenced account's selected votes instead.
account_id_type voting_account;
/// The number of active witnesses this account votes the blockchain should appoint
/// Must not exceed the actual number of witnesses voted for in @ref votes
uint16_t num_witness = 0;
/// The number of active committee members this account votes the blockchain should appoint
/// Must not exceed the actual number of committee members voted for in @ref votes
uint16_t num_committee = 0;
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
/// account's balance of core asset.
flat_set<vote_id_type> votes;
extensions_type extensions;
void validate()const;
};
/**
* @ingroup operations
*/
struct account_create_operation : public base_operation
{
struct fee_parameters_type {
uint64_t basic_fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint64_t premium_fee = 2000*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
/// This account pays the fee. Must be a lifetime member.
account_id_type registrar;
/// This account receives a portion of the fee split between registrar and referrer. Must be a member.
account_id_type referrer;
/// Of the fee split between registrar and referrer, this percentage goes to the referrer. The rest goes to the
/// registrar.
uint16_t referrer_percent = 0;
string name;
authority owner;
authority active;
account_options options;
extensions_type extensions;
account_id_type fee_payer()const { return registrar; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& )const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(registrar);
i.insert(referrer);
add_authority_accounts( i, owner );
add_authority_accounts( i, active );
}
};
/**
* @ingroup operations
* @brief Update an existing account
*
* This operation is used to update an existing account. It can be used to update the authorities, or adjust the options on the account.
* See @ref account_object::options_type for the options which may be updated.
*/
struct account_update_operation : public base_operation
{
struct fee_parameters_type {
share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
/// The account to update
account_id_type account;
/// New owner authority. If set, this operation requires owner authority to execute.
optional<authority> owner;
/// New active authority. If set, this operation requires owner authority to execute: TODO: why?
optional<authority> active;
/// New account options
optional<account_options> new_options;
extensions_type extensions;
account_id_type fee_payer()const { return account; }
void validate()const;
share_type calculate_fee( const fee_parameters_type& k )const;
void get_required_owner_authorities( flat_set<account_id_type>& a )const
{ if( owner || active ) a.insert( account ); }
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(account);
if( owner ) add_authority_accounts( i, *owner );
if( active ) add_authority_accounts( i, *active );
}
};
/**
* @brief This operation is used to whitelist and blacklist accounts, primarily for transacting in whitelisted assets
* @ingroup operations
*
* Accounts can freely specify opinions about other accounts, in the form of either whitelisting or blacklisting
* them. This information is used in chain validation only to determine whether an account is authorized to transact
* in an asset type which enforces a whitelist, but third parties can use this information for other uses as well,
* as long as it does not conflict with the use of whitelisted assets.
*
* An asset which enforces a whitelist specifies a list of accounts to maintain its whitelist, and a list of
* accounts to maintain its blacklist. In order for a given account A to hold and transact in a whitelisted asset S,
* A must be whitelisted by at least one of S's whitelist_authorities and blacklisted by none of S's
* blacklist_authorities. If A receives a balance of S, and is later removed from the whitelist(s) which allowed it
* to hold S, or added to any blacklist S specifies as authoritative, A's balance of S will be frozen until A's
* authorization is reinstated.
*
* This operation requires authorizing_account's signature, but not account_to_list's. The fee is paid by
* authorizing_account.
*/
struct account_whitelist_operation : public base_operation
{
struct fee_parameters_type { share_type fee = 300000; };
enum account_listing {
no_listing = 0x0, ///< No opinion is specified about this account
white_listed = 0x1, ///< This account is whitelisted, but not blacklisted
black_listed = 0x2, ///< This account is blacklisted, but not whitelisted
white_and_black_listed = white_listed | black_listed ///< This account is both whitelisted and blacklisted
};
/// Paid by authorizing_account
asset fee;
/// The account which is specifying an opinion of another account
account_id_type authorizing_account;
/// The account being opined about
account_id_type account_to_list;
/// The new white and blacklist status of account_to_list, as determined by authorizing_account
/// This is a bitfield using values defined in the account_listing enum
uint8_t new_listing;
extensions_type extensions;
account_id_type fee_payer()const { return authorizing_account; }
void validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT(new_listing < 0x4); }
void get_impacted_accounts( flat_set<account_id_type>& i )const
{ i.insert(account_to_list); }
};
/**
* @brief Manage an account's membership status
* @ingroup operations
*
* This operation is used to upgrade an account to a member, or renew its subscription. If an account which is an
* unexpired annual subscription member publishes this operation with @ref upgrade_to_lifetime_member set to false,
* the account's membership expiration date will be pushed backward one year. If a basic account publishes it with
* @ref upgrade_to_lifetime_member set to false, the account will be upgraded to a subscription member with an
* expiration date one year after the processing time of this operation.
*
* Any account may use this operation to become a lifetime member by setting @ref upgrade_to_lifetime_member to
* true. Once an account has become a lifetime member, it may not use this operation anymore.
*/
struct account_upgrade_operation : public base_operation
{
struct fee_parameters_type {
uint64_t membership_annual_fee = 2000 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint64_t membership_lifetime_fee = 10000 * GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to upgrade to a lifetime member
};
asset fee;
/// The account to upgrade; must not already be a lifetime member
account_id_type account_to_upgrade;
/// If true, the account will be upgraded to a lifetime member; otherwise, it will add a year to the subscription
bool upgrade_to_lifetime_member = false;
extensions_type extensions;
account_id_type fee_payer()const { return account_to_upgrade; }
void validate()const;
share_type calculate_fee( const fee_parameters_type& k )const;
};
/**
* @brief transfers the account to another account while clearing the white list
* @ingroup operations
*
* In theory an account can be transferred by simply updating the authorities, but that kind
* of transfer lacks semantic meaning and is more often done to rotate keys without transferring
* ownership. This operation is used to indicate the legal transfer of title to this account and
* a break in the operation history.
*
* The account_id's owner/active/voting/memo authority should be set to new_owner
*
* This operation will clear the account's whitelist statuses, but not the blacklist statuses.
*/
struct account_transfer_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type account_id;
account_id_type new_owner;
extensions_type extensions;
account_id_type fee_payer()const { return account_id; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(new_owner);
}
};
} }
FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
FC_REFLECT( graphene::chain::account_create_operation,
(fee)(registrar)
(referrer)(referrer_percent)
(name)(owner)(active)(options)(extensions)
)
FC_REFLECT( graphene::chain::account_update_operation,
(fee)(account)(owner)(active)(new_options)(extensions)
)
FC_REFLECT( graphene::chain::account_upgrade_operation,
(fee)(account_to_upgrade)(upgrade_to_lifetime_member)(extensions) )
FC_REFLECT( graphene::chain::account_whitelist_operation, (fee)(authorizing_account)(account_to_list)(new_listing)(extensions))
FC_REFLECT( graphene::chain::account_create_operation::fee_parameters_type, (basic_fee)(premium_fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::account_whitelist_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::account_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::account_upgrade_operation::fee_parameters_type, (membership_annual_fee)(membership_lifetime_fee) )
FC_REFLECT( graphene::chain::account_transfer_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) )

View file

@ -56,6 +56,7 @@ namespace graphene { namespace chain {
explicit operator std::string()const; ///< converts to base58 + checksum
friend size_t hash_value( const address& v ) { return *((size_t*)&v.addr._hash[2]); }
fc::ripemd160 addr;
};
inline bool operator == ( const address& a, const address& b ) { return a.addr == b.addr; }

View file

@ -0,0 +1,75 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* Used to verify that account_id->name is equal to the given string literal.
*/
struct account_name_eq_lit_predicate
{
account_id_type account_id;
string name;
/**
* Perform state-independent checks. Verify
* account_name is a valid account name.
*/
bool validate()const;
};
/**
* Used to verify that asset_id->symbol is equal to the given string literal.
*/
struct asset_symbol_eq_lit_predicate
{
asset_id_type asset_id;
string symbol;
/**
* Perform state independent checks. Verify symbol is a
* valid asset symbol.
*/
bool validate()const;
};
/**
* When defining predicates do not make the protocol dependent upon
* implementation details.
*/
typedef static_variant<
account_name_eq_lit_predicate,
asset_symbol_eq_lit_predicate
> predicate;
/**
* @brief assert that some conditions are true.
* @ingroup operations
*
* This operation performs no changes to the database state, but can but used to verify
* pre or post conditions for other operations.
*/
struct assert_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type fee_paying_account;
vector<predicate> predicates;
flat_set<account_id_type> required_auths;
extensions_type extensions;
account_id_type fee_payer()const { return fee_paying_account; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) )
FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) )
FC_REFLECT_TYPENAME( graphene::chain::predicate )
FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) )

View file

@ -16,8 +16,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/types.hpp>
#include <graphene/chain/config.hpp>
#include <graphene/chain/protocol/config.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
@ -42,20 +42,34 @@ namespace graphene { namespace chain {
return *this;
}
asset operator -()const { return asset( -amount, asset_id ); }
friend bool operator == ( const asset& a, const asset& b )
{
return tie(a.asset_id,a.amount) == tie(b.asset_id,b.amount);
return std::tie( a.asset_id, a.amount ) == std::tie( b.asset_id, b.amount );
}
friend bool operator >= ( const asset& a, const asset& b )
friend bool operator < ( const asset& a, const asset& b )
{
FC_ASSERT( a.asset_id == b.asset_id );
return a.amount >= b.amount;
return a.amount < b.amount;
}
friend bool operator <= ( const asset& a, const asset& b )
{
return (a == b) || (a < b);
}
friend bool operator != ( const asset& a, const asset& b )
{
return !(a == b);
}
friend bool operator > ( const asset& a, const asset& b )
{
FC_ASSERT( a.asset_id == b.asset_id );
return a.amount > b.amount;
return !(a <= b);
}
friend bool operator >= ( const asset& a, const asset& b )
{
return !(a < b);
}
friend asset operator - ( const asset& a, const asset& b )
{
FC_ASSERT( a.asset_id == b.asset_id );
@ -66,7 +80,6 @@ namespace graphene { namespace chain {
FC_ASSERT( a.asset_id == b.asset_id );
return asset( a.amount + b.amount, a.asset_id );
}
};
/**
@ -121,47 +134,14 @@ namespace graphene { namespace chain {
/**
* @class price_feed
* @brief defines market parameters for shorts and margin positions
* @brief defines market parameters for margin positions
*/
struct price_feed
{
/**
* This is the lowest price at which margin positions will be forced to sell their collateral. This does not
* directly affect the price at which margin positions will be called; it is only a safety to prevent calls at
* unreasonable prices.
*/
price call_limit;
/**
* Short orders will only be matched against bids above this price.
*/
price short_limit;
/**
* Forced settlements will evaluate using this price.
*/
price settlement_price;
/**
* Maximum number of seconds margin positions should be able to remain open.
*/
uint32_t max_margin_period_sec = GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC;
/**
* Required maintenance collateral is defined
* as a fixed point number with a maximum value of 10.000
* and a minimum value of 1.000.
*
* This value must be greater than required_maintenance_collateral or
* a margin call would be triggered immediately.
*
* Default requirement is $2 of collateral per $1 of debt based
* upon the premise that both parties to every trade should bring
* equal value to the table.
*/
uint16_t required_initial_collateral = GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO;
/**
* Required maintenance collateral is defined
* as a fixed point number with a maximum value of 10.000
* and a minimum value of 1.000.
* and a minimum value of 1.000. (denominated in GRAPHENE_COLLATERAL_RATIO_DENOM)
*
* A black swan event occurs when value_of_collateral equals
* value_of_debt, to avoid a black swan a margin call is
@ -169,19 +149,46 @@ namespace graphene { namespace chain {
* equals value_of_collateral using rate.
*
* Default requirement is $1.75 of collateral per $1 of debt
*
* BlackSwan ---> SQR ---> MCR ----> SP
*/
uint16_t required_maintenance_collateral = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;
///@{
/**
* Forced settlements will evaluate using this price, defined as BITASSET / COLLATERAL
*/
price settlement_price;
friend bool operator < ( const price_feed& a, const price_feed& b )
{
return std::tie( a.call_limit.base.asset_id, a.call_limit.quote.asset_id ) <
std::tie( b.call_limit.base.asset_id, b.call_limit.quote.asset_id );
}
/// Price at which automatically exchanging this asset for CORE from fee pool occurs (used for paying fees)
price core_exchange_rate;
/** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */
uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;
/** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */
uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;
/**
* When updating a call order the following condition must be maintained:
*
* debt * maintenance_price() < collateral
* debt * settlement_price < debt * maintenance
* debt * maintenance_price() < debt * max_short_squeeze_price()
price maintenance_price()const;
*/
/** When selling collateral to pay off debt, the least amount of debt to receive should be
* min_usd = max_short_squeeze_price() * collateral
*
* This is provided to ensure that a black swan cannot be trigged due to poor liquidity alone, it
* must be confirmed by having the max_short_squeeze_price() move below the black swan price.
*/
price max_short_squeeze_price()const;
///@}
friend bool operator == ( const price_feed& a, const price_feed& b )
{
return std::tie( a.call_limit.base.asset_id, a.call_limit.quote.asset_id ) ==
std::tie( b.call_limit.base.asset_id, b.call_limit.quote.asset_id );
return std::tie( a.settlement_price, a.maintenance_collateral_ratio, a.maximum_short_squeeze_ratio ) ==
std::tie( b.settlement_price, b.maintenance_collateral_ratio, b.maximum_short_squeeze_ratio );
}
void validate() const;
@ -191,6 +198,9 @@ namespace graphene { namespace chain {
FC_REFLECT( graphene::chain::asset, (amount)(asset_id) )
FC_REFLECT( graphene::chain::price, (base)(quote) )
#define GRAPHENE_PRICE_FEED_FIELDS (call_limit)(short_limit)(settlement_price)(max_margin_period_sec)\
(required_initial_collateral)(required_maintenance_collateral)
#define GRAPHENE_PRICE_FEED_FIELDS (settlement_price)(maintenance_collateral_ratio)(maximum_short_squeeze_ratio) \
(core_exchange_rate)
FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS )

View file

@ -0,0 +1,461 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>
namespace graphene { namespace chain {
bool is_valid_symbol( const string& symbol );
/**
* @brief The asset_options struct contains options available on all assets in the network
*
* @note Changes to this struct will break protocol compatibility
*/
struct asset_options {
/// The maximum supply of this asset which may exist at any given time. This can be as large as
/// GRAPHENE_MAX_SHARE_SUPPLY
share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
/// When this asset is traded on the markets, this percentage of the total traded will be exacted and paid
/// to the issuer. This is a fixed point value, representing hundredths of a percent, i.e. a value of 100
/// in this field means a 1% fee is charged on market trades of this asset.
uint16_t market_fee_percent = 0;
/// Market fees calculated as @ref market_fee_percent of the traded volume are capped to this value
share_type max_market_fee = GRAPHENE_MAX_SHARE_SUPPLY;
/// The flags which the issuer has permission to update. See @ref asset_issuer_permission_flags
uint16_t issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
/// The currently active flags on this permission. See @ref asset_issuer_permission_flags
uint16_t flags = 0;
/// When a non-core asset is used to pay a fee, the blockchain must convert that asset to core asset in
/// order to accept the fee. If this asset's fee pool is funded, the chain will automatically deposite fees
/// in this asset to its accumulated fees, and withdraw from the fee pool the same amount as converted at
/// the core exchange rate.
price core_exchange_rate;
/// A set of accounts which maintain whitelists to consult for this asset. If enforce_white_list() returns
/// true, an account may only send, receive, trade, etc. in this asset if one of these accounts appears in
/// its account_object::whitelisting_accounts field.
flat_set<account_id_type> whitelist_authorities;
/// A set of accounts which maintain blacklists to consult for this asset. If enforce_white_list() returns
/// true, an account may only send, receive, trade, etc. in this asset if none of these accounts appears in
/// its account_object::blacklisting_accounts field. If the account is blacklisted, it may not transact in
/// this asset even if it is also whitelisted.
flat_set<account_id_type> blacklist_authorities;
/** defines the assets that this asset may be traded against in the market */
flat_set<asset_id_type> whitelist_markets;
/** defines the assets that this asset may not be traded against in the market, must not overlap whitelist */
flat_set<asset_id_type> blacklist_markets;
/**
* data that describes the meaning/purpose of this asset, fee will be charged proportional to
* size of description.
*/
string description;
extensions_type extensions;
/// Perform internal consistency checks.
/// @throws fc::exception if any check fails
void validate()const;
};
/**
* @brief The bitasset_options struct contains configurable options available only to BitAssets.
*
* @note Changes to this struct will break protocol compatibility
*/
struct bitasset_options {
/// Time before a price feed expires
uint32_t feed_lifetime_sec = GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME;
/// Minimum number of unexpired feeds required to extract a median feed from
uint8_t minimum_feeds = 1;
/// This is the delay between the time a long requests settlement and the chain evaluates the settlement
uint32_t force_settlement_delay_sec = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY;
/// This is the percent to adjust the feed price in the short's favor in the event of a forced settlement
uint16_t force_settlement_offset_percent = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET;
/// Force settlement volume can be limited such that only a certain percentage of the total existing supply
/// of the asset may be force-settled within any given chain maintenance interval. This field stores the
/// percentage of the current supply which may be force settled within the current maintenance interval. If
/// force settlements come due in an interval in which the maximum volume has already been settled, the new
/// settlements will be enqueued and processed at the beginning of the next maintenance interval.
uint16_t maximum_force_settlement_volume = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME;
/// This speicifies which asset type is used to collateralize short sales
/// This field may only be updated if the current supply of the asset is zero.
asset_id_type short_backing_asset;
extensions_type extensions;
/// Perform internal consistency checks.
/// @throws fc::exception if any check fails
void validate()const;
};
/**
* @ingroup operations
*/
struct asset_create_operation : public base_operation
{
struct fee_parameters_type {
uint64_t symbol3 = 500000 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint64_t symbol4 = 300000 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint64_t long_symbol = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10; /// only required for large memos.
};
asset fee;
/// This account must sign and pay the fee for this operation. Later, this account may update the asset
account_id_type issuer;
/// The ticker symbol of this asset
string symbol;
/// Number of digits to the right of decimal point, must be less than or equal to 12
uint8_t precision = 0;
/// Options common to all assets.
///
/// @note common_options.core_exchange_rate technically needs to store the asset ID of this new asset. Since this
/// ID is not known at the time this operation is created, create this price as though the new asset has instance
/// ID 1, and the chain will overwrite it with the new asset's ID.
asset_options common_options;
/// Options only available for BitAssets. MUST be non-null if and only if the @ref market_issued flag is set in
/// common_options.flags
optional<bitasset_options> bitasset_opts;
/// For BitAssets, set this to true if the asset implements a @ref prediction_market; false otherwise
bool is_prediction_market = false;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee( const fee_parameters_type& k )const;
};
/**
* @brief allows global settling of bitassets (black swan or prediction markets)
*
* In order to use this operation, @ref asset_to_settle must have the global_settle flag set
*
* When this operation is executed all balances are converted into the backing asset at the
* settle_price and all open margin positions are called at the settle price. If this asset is
* used as backing for other bitassets, those bitassets will be force settled at their current
* feed price.
*/
struct asset_global_settle_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type issuer; ///< must equal @ref asset_to_settle->issuer
asset_id_type asset_to_settle;
price settle_price;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
};
/**
* @brief Schedules a market-issued asset for automatic settlement
* @ingroup operations
*
* Holders of market-issued assests may request a forced settlement for some amount of their asset. This means that
* the specified sum will be locked by the chain and held for the settlement period, after which time the chain will
* choose a margin posision holder and buy the settled asset using the margin's collateral. The price of this sale
* will be based on the feed price for the market-issued asset being settled. The exact settlement price will be the
* feed price at the time of settlement with an offset in favor of the margin position, where the offset is a
* blockchain parameter set in the global_property_object.
*
* The fee is paid by @ref account, and @ref account must authorize this operation
*/
struct asset_settle_operation : public base_operation
{
struct fee_parameters_type {
/** this fee should be high to encourage small settlement requests to
* be performed on the market rather than via forced settlement.
*
* Note that in the event of a black swan or prediction market close out
* everyone will have to pay this fee.
*/
uint64_t fee = 100 * GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
/// Account requesting the force settlement. This account pays the fee
account_id_type account;
/// Amount of asset to force settle. This must be a market-issued asset
asset amount;
extensions_type extensions;
account_id_type fee_payer()const { return account; }
void validate()const;
};
/**
* @ingroup operations
*/
struct asset_fund_fee_pool_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee; ///< core asset
account_id_type from_account;
asset_id_type asset_id;
share_type amount; ///< core asset
extensions_type extensions;
account_id_type fee_payer()const { return from_account; }
void validate()const;
};
/**
* @brief Update options common to all assets
* @ingroup operations
*
* There are a number of options which all assets in the network use. These options are enumerated in the @ref
* asset_options struct. This operation is used to update these options for an existing asset.
*
* @note This operation cannot be used to update BitAsset-specific options. For these options, use @ref
* asset_update_bitasset_operation instead.
*
* @pre @ref issuer SHALL be an existing account and MUST match asset_object::issuer on @ref asset_to_update
* @pre @ref fee SHALL be nonnegative, and @ref issuer MUST have a sufficient balance to pay it
* @pre @ref new_options SHALL be internally consistent, as verified by @ref validate()
* @post @ref asset_to_update will have options matching those of new_options
*/
struct asset_update_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10;
};
asset_update_operation(){}
asset fee;
account_id_type issuer;
asset_id_type asset_to_update;
/// If the asset is to be given a new issuer, specify his ID here.
optional<account_id_type> new_issuer;
asset_options new_options;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ if( new_issuer ) i.insert( *new_issuer ); }
};
/**
* @brief Update options specific to BitAssets
* @ingroup operations
*
* BitAssets have some options which are not relevant to other asset types. This operation is used to update those
* options an an existing BitAsset.
*
* @pre @ref issuer MUST be an existing account and MUST match asset_object::issuer on @ref asset_to_update
* @pre @ref asset_to_update MUST be a BitAsset, i.e. @ref asset_object::is_market_issued() returns true
* @pre @ref fee MUST be nonnegative, and @ref issuer MUST have a sufficient balance to pay it
* @pre @ref new_options SHALL be internally consistent, as verified by @ref validate()
* @post @ref asset_to_update will have BitAsset-specific options matching those of new_options
*/
struct asset_update_bitasset_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type issuer;
asset_id_type asset_to_update;
bitasset_options new_options;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
};
/**
* @brief Update the set of feed-producing accounts for a BitAsset
* @ingroup operations
*
* BitAssets have price feeds selected by taking the median values of recommendations from a set of feed producers.
* This operation is used to specify which accounts may produce feeds for a given BitAsset.
*
* @pre @ref issuer MUST be an existing account, and MUST match asset_object::issuer on @ref asset_to_update
* @pre @ref issuer MUST NOT be the committee account
* @pre @ref asset_to_update MUST be a BitAsset, i.e. @ref asset_object::is_market_issued() returns true
* @pre @ref fee MUST be nonnegative, and @ref issuer MUST have a sufficient balance to pay it
* @pre Cardinality of @ref new_feed_producers MUST NOT exceed @ref chain_parameters::maximum_asset_feed_publishers
* @post @ref asset_to_update will have a set of feed producers matching @ref new_feed_producers
* @post All valid feeds supplied by feed producers in @ref new_feed_producers, which were already feed producers
* prior to execution of this operation, will be preserved
*/
struct asset_update_feed_producers_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type issuer;
asset_id_type asset_to_update;
flat_set<account_id_type> new_feed_producers;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
};
/**
* @brief Publish price feeds for market-issued assets
* @ingroup operations
*
* Price feed providers use this operation to publish their price feeds for market-issued assets. A price feed is
* used to tune the market for a particular market-issued asset. For each value in the feed, the median across all
* committee_member feeds for that asset is calculated and the market for the asset is configured with the median of that
* value.
*
* The feed in the operation contains three prices: a call price limit, a short price limit, and a settlement price.
* The call limit price is structured as (collateral asset) / (debt asset) and the short limit price is structured
* as (asset for sale) / (collateral asset). Note that the asset IDs are opposite to eachother, so if we're
* publishing a feed for USD, the call limit price will be CORE/USD and the short limit price will be USD/CORE. The
* settlement price may be flipped either direction, as long as it is a ratio between the market-issued asset and
* its collateral.
*/
struct asset_publish_feed_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee; ///< paid for by publisher
account_id_type publisher;
asset_id_type asset_id; ///< asset for which the feed is published
price_feed feed;
extensions_type extensions;
account_id_type fee_payer()const { return publisher; }
void validate()const;
};
/**
* @ingroup operations
*/
struct asset_issue_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type issuer; ///< Must be asset_to_issue->asset_id->issuer
asset asset_to_issue;
account_id_type issue_to_account;
/** user provided data encrypted to the memo key of the "to" account */
optional<memo_data> memo;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( issue_to_account ); }
};
/**
* @brief used to take an asset out of circulation, returning to the issuer
* @ingroup operations
*
* @note You cannot burn market-issued assets.
*/
struct asset_reserve_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type payer;
asset amount_to_reserve;
extensions_type extensions;
account_id_type fee_payer()const { return payer; }
void validate()const;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::asset_options,
(max_supply)
(market_fee_percent)
(max_market_fee)
(issuer_permissions)
(flags)
(core_exchange_rate)
(whitelist_authorities)
(blacklist_authorities)
(whitelist_markets)
(blacklist_markets)
(description)
(extensions)
)
FC_REFLECT( graphene::chain::bitasset_options,
(feed_lifetime_sec)
(minimum_feeds)
(force_settlement_delay_sec)
(force_settlement_offset_percent)
(maximum_force_settlement_volume)
(short_backing_asset)
(extensions)
)
FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )
FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::asset_update_bitasset_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_update_feed_producers_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_publish_feed_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_issue_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::asset_reserve_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_create_operation,
(fee)
(issuer)
(symbol)
(precision)
(common_options)
(bitasset_opts)
(is_prediction_market)
(extensions)
)
FC_REFLECT( graphene::chain::asset_update_operation,
(fee)
(issuer)
(asset_to_update)
(new_issuer)
(new_options)
(extensions)
)
FC_REFLECT( graphene::chain::asset_update_bitasset_operation,
(fee)
(issuer)
(asset_to_update)
(new_options)
(extensions)
)
FC_REFLECT( graphene::chain::asset_update_feed_producers_operation,
(fee)(issuer)(asset_to_update)(new_feed_producers)(extensions)
)
FC_REFLECT( graphene::chain::asset_publish_feed_operation,
(fee)(publisher)(asset_id)(feed)(extensions) )
FC_REFLECT( graphene::chain::asset_settle_operation, (fee)(account)(amount)(extensions) )
FC_REFLECT( graphene::chain::asset_global_settle_operation, (fee)(issuer)(asset_to_settle)(settle_price)(extensions) )
FC_REFLECT( graphene::chain::asset_issue_operation,
(fee)(issuer)(asset_to_issue)(issue_to_account)(memo)(extensions) )
FC_REFLECT( graphene::chain::asset_reserve_operation,
(fee)(payer)(amount_to_reserve)(extensions) )
FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation, (fee)(from_account)(asset_id)(amount)(extensions) );

View file

@ -16,8 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <fc/io/varint.hpp>
#include <graphene/chain/types.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
@ -43,17 +42,17 @@ namespace graphene { namespace chain {
active = 1,
key = 2
};
void add_authority( key_id_type k, weight_type w )
void add_authority( const public_key_type& k, weight_type w )
{
auths[k] = w;
key_auths[k] = w;
}
void add_authority( const address& k, weight_type w )
{
address_auths[k] = w;
}
void add_authority( account_id_type k, weight_type w )
{
auths[k] = w;
}
void add_authority( relative_key_id_type k, weight_type w )
{
auths[k] = w;
account_auths[k] = w;
}
template<typename AuthType>
@ -68,24 +67,26 @@ namespace graphene { namespace chain {
add_authorities(auths...);
}
vector<key_id_type> get_keys() const
vector<public_key_type> get_keys() const
{
vector<key_id_type> result;
result.reserve( auths.size() );
for( const pair<object_id_type, weight_type>& item : auths )
{
if( item.first.type() == key_object_type )
result.push_back( item.first );
}
vector<public_key_type> result;
result.reserve( key_auths.size() );
for( const auto& k : key_auths )
result.push_back(k.first);
return result;
}
uint32_t num_auths()const { return account_auths.size() + key_auths.size(); }
void clear() { account_auths.clear(); key_auths.clear(); }
uint32_t weight_threshold = 0;
flat_map<object_id_type,weight_type> auths;
uint32_t weight_threshold = 0;
flat_map<account_id_type,weight_type> account_auths;
flat_map<public_key_type,weight_type> key_auths;
/** needed for backward compatibility only */
flat_map<address,weight_type> address_auths;
};
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::authority, (weight_threshold)(auths) )
FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) )
FC_REFLECT_TYPENAME( graphene::chain::authority::classification )
FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) )

View file

@ -0,0 +1,36 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* @brief Claim a balance in a @ref balanc_object
*
* This operation is used to claim the balance in a given @ref balance_object. If the balance object contains a
* vesting balance, @ref total_claimed must not exceed @ref balance_object::available at the time of evaluation. If
* the object contains a non-vesting balance, @ref total_claimed must be the full balance of the object.
*/
struct balance_claim_operation : public base_operation
{
struct fee_parameters_type {};
asset fee;
account_id_type deposit_to_account;
balance_id_type balance_to_claim;
public_key_type balance_owner_key;
asset total_claimed;
account_id_type fee_payer()const { return deposit_to_account; }
share_type calculate_fee(const fee_parameters_type& )const { return 0; }
void validate()const;
void get_required_authorities( vector<authority>& a )const
{
a.push_back( authority( 1, balance_owner_key, 1 ) );
}
};
}} // graphene::chain
FC_REFLECT( graphene::chain::balance_claim_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::balance_claim_operation,
(fee)(deposit_to_account)(balance_to_claim)(balance_owner_key)(total_claimed) )

View file

@ -0,0 +1,105 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/asset.hpp>
#include <graphene/chain/protocol/authority.hpp>
namespace graphene { namespace chain {
/**
* @defgroup operations Operations
* @ingroup transactions Transactions
* @brief A set of valid comands for mutating the globally shared state.
*
* An operation can be thought of like a function that will modify the global
* shared state of the blockchain. The members of each struct are like function
* arguments and each operation can potentially generate a return value.
*
* Operations can be grouped into transactions (@ref transaction) to ensure that they occur
* in a particular order and that all operations apply successfully or
* no operations apply.
*
* Each operation is a fully defined state transition and can exist in a transaction on its own.
*
* @section operation_design_principles Design Principles
*
* Operations have been carefully designed to include all of the information necessary to
* interpret them outside the context of the blockchain. This means that information about
* current chain state is included in the operation even though it could be inferred from
* a subset of the data. This makes the expected outcome of each operation well defined and
* easily understood without access to chain state.
*
* @subsection balance_calculation Balance Calculation Principle
*
* We have stipulated that the current account balance may be entirely calculated from
* just the subset of operations that are relevant to that account. There should be
* no need to process the entire blockchain inorder to know your account's balance.
*
* @subsection fee_calculation Explicit Fee Principle
*
* Blockchain fees can change from time to time and it is important that a signed
* transaction explicitly agree to the fees it will be paying. This aids with account
* balance updates and ensures that the sender agreed to the fee prior to making the
* transaction.
*
* @subsection defined_authority Explicit Authority
*
* Each operation shall contain enough information to know which accounts must authorize
* the operation. This principle enables authority verification to occur in a centralized,
* optimized, and parallel manner.
*
* @subsection relevancy_principle Explicit Relevant Accounts
*
* Each operation contains enough information to enumerate all accounts for which the
* operation should apear in its account history. This principle enables us to easily
* define and enforce the @balance_calculation. This is superset of the @ref defined_authority
*
* @{
*/
struct void_result{};
typedef fc::static_variant<void_result,object_id_type,asset> operation_result;
struct base_operation
{
template<typename T>
share_type calculate_fee(const T& params)const
{
return params.fee;
}
void get_required_authorities( vector<authority>& )const{}
void get_required_active_authorities( flat_set<account_id_type>& )const{}
void get_required_owner_authorities( flat_set<account_id_type>& )const{}
void get_impacted_accounts( flat_set<account_id_type>& )const{}
void validate()const{}
static uint64_t calculate_data_fee( uint64_t bytes, uint64_t price_per_kbyte );
static void add_authority_accounts( flat_set<account_id_type>& i, const authority& a )
{
for( auto& item : a.account_auths )
i.insert( item.first );
}
};
/**
* For future expansion many structus include a single member of type
* extensions_type that can be changed when updating a protocol. You can
* always add new types to a static_variant without breaking backward
* compatibility.
*/
typedef static_variant<void_t> future_extensions;
/**
* A flat_set is used to make sure that only one extension of
* each type is added and that they are added in order.
*
* @note static_variant compares only the type tag and not the
* content.
*/
typedef flat_set<future_extensions> extensions_type;
///@}
} }
FC_REFLECT_TYPENAME( graphene::chain::operation_result )
FC_REFLECT( graphene::chain::void_result, )

View file

@ -16,14 +16,10 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/types.hpp>
#include <graphene/chain/transaction.hpp>
#include <graphene/chain/protocol/transaction.hpp>
namespace graphene { namespace chain {
struct void_header{};
typedef fc::static_variant<void_header> header_extension;
struct block_header
{
digest_type digest()const;
@ -34,9 +30,9 @@ namespace graphene { namespace chain {
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
checksum_type transaction_merkle_root;
vector<header_extension> extensions;
extensions_type extensions;
static uint32_t num_from_id(const block_id_type& id) { return htonl(id._hash[0]); }
static uint32_t num_from_id(const block_id_type& id);
};
struct signed_block_header : public block_header
@ -46,7 +42,7 @@ namespace graphene { namespace chain {
void sign( const fc::ecc::private_key& signer );
bool validate_signee( const fc::ecc::public_key& expected_signee )const;
signature_type delegate_signature;
signature_type witness_signature;
};
struct signed_block : public signed_block_header
@ -57,8 +53,7 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT( graphene::chain::void_header, )
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)
(next_secret_hash)(previous_secret)(transaction_merkle_root)(extensions) )
FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (delegate_signature) )
FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) )
FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) )

View file

@ -0,0 +1,130 @@
/*
* 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 <graphene/chain/protocol/types.hpp>
#include <fc/smart_ref_fwd.hpp>
namespace graphene { namespace chain { struct fee_schedule; } }
/*
namespace fc {
template<typename Stream, typename T> inline void pack( Stream& s, const graphene::chain::fee_schedule& value );
template<typename Stream, typename T> inline void unpack( Stream& s, graphene::chain::fee_schedule& value );
} // namespace fc
*/
namespace graphene { namespace chain {
typedef static_variant<> parameter_extension;
struct chain_parameters
{
/** using a smart ref breaks the circular dependency created between operations and the fee schedule */
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 committee_proposal_review_period = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC; ///< minimum time in seconds that a proposed transaction requiring committee 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
uint32_t maximum_time_until_expiration = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION; ///< maximum lifetime in seconds for transactions to be valid, before expiring
uint32_t maximum_proposal_lifetime = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC; ///< maximum lifetime in seconds for proposed transactions to be kept, before expiring
uint8_t maximum_asset_whitelist_authorities = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES; ///< maximum number of accounts which an asset may list as authorities for its whitelist OR blacklist
uint8_t maximum_asset_feed_publishers = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset
uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses
uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active committee_members
uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have
uint16_t reserve_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation
uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint16_t lifetime_referrer_percent_of_fee = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint32_t cashback_vesting_period_seconds = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC; ///< time after cashback rewards are accrued before they become liquid
share_type cashback_vesting_threshold = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD; ///< the maximum cashback that can be received without vesting
uint16_t max_bulk_discount_percent_of_fee = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT; ///< the maximum percentage discount for bulk discounts
share_type bulk_discount_threshold_min = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN; ///< the minimum amount of fees paid to qualify for bulk discounts
share_type bulk_discount_threshold_max = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX; ///< the amount of fees paid to qualify for the max bulk discount percent
bool count_non_member_votes = true; ///< set to false to restrict voting privlegages to member accounts
bool allow_non_member_whitelists = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise
share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block)
share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)
uint16_t max_predicate_opcode = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number
share_type fee_liquidation_threshold = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD; ///< value in CORE at which accumulated fees in blockchain-issued market assets should be liquidated
uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings
uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling
extensions_type extensions;
void validate()const
{
FC_ASSERT( reserve_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( max_bulk_discount_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee + lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( bulk_discount_threshold_min <= bulk_discount_threshold_max );
FC_ASSERT( bulk_discount_threshold_min > 0 );
FC_ASSERT( block_interval >= GRAPHENE_MIN_BLOCK_INTERVAL );
FC_ASSERT( block_interval <= GRAPHENE_MAX_BLOCK_INTERVAL );
FC_ASSERT( block_interval > 0 );
FC_ASSERT( maintenance_interval > block_interval,
"Maintenance interval must be longer than block interval" );
FC_ASSERT( maintenance_interval % block_interval == 0,
"Maintenance interval must be a multiple of block interval" );
FC_ASSERT( maximum_transaction_size >= GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT,
"Transaction size limit is too low" );
FC_ASSERT( maximum_block_size >= GRAPHENE_MIN_BLOCK_SIZE_LIMIT,
"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 - committee_proposal_review_period > block_interval,
"Committee proposal review period must be less than the maximum proposal lifetime" );
}
};
} } // graphene::chain
FC_REFLECT( graphene::chain::chain_parameters,
(current_fees)
(block_interval)
(maintenance_interval)
(committee_proposal_review_period)
(maximum_transaction_size)
(maximum_block_size)
(maximum_undo_history)
(maximum_time_until_expiration)
(maximum_proposal_lifetime)
(maximum_asset_whitelist_authorities)
(maximum_asset_feed_publishers)
(maximum_witness_count)
(maximum_committee_count)
(maximum_authority_membership)
(reserve_percent_of_fee)
(network_percent_of_fee)
(lifetime_referrer_percent_of_fee)
(max_bulk_discount_percent_of_fee)
(cashback_vesting_period_seconds)
(cashback_vesting_threshold)
(bulk_discount_threshold_min)
(bulk_discount_threshold_max)
(count_non_member_votes)
(allow_non_member_whitelists)
(witness_pay_per_block)
(worker_budget_per_day)
(max_predicate_opcode)
(fee_liquidation_threshold)
(accounts_per_fee_scale)
(account_fee_scale_bitshifts)
(extensions)
)

View file

@ -0,0 +1,59 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/chain_parameters.hpp>
namespace graphene { namespace chain {
/**
* @brief Create a committee_member object, as a bid to hold a committee_member seat on the network.
* @ingroup operations
*
* Accounts which wish to become committee_members may use this operation to create a committee_member object which stakeholders may
* vote on to approve its position as a committee_member.
*/
struct committee_member_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The account which owns the committee_member. This account pays the fee for this operation.
account_id_type committee_member_account;
string url;
account_id_type fee_payer()const { return committee_member_account; }
void validate()const;
};
/**
* @brief Used by committee_members to update the global parameters of the blockchain.
* @ingroup operations
*
* This operation allows the committee_members to update the global parameters on the blockchain. These control various
* tunable aspects of the chain, including block and maintenance intervals, maximum data sizes, the fees charged by
* the network, etc.
*
* This operation may only be used in a proposed transaction, and a proposed transaction which contains this
* operation must have a review period specified in the current global parameters before it may be accepted.
*/
struct committee_member_update_global_parameters_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
chain_parameters new_parameters;
account_id_type fee_payer()const { return account_id_type(); }
void validate()const;
};
/// TODO: committee_member_resign_operation : public base_operation
} } // graphene::chain
FC_REFLECT( graphene::chain::committee_member_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::committee_member_update_global_parameters_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::committee_member_create_operation,
(fee)(committee_member_account)(url) )
FC_REFLECT( graphene::chain::committee_member_update_global_parameters_operation, (fee)(new_parameters) );

View file

@ -0,0 +1,2 @@
#pragma once
#include <graphene/chain/config.hpp>

View file

@ -0,0 +1,35 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* @brief provides a generic way to add higher level protocols on top of witness consensus
* @ingroup operations
*
* There is no validation for this operation other than that required auths are valid and a fee
* is paid that is appropriate for the data contained.
*/
struct custom_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10;
};
asset fee;
account_id_type payer;
flat_set<account_id_type> required_auths;
uint16_t id;
vector<char> data;
account_id_type fee_payer()const { return payer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
};
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::custom_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) )

View file

@ -0,0 +1,58 @@
#pragma once
#include <graphene/chain/protocol/operations.hpp>
namespace graphene { namespace chain {
template<typename T> struct transform_to_fee_parameters;
template<typename ...T>
struct transform_to_fee_parameters<fc::static_variant<T...>>
{
typedef fc::static_variant< typename T::fee_parameters_type... > type;
};
typedef transform_to_fee_parameters<operation>::type fee_parameters;
/**
* @brief contains all of the parameters necessary to calculate the fee for any operation
*/
struct fee_schedule
{
fee_schedule();
static fee_schedule get_default();
/**
* Finds the appropriate fee parameter struct for the operation
* and then calculates the appropriate fee.
*/
asset calculate_fee( const operation& op, const price& core_exchange_rate = price::unit_price() )const;
asset set_fee( operation& op, const price& core_exchange_rate = price::unit_price() )const;
void zero_all_fees();
/**
* Validates all of the parameters are present and accounted for.
*/
void validate()const;
template<typename Operation>
const typename Operation::fee_parameters_type& get()const
{
auto itr = parameters.find( typename Operation::fee_parameters_type() );
FC_ASSERT( itr != parameters.end() );
return itr->template get<typename Operation::fee_parameters_type>();
}
/**
* @note must be sorted by fee_parameters.which() and have no duplicates
*/
flat_set<fee_parameters> parameters;
uint32_t scale = GRAPHENE_100_PERCENT; ///< fee * scale / GRAPHENE_100_PERCENT
};
typedef fee_schedule fee_schedule_type;
} } // graphene::chain
FC_REFLECT_TYPENAME( graphene::chain::fee_parameters )
FC_REFLECT( graphene::chain::fee_schedule, (parameters)(scale) )

View file

@ -0,0 +1,152 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* @class limit_order_create_operation
* @brief instructs the blockchain to attempt to sell one asset for another
* @ingroup operations
*
* The blockchain will atempt to sell amount_to_sell.asset_id for as
* much min_to_receive.asset_id as possible. The fee will be paid by
* the seller's account. Market fees will apply as specified by the
* issuer of both the selling asset and the receiving asset as
* a percentage of the amount exchanged.
*
* If either the selling asset or the receiving asset is white list
* restricted, the order will only be created if the seller is on
* the white list of the restricted asset type.
*
* Market orders are matched in the order they are included
* in the block chain.
*/
struct limit_order_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type seller;
asset amount_to_sell;
asset min_to_receive;
/// The order will be removed from the books if not filled by expiration
/// Upon expiration, all unsold asset will be returned to seller
time_point_sec expiration = time_point_sec::maximum();
/// If this flag is set the entire order must be filled or the operation is rejected
bool fill_or_kill = false;
extensions_type extensions;
pair<asset_id_type,asset_id_type> get_market()const
{
return amount_to_sell.asset_id < min_to_receive.asset_id ?
std::make_pair(amount_to_sell.asset_id, min_to_receive.asset_id) :
std::make_pair(min_to_receive.asset_id, amount_to_sell.asset_id);
}
account_id_type fee_payer()const { return seller; }
void validate()const;
price get_price()const { return amount_to_sell / min_to_receive; }
};
/**
* @ingroup operations
* Used to cancel an existing limit order. Both fee_pay_account and the
* account to receive the proceeds must be the same as order->seller.
*
* @return the amount actually refunded
*/
struct limit_order_cancel_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 0; };
asset fee;
limit_order_id_type order;
/** must be order->seller */
account_id_type fee_paying_account;
extensions_type extensions;
account_id_type fee_payer()const { return fee_paying_account; }
void validate()const;
};
/**
* @ingroup operations
*
* This operation can be used to add collateral, cover, and adjust the margin call price for a particular user.
*
* For prediction markets the collateral and debt must always be equal.
*
* This operation will fail if it would trigger a margin call that couldn't be filled. If the margin call hits
* the call price limit then it will fail if the call price is above the settlement price.
*
* @note this operation can be used to force a market order using the collateral without requiring outside funds.
*/
struct call_order_update_operation : public base_operation
{
/** this is slightly more expensive than limit orders, this pricing impacts prediction markets */
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type funding_account; ///< pays fee, collateral, and cover
asset delta_collateral; ///< the amount of collateral to add to the margin position
asset delta_debt; ///< the amount of the debt to be paid off, may be negative to issue new debt
extensions_type extensions;
account_id_type fee_payer()const { return funding_account; }
void validate()const;
};
/**
* @ingroup operations
*
* @note This is a virtual operation that is created while matching orders and
* emitted for the purpose of accurately tracking account history, accelerating
* a reindex.
*/
struct fill_order_operation : public base_operation
{
struct fee_parameters_type {};
fill_order_operation(){}
fill_order_operation( object_id_type o, account_id_type a, asset p, asset r, asset f )
:order_id(o),account_id(a),pays(p),receives(r),fee(f){}
object_id_type order_id;
account_id_type account_id;
asset pays;
asset receives;
asset fee; // paid by receiving account
pair<asset_id_type,asset_id_type> get_market()const
{
return pays.asset_id < receives.asset_id ?
std::make_pair( pays.asset_id, receives.asset_id ) :
std::make_pair( receives.asset_id, pays.asset_id );
}
account_id_type fee_payer()const { return account_id; }
void validate()const { FC_ASSERT( !"virtual operation" ); }
/// This is a virtual operation; there is no fee
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( account_id ); }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::limit_order_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::limit_order_cancel_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::call_order_update_operation::fee_parameters_type, (fee) )
/// THIS IS THE ONLY VIRTUAL OPERATION THUS FAR...
FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions))
FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) )
FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(extensions) )
FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) )

View file

@ -0,0 +1,68 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
/**
* @brief defines the keys used to derive the shared secret
*
* Because account authorities and keys can change at any time, each memo must
* capture the specific keys used to derive the shared secret. In order to read
* the cipher message you will need one of the two private keys.
*
* If @ref from == @ref to and @ref from == 0 then no encryption is used, the memo is public.
* If @ref from == @ref to and @ref from != 0 then invalid memo data
*
*/
struct memo_data
{
public_key_type from;
public_key_type to;
/**
* 64 bit nonce format:
* [ 8 bits | 56 bits ]
* [ entropy | timestamp ]
* Timestamp is number of microseconds since the epoch
* Entropy is a byte taken from the hash of a new private key
*
* This format is not mandated or verified; it is chosen to ensure uniqueness of key-IV pairs only. This should
* be unique with high probability as long as the generating host has a high-resolution clock OR a strong source
* of entropy for generating private keys.
*/
uint64_t nonce;
/**
* This field contains the AES encrypted packed @ref memo_message
*/
vector<char> message;
/// @note custom_nonce is for debugging only; do not set to a nonzero value in production
void set_message(const fc::ecc::private_key& priv,
const fc::ecc::public_key& pub, const string& msg, uint64_t custom_nonce = 0);
std::string get_message(const fc::ecc::private_key& priv,
const fc::ecc::public_key& pub)const;
};
/**
* @brief defines a message and checksum to enable validation of successful decryption
*
* When encrypting/decrypting a checksum is required to determine whether or not
* decryption was successful.
*/
struct memo_message
{
memo_message(){}
memo_message( uint32_t checksum, const std::string& text )
:checksum(checksum),text(text){}
uint32_t checksum = 0;
std::string text;
string serialize() const;
static memo_message deserialize(const string& serial);
};
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::memo_message, (checksum)(text) )
FC_REFLECT( graphene::chain::memo_data, (from)(to)(nonce)(message) )

View file

@ -0,0 +1,117 @@
/* Copyright (C) Cryptonomex, Inc - All Rights Reserved **/
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/account.hpp>
#include <graphene/chain/protocol/assert.hpp>
#include <graphene/chain/protocol/asset_ops.hpp>
#include <graphene/chain/protocol/balance.hpp>
#include <graphene/chain/protocol/custom.hpp>
#include <graphene/chain/protocol/committee_member.hpp>
#include <graphene/chain/protocol/market.hpp>
#include <graphene/chain/protocol/proposal.hpp>
#include <graphene/chain/protocol/transfer.hpp>
#include <graphene/chain/protocol/vesting.hpp>
#include <graphene/chain/protocol/withdraw_permission.hpp>
#include <graphene/chain/protocol/witness.hpp>
#include <graphene/chain/protocol/worker.hpp>
namespace graphene { namespace chain {
/**
* @ingroup operations
*
* Defines the set of valid operations as a discriminated union type.
*/
typedef fc::static_variant<
transfer_operation,
limit_order_create_operation,
limit_order_cancel_operation,
call_order_update_operation,
fill_order_operation,
account_create_operation,
account_update_operation,
account_whitelist_operation,
account_upgrade_operation,
account_transfer_operation,
asset_create_operation,
asset_update_operation,
asset_update_bitasset_operation,
asset_update_feed_producers_operation,
asset_issue_operation,
asset_reserve_operation,
asset_fund_fee_pool_operation,
asset_settle_operation,
asset_global_settle_operation,
asset_publish_feed_operation,
witness_create_operation,
witness_withdraw_pay_operation,
proposal_create_operation,
proposal_update_operation,
proposal_delete_operation,
withdraw_permission_create_operation,
withdraw_permission_update_operation,
withdraw_permission_claim_operation,
withdraw_permission_delete_operation,
committee_member_create_operation,
committee_member_update_global_parameters_operation,
vesting_balance_create_operation,
vesting_balance_withdraw_operation,
worker_create_operation,
custom_operation,
assert_operation,
balance_claim_operation,
override_transfer_operation
> operation;
/// @} // operations group
/**
* Appends required authorites to the result vector. The authorities appended are not the
* same as those returned by get_required_auth
*
* @return a set of required authorities for @ref op
*/
void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other );
void operation_get_impacted_accounts( const operation& op,
flat_set<account_id_type>& accounts );
void operation_validate( const operation& op );
/**
* Used to track the result of applying an operation and when it was applied.
*
* TODO: this doesn't belong here.
*/
struct applied_operation
{
operation op;
operation_result result;
uint32_t block_num;
uint16_t transaction_num;
uint16_t op_num;
};
/**
* @brief necessary to support nested operations inside the proposal_create_operation
*/
struct op_wrapper
{
public:
op_wrapper(const operation& op = operation()):op(op){}
operation op;
};
} } // graphene::chain
FC_REFLECT_TYPENAME( graphene::chain::operation )
FC_REFLECT( graphene::chain::op_wrapper, (op) )

View file

@ -0,0 +1,153 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* @defgroup proposed_transactions The Graphene Transaction Proposal Protocol
* @ingroup operations
*
* Graphene allows users to propose a transaction which requires approval of multiple accounts in order to execute.
* The user proposes a transaction using proposal_create_operation, then signatory accounts use
* proposal_update_operations to add or remove their approvals from this operation. When a sufficient number of
* approvals have been granted, the operations in the proposal are used to create a virtual transaction which is
* subsequently evaluated. Even if the transaction fails, the proposal will be kept until the expiration time, at
* which point, if sufficient approval is granted, the transaction will be evaluated a final time. This allows
* transactions which will not execute successfully until a given time to still be executed through the proposal
* mechanism. The first time the proposed transaction succeeds, the proposal will be regarded as resolved, and all
* future updates will be invalid.
*
* The proposal system allows for arbitrarily complex or recursively nested authorities. If a recursive authority
* (i.e. an authority which requires approval of 'nested' authorities on other accounts) is required for a
* proposal, then a second proposal can be used to grant the nested authority's approval. That is, a second
* proposal can be created which, when sufficiently approved, adds the approval of a nested authority to the first
* proposal. This multiple-proposal scheme can be used to acquire approval for an arbitrarily deep authority tree.
*
* Note that at any time, a proposal can be approved in a single transaction if sufficient signatures are available
* on the proposal_update_operation, as long as the authority tree to approve the proposal does not exceed the
* maximum recursion depth. In practice, however, it is easier to use proposals to acquire all approvals, as this
* leverages on-chain notification of all relevant parties that their approval is required. Off-chain
* multi-signature approval requires some off-chain mechanism for acquiring several signatures on a single
* transaction. This off-chain synchronization can be avoided using proposals.
* @{
*/
/**
* op_wrapper is used to get around the circular definition of operation and proposals that contain them.
*/
struct op_wrapper;
/**
* @brief The proposal_create_operation creates a transaction proposal, for use in multi-sig scenarios
* @ingroup operations
*
* Creates a transaction proposal. The operations which compose the transaction are listed in order in proposed_ops,
* and expiration_time specifies the time by which the proposal must be accepted or it will fail permanently. The
* expiration_time cannot be farther in the future than the maximum expiration time set in the global properties
* object.
*/
struct proposal_create_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10;
};
asset fee;
account_id_type fee_paying_account;
vector<op_wrapper> proposed_ops;
time_point_sec expiration_time;
optional<uint32_t> review_period_seconds;
extensions_type extensions;
/// Constructs a proposal_create_operation suitable for committee proposals, with fee, expiration time and review
/// period set appropriately.
static proposal_create_operation committee_proposal(const chain_parameters& param, fc::time_point_sec head_block_time );
account_id_type fee_payer()const { return fee_paying_account; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& )const;
};
/**
* @brief The proposal_update_operation updates an existing transaction proposal
* @ingroup operations
*
* This operation allows accounts to add or revoke approval of a proposed transaction. Signatures sufficient to
* satisfy the authority of each account in approvals are required on the transaction containing this operation.
*
* If an account with a multi-signature authority is listed in approvals_to_add or approvals_to_remove, either all
* required signatures to satisfy that account's authority must be provided in the transaction containing this
* operation, or a secondary proposal must be created which contains this operation.
*
* NOTE: If the proposal requires only an account's active authority, the account must not update adding its owner
* authority's approval. This is considered an error. An owner approval may only be added if the proposal requires
* the owner's authority.
*
* If an account's owner and active authority are both required, only the owner authority may approve. An attempt to
* add or remove active authority approval to such a proposal will fail.
*/
struct proposal_update_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10;
};
account_id_type fee_paying_account;
asset fee;
proposal_id_type proposal;
flat_set<account_id_type> active_approvals_to_add;
flat_set<account_id_type> active_approvals_to_remove;
flat_set<account_id_type> owner_approvals_to_add;
flat_set<account_id_type> owner_approvals_to_remove;
flat_set<public_key_type> key_approvals_to_add;
flat_set<public_key_type> key_approvals_to_remove;
extensions_type extensions;
account_id_type fee_payer()const { return fee_paying_account; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_required_authorities( vector<authority>& )const;
void get_required_active_authorities( flat_set<account_id_type>& )const;
void get_required_owner_authorities( flat_set<account_id_type>& )const;
};
/**
* @brief The proposal_delete_operation deletes an existing transaction proposal
* @ingroup operations
*
* This operation allows the early veto of a proposed transaction. It may be used by any account which is a required
* authority on the proposed transaction, when that account's holder feels the proposal is ill-advised and he decides
* he will never approve of it and wishes to put an end to all discussion of the issue. Because he is a required
* authority, he could simply refuse to add his approval, but this would leave the topic open for debate until the
* proposal expires. Using this operation, he can prevent any further breath from being wasted on such an absurd
* proposal.
*/
struct proposal_delete_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
account_id_type fee_paying_account;
bool using_owner_authority = false;
asset fee;
proposal_id_type proposal;
extensions_type extensions;
account_id_type fee_payer()const { return fee_paying_account; }
void validate()const;
};
///@}
}} // graphene::chain
FC_REFLECT( graphene::chain::proposal_create_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::proposal_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::proposal_delete_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::proposal_create_operation, (fee)(fee_paying_account)(expiration_time)
(proposed_ops)(review_period_seconds)(extensions) )
FC_REFLECT( graphene::chain::proposal_update_operation, (fee)(fee_paying_account)(proposal)
(active_approvals_to_add)(active_approvals_to_remove)(owner_approvals_to_add)(owner_approvals_to_remove)
(key_approvals_to_add)(key_approvals_to_remove)(extensions) )
FC_REFLECT( graphene::chain::proposal_delete_operation, (fee)(fee_paying_account)(using_owner_authority)(proposal)(extensions) )

View file

@ -0,0 +1,3 @@
#pragma once
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/protocol/block.hpp>

View file

@ -16,19 +16,10 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/types.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <numeric>
// this is for htonl() and ntohl() functions
// TODO: write and use FC wrappers for these functions
#ifndef WIN32
#include <arpa/inet.h>
#else
#include <winsock2.h>
#endif
namespace graphene { namespace chain {
/**
@ -96,6 +87,7 @@ namespace graphene { namespace chain {
*/
uint16_t relative_expiration = 1;
vector<operation> operations;
extensions_type extensions;
/// Calculate the digest for a transaction with a reference block
/// @param ref_block_id Full block ID of the reference block
@ -105,20 +97,8 @@ namespace graphene { namespace chain {
transaction_id_type id()const;
void validate() const;
void set_expiration( fc::time_point_sec expiration_time )
{
ref_block_num = 0;
relative_expiration = 0;
ref_block_prefix = expiration_time.sec_since_epoch();
block_id_cache.reset();
}
void set_expiration( const block_id_type& reference_block, unsigned_int lifetime_intervals = 3 )
{
ref_block_num = ntohl(reference_block._hash[0]);
ref_block_prefix = reference_block._hash[1];
relative_expiration = lifetime_intervals;
block_id_cache = reference_block;
}
void set_expiration( fc::time_point_sec expiration_time );
void set_expiration( const block_id_type& reference_block, unsigned_int lifetime_intervals = 3 );
/// visit all operations
template<typename Visitor>
@ -127,6 +107,14 @@ namespace graphene { namespace chain {
for( auto& op : operations )
op.visit( std::forward<Visitor>( visitor ) );
}
template<typename Visitor>
void visit( Visitor&& visitor )const
{
for( auto& op : operations )
op.visit( std::forward<Visitor>( visitor ) );
}
void get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const;
protected:
// Intentionally unreflected: does not go on wire
@ -141,18 +129,28 @@ namespace graphene { namespace chain {
signed_transaction( const transaction& trx = transaction() )
: transaction(trx){}
void sign( key_id_type id, const private_key_type& key );
/** signs and appends to signatures */
const signature_type& sign( const private_key_type& key );
flat_map<key_id_type,signature_type> signatures;
/** returns signature but does not append */
signature_type sign( const private_key_type& key )const;
/** some operations may depend only upon a signature and not
* require account approval. This allows those extra signatures
* to be added to the transaction.
/**
* Given a set of private keys sign this transaction with a minimial subset of required keys.
*
* @pram get_auth - used to fetch the active authority required for an account referenced by another authority
*/
flat_map<address,signature_type> extra_signatures;
void sign( const vector<private_key_type>& keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner );
bool verify( const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner )const;
vector<signature_type> signatures;
/// Removes all operations and signatures
void clear() { operations.clear(); signatures.clear(); extra_signatures.clear(); }
void clear() { operations.clear(); signatures.clear(); }
};
/**
@ -183,6 +181,6 @@ namespace graphene { namespace chain {
} }
FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(relative_expiration)(operations) )
FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures)(extra_signatures) )
FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(relative_expiration)(operations)(extensions) )
FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures) )
FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::signed_transaction), (operation_results) )

View file

@ -0,0 +1,93 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>
namespace graphene { namespace chain {
/**
* @ingroup operations
*
* @brief Transfers an amount of one asset from one account to another
*
* Fees are paid by the "from" account
*
* @pre amount.amount > 0
* @pre fee.amount >= 0
* @pre from != to
* @post from account's balance will be reduced by fee and amount
* @post to account's balance will be increased by amount
* @return n/a
*/
struct transfer_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10; /// only required for large memos.
};
asset fee;
/// Account to transfer asset from
account_id_type from;
/// Account to transfer asset to
account_id_type to;
/// The amount of asset to transfer from @ref from to @ref to
asset amount;
/// User provided data encrypted to the memo key of the "to" account
optional<memo_data> memo;
extensions_type extensions;
account_id_type fee_payer()const { return from; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{ i.insert(to); }
};
/**
* @class override_transfer_operation
* @brief Allows the issuer of an asset to transfer an asset from any account to any account if they have override_authority
* @ingroup operations
*
* @pre amount.asset_id->issuer == issuer
* @pre issuer != from because this is pointless, use a normal transfer operation
*/
struct override_transfer_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10; /// only required for large memos.
};
asset fee;
account_id_type issuer;
/// Account to transfer asset from
account_id_type from;
/// Account to transfer asset to
account_id_type to;
/// The amount of asset to transfer from @ref from to @ref to
asset amount;
/// User provided data encrypted to the memo key of the "to" account
optional<memo_data> memo;
extensions_type extensions;
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(to);
i.insert(from);
i.insert(issuer);
}
};
}} // graphene::chain
FC_REFLECT( graphene::chain::transfer_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::override_transfer_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::override_transfer_operation, (fee)(issuer)(from)(to)(amount)(memo)(extensions) )
FC_REFLECT( graphene::chain::transfer_operation, (fee)(from)(to)(amount)(memo)(extensions) )

View file

@ -18,7 +18,6 @@
#pragma once
#include <fc/container/flat_fwd.hpp>
#include <fc/io/varint.hpp>
#include <fc/io/raw_fwd.hpp>
#include <fc/io/enum_type.hpp>
#include <fc/crypto/sha224.hpp>
#include <fc/crypto/elliptic.hpp>
@ -28,11 +27,18 @@
#include <fc/safe.hpp>
#include <fc/container/flat.hpp>
#include <fc/string.hpp>
#include <fc/io/raw.hpp>
#include <fc/uint128.hpp>
#include <fc/static_variant.hpp>
#include <fc/smart_ref_fwd.hpp>
#include <memory>
#include <vector>
#include <deque>
#include <graphene/chain/address.hpp>
#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;
@ -51,6 +57,7 @@ namespace graphene { namespace chain {
using std::tie;
using std::make_pair;
using fc::smart_ref;
using fc::variant_object;
using fc::variant;
using fc::enum_type;
@ -66,6 +73,7 @@ namespace graphene { namespace chain {
using fc::ecc::range_proof_type;
using fc::ecc::range_proof_info;
using fc::ecc::commitment_type;
struct void_t{};
typedef fc::ecc::private_key private_key_type;
@ -101,14 +109,12 @@ namespace graphene { namespace chain {
{
null_object_type,
base_object_type,
key_object_type,
account_object_type,
asset_object_type,
force_settlement_object_type,
delegate_object_type,
committee_member_object_type,
witness_object_type,
limit_order_object_type,
short_order_object_type,
call_order_object_type,
custom_object_type,
proposal_object_type,
@ -116,6 +122,7 @@ namespace graphene { namespace chain {
withdraw_permission_object_type,
vesting_balance_object_type,
worker_object_type,
balance_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
@ -126,7 +133,7 @@ namespace graphene { namespace chain {
impl_index_meta_object_type,
impl_asset_dynamic_data_type,
impl_asset_bitasset_data_type,
impl_delegate_feeds_object_type,
impl_committee_member_feeds_object_type,
impl_account_balance_object_type,
impl_account_statistics_object_type,
impl_account_debt_object_type,
@ -142,17 +149,17 @@ namespace graphene { namespace chain {
meta_account_object_type
};
struct chain_parameters;
//typedef fc::unsigned_int object_id_type;
//typedef uint64_t object_id_type;
class account_object;
class delegate_object;
class committee_member_object;
class witness_object;
class asset_object;
class force_settlement_object;
class key_object;
class limit_order_object;
class short_order_object;
class call_order_object;
class custom_object;
class proposal_object;
@ -161,15 +168,14 @@ namespace graphene { namespace chain {
class vesting_balance_object;
class witness_schedule_object;
class worker_object;
class balance_object;
typedef object_id< protocol_ids, key_object_type, key_object> key_id_type;
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
typedef object_id< protocol_ids, delegate_object_type, delegate_object> delegate_id_type;
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
typedef object_id< protocol_ids, short_order_object_type, short_order_object> short_order_id_type;
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type;
typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type;
@ -177,9 +183,7 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type;
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
typedef object_id< relative_protocol_ids, key_object_type, key_object> relative_key_id_type;
typedef object_id< relative_protocol_ids, account_object_type, account_object> relative_account_id_type;
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
// implementation types
class global_property_object;
@ -196,7 +200,7 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
typedef object_id< implementation_ids, impl_asset_dynamic_data_type, asset_dynamic_data_object> dynamic_asset_data_id_type;
typedef object_id< implementation_ids, impl_asset_dynamic_data_type, asset_dynamic_data_object> asset_dynamic_data_id_type;
typedef object_id< implementation_ids, impl_asset_bitasset_data_type, asset_bitasset_data_object> asset_bitasset_data_id_type;
typedef object_id< implementation_ids, impl_account_balance_object_type, account_balance_object> account_balance_id_type;
typedef object_id< implementation_ids, impl_account_statistics_object_type,account_statistics_object> account_statistics_id_type;
@ -209,15 +213,15 @@ namespace graphene { namespace chain {
account_transaction_history_object> account_transaction_history_id_type;
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object > witness_schedule_id_type;
typedef fc::array<char,GRAPHENE_MAX_SYMBOL_NAME_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
typedef fc::ripemd160 checksum_type;
typedef fc::ripemd160 transaction_id_type;
typedef fc::sha256 digest_type;
typedef fc::ecc::compact_signature signature_type;
typedef safe<int64_t> share_type;
typedef fc::sha224 secret_hash_type;
typedef uint16_t weight_type;
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
typedef fc::ripemd160 checksum_type;
typedef fc::ripemd160 transaction_id_type;
typedef fc::sha256 digest_type;
typedef fc::ecc::compact_signature signature_type;
typedef safe<int64_t> share_type;
typedef fc::ripemd160 secret_hash_type;
typedef uint16_t weight_type;
/**
* @brief An ID for some votable object
@ -314,73 +318,6 @@ namespace graphene { namespace chain {
return std::to_string(type()) + ":" + std::to_string(instance());
}
};
struct fee_schedule_type
{
/**
* @brief The fee_set_visitor struct sets all fees to a particular value in one fell swoop
*
* Example:
* @code
* fee_schedule_type sch;
* // Set all fees to 50
* fc::reflector<fee_schedule_type>::visit(fee_schedule_type::fee_set_visitor{sch, 50});
* @endcode
*/
struct fee_set_visitor {
fee_schedule_type& f;
uint32_t fee;
template<typename Member, typename Class, Member (Class::*member)>
void operator()(const char*)const
{
f.*member = fee;
}
};
fee_schedule_type()
{
memset( (char*)this, 0, sizeof(*this) );
}
uint32_t key_create_fee; ///< the cost to register a public key with the blockchain
uint32_t account_create_fee; ///< the cost to register the cheapest non-free account
uint32_t account_len8_fee;
uint32_t account_len7_fee;
uint32_t account_len6_fee;
uint32_t account_len5_fee;
uint32_t account_len4_fee;
uint32_t account_len3_fee;
uint32_t account_len2_fee;
uint32_t account_premium_fee; ///< accounts with premium names; i.e. @ref is_cheap_name returns false
uint32_t account_whitelist_fee; ///< the fee to whitelist an account
uint32_t delegate_create_fee; ///< fixed fee for registering as a delegate; used to discourage frivioulous delegates
uint32_t witness_withdraw_pay_fee; ///< fee for withdrawing witness pay
uint32_t transfer_fee; ///< fee for transferring some asset
uint32_t limit_order_fee; ///< fee for placing a limit order in the markets
uint32_t short_order_fee; ///< fee for placing a short order in the markets
uint32_t publish_feed_fee; ///< fee for publishing a price feed
uint32_t asset_create_fee; ///< the cost to register the cheapest asset
uint32_t asset_update_fee; ///< the cost to modify a registered asset
uint32_t asset_issue_fee; ///< the cost to modify a registered asset
uint32_t asset_fund_fee_pool_fee; ///< the cost to add funds to an asset's fee pool
uint32_t asset_settle_fee; ///< the cost to trigger a forced settlement of a market-issued asset
uint32_t market_fee; ///< a percentage charged on market orders
uint32_t transaction_fee; ///< a base price for every transaction
uint32_t data_fee; ///< a price per 1024 bytes of user data
uint32_t signature_fee; ///< a surcharge on transactions with more than 2 signatures.
uint32_t global_parameters_update_fee; ///< the cost to update the global parameters
uint32_t membership_annual_fee; ///< the annual cost of a membership subscription
uint32_t membership_lifetime_fee; ///< the cost to upgrade to a lifetime member
uint32_t withdraw_permission_update_fee; ///< the cost to create/update a withdraw permission
uint32_t vesting_balance_create_fee;
uint32_t vesting_balance_withdraw_fee;
uint32_t global_settle_fee;
uint32_t worker_create_fee; ///< the cost to create a new worker
uint32_t worker_delete_fee; ///< the cost to delete a worker
};
struct public_key_type
{
struct binary_key
@ -389,9 +326,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 );
@ -402,62 +337,8 @@ 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);
};
struct chain_parameters
{
fee_schedule_type 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 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
uint32_t maximum_time_until_expiration = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION; ///< maximum lifetime in seconds for transactions to be valid, before expiring
uint32_t maximum_proposal_lifetime = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC; ///< maximum lifetime in seconds for proposed transactions to be kept, before expiring
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
uint8_t maximum_asset_whitelist_authorities = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES; ///< maximum number of accounts which an asset may list as authorities for its whitelist OR blacklist
uint8_t maximum_asset_feed_publishers = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset
uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses
uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active delegates
uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have
uint16_t burn_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation
uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint16_t lifetime_referrer_percent_of_fee = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint32_t cashback_vesting_period_seconds = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC; ///< time after cashback rewards are accrued before they become liquid
share_type cashback_vesting_threshold = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD; ///< the maximum cashback that can be received without vesting
uint16_t max_bulk_discount_percent_of_fee = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT; ///< the maximum percentage discount for bulk discounts
share_type bulk_discount_threshold_min = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN; ///< the minimum amount of fees paid to qualify for bulk discounts
share_type bulk_discount_threshold_max = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX; ///< the amount of fees paid to qualify for the max bulk discount percent
bool count_non_member_votes = true; ///< set to false to restrict voting privlegages to member accounts
bool allow_non_member_whitelists = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise
share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block)
share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)
void validate()const
{
FC_ASSERT( burn_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( max_bulk_discount_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee + lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( bulk_discount_threshold_min <= bulk_discount_threshold_max );
FC_ASSERT( bulk_discount_threshold_min > 0 );
FC_ASSERT( block_interval <= GRAPHENE_MAX_BLOCK_INTERVAL );
FC_ASSERT( block_interval > 0 );
FC_ASSERT( maintenance_interval > block_interval,
"Maintenance interval must be longer than block interval" );
FC_ASSERT( maintenance_interval % block_interval == 0,
"Maintenance interval must be a multiple of block interval" );
FC_ASSERT( maximum_transaction_size >= GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT,
"Transaction size limit is too low" );
FC_ASSERT( maximum_block_size >= GRAPHENE_MIN_BLOCK_SIZE_LIMIT,
"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,
"Genesis proposal review period must be less than the maximum proposal lifetime" );
}
// TODO: This is temporary for testing
bool is_valid_v1( const std::string& base58str );
};
} } // graphene::chain
@ -469,8 +350,9 @@ namespace fc
void to_variant( const graphene::chain::vote_id_type& var, fc::variant& vo );
void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo );
}
FC_REFLECT_TYPENAME( graphene::chain::vote_id_type::vote_type )
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
FC_REFLECT( graphene::chain::vote_id_type, (content) )
@ -480,14 +362,12 @@ FC_REFLECT( graphene::chain::public_key_type::binary_key, (data)(check) )
FC_REFLECT_ENUM( graphene::chain::object_type,
(null_object_type)
(base_object_type)
(key_object_type)
(account_object_type)
(force_settlement_object_type)
(asset_object_type)
(delegate_object_type)
(committee_member_object_type)
(witness_object_type)
(limit_order_object_type)
(short_order_object_type)
(call_order_object_type)
(custom_object_type)
(proposal_object_type)
@ -495,6 +375,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(withdraw_permission_object_type)
(vesting_balance_object_type)
(worker_object_type)
(balance_object_type)
(OBJECT_TYPE_COUNT)
)
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
@ -503,7 +384,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_index_meta_object_type)
(impl_asset_dynamic_data_type)
(impl_asset_bitasset_data_type)
(impl_delegate_feeds_object_type)
(impl_committee_member_feeds_object_type)
(impl_account_balance_object_type)
(impl_account_statistics_object_type)
(impl_account_debt_object_type)
@ -515,79 +396,14 @@ 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( graphene::chain::fee_schedule_type,
(key_create_fee)
(account_create_fee)
(account_len8_fee)
(account_len7_fee)
(account_len6_fee)
(account_len5_fee)
(account_len4_fee)
(account_len3_fee)
(account_len2_fee)
(account_premium_fee)
(account_whitelist_fee)
(delegate_create_fee)
(witness_withdraw_pay_fee)
(transfer_fee)
(limit_order_fee)
(short_order_fee)
(publish_feed_fee)
(asset_create_fee)
(asset_update_fee)
(asset_issue_fee)
(asset_fund_fee_pool_fee)
(asset_settle_fee)
(market_fee)
(transaction_fee)
(data_fee)
(signature_fee)
(global_parameters_update_fee)
(membership_annual_fee)
(membership_lifetime_fee)
(withdraw_permission_update_fee)
(vesting_balance_create_fee)
(vesting_balance_withdraw_fee)
(global_settle_fee)
(worker_create_fee)
(worker_delete_fee)
)
FC_REFLECT( graphene::chain::chain_parameters,
(current_fees)
(block_interval)
(maintenance_interval)
(maximum_transaction_size)
(maximum_block_size)
(maximum_undo_history)
(maximum_time_until_expiration)
(maximum_proposal_lifetime)
(maximum_asset_whitelist_authorities)
(maximum_asset_feed_publishers)
(maximum_authority_membership)
(burn_percent_of_fee)
(network_percent_of_fee)
(lifetime_referrer_percent_of_fee)
(max_bulk_discount_percent_of_fee)
(cashback_vesting_period_seconds)
(cashback_vesting_threshold)
(bulk_discount_threshold_min)
(bulk_discount_threshold_max)
(count_non_member_votes)
(allow_non_member_whitelists)
(witness_pay_per_block)
(worker_budget_per_day)
)
FC_REFLECT_TYPENAME( graphene::chain::key_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_id_type )
FC_REFLECT_TYPENAME( graphene::chain::force_settlement_id_type )
FC_REFLECT_TYPENAME( graphene::chain::delegate_id_type )
FC_REFLECT_TYPENAME( graphene::chain::committee_member_id_type )
FC_REFLECT_TYPENAME( graphene::chain::witness_id_type )
FC_REFLECT_TYPENAME( graphene::chain::limit_order_id_type )
FC_REFLECT_TYPENAME( graphene::chain::short_order_id_type )
FC_REFLECT_TYPENAME( graphene::chain::call_order_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_id_type )
FC_REFLECT_TYPENAME( graphene::chain::proposal_id_type )
@ -595,11 +411,9 @@ FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type )
FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type )
FC_REFLECT_TYPENAME( graphene::chain::worker_id_type )
FC_REFLECT_TYPENAME( graphene::chain::relative_key_id_type )
FC_REFLECT_TYPENAME( graphene::chain::relative_account_id_type )
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::dynamic_asset_data_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_bitasset_data_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_balance_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_statistics_id_type )
@ -608,5 +422,6 @@ FC_REFLECT_TYPENAME( graphene::chain::transaction_obj_id_type )
FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::witness_schedule_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle) )

View file

@ -0,0 +1,99 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct linear_vesting_policy_initializer
{
/** while vesting begins on begin_timestamp, none may be claimed before vesting_cliff_seconds have passed */
fc::time_point_sec begin_timestamp;
uint32_t vesting_cliff_seconds = 0;
uint32_t vesting_duration_seconds = 0;
};
struct cdd_vesting_policy_initializer
{
/** while coindays may accrue over time, none may be claimed before the start_claim time */
fc::time_point_sec start_claim;
uint32_t vesting_seconds = 0;
cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){}
};
typedef fc::static_variant<linear_vesting_policy_initializer, cdd_vesting_policy_initializer> vesting_policy_initializer;
/**
* @brief Create a vesting balance.
* @ingroup operations
*
* The chain allows a user to create a vesting balance.
* Normally, vesting balances are created automatically as part
* of cashback and worker operations. This operation allows
* vesting balances to be created manually as well.
*
* Manual creation of vesting balances can be used by a stakeholder
* to publicly demonstrate that they are committed to the chain.
* It can also be used as a building block to create transactions
* that function like public debt. Finally, it is useful for
* testing vesting balance functionality.
*
* @return ID of newly created vesting_balance_object
*/
struct vesting_balance_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type creator; ///< Who provides funds initially
account_id_type owner; ///< Who is able to withdraw the balance
asset amount;
vesting_policy_initializer policy;
account_id_type fee_payer()const { return creator; }
void validate()const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( amount.amount > 0 );
}
void get_impacted_accounts( flat_set<account_id_type>& i )const
{ i.insert(owner); }
};
/**
* @brief Withdraw from a vesting balance.
* @ingroup operations
*
* Withdrawal from a not-completely-mature vesting balance
* will result in paying fees.
*
* @return Nothing
*/
struct vesting_balance_withdraw_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 20*GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
vesting_balance_id_type vesting_balance;
account_id_type owner; ///< Must be vesting_balance.owner
asset amount;
account_id_type fee_payer()const { return owner; }
void validate()const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( amount.amount > 0 );
}
};
}} // graphene::chain
FC_REFLECT( graphene::chain::vesting_balance_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy) )
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) )
FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) )
FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) )
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer )

View file

@ -0,0 +1,167 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>
namespace graphene { namespace chain {
/**
* @brief Create a new withdrawal permission
* @ingroup operations
*
* This operation creates a withdrawal permission, which allows some authorized account to withdraw from an
* authorizing account. This operation is primarily useful for scheduling recurring payments.
*
* Withdrawal permissions define withdrawal periods, which is a span of time during which the authorized account may
* make a withdrawal. Any number of withdrawals may be made so long as the total amount withdrawn per period does
* not exceed the limit for any given period.
*
* Withdrawal permissions authorize only a specific pairing, i.e. a permission only authorizes one specified
* authorized account to withdraw from one specified authorizing account. Withdrawals are limited and may not exceet
* the withdrawal limit. The withdrawal must be made in the same asset as the limit; attempts with withdraw any
* other asset type will be rejected.
*
* The fee for this operation is paid by withdraw_from_account, and this account is required to authorize this
* operation.
*/
struct withdraw_permission_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The account authorizing withdrawals from its balances
account_id_type withdraw_from_account;
/// The account authorized to make withdrawals from withdraw_from_account
account_id_type authorized_account;
/// The maximum amount authorized_account is allowed to withdraw in a given withdrawal period
asset withdrawal_limit;
/// Length of the withdrawal period in seconds
uint32_t withdrawal_period_sec;
/// The number of withdrawal periods this permission is valid for
uint32_t periods_until_expiration;
/// Time at which the first withdrawal period begins; must be in the future
time_point_sec period_start_time;
account_id_type fee_payer()const { return withdraw_from_account; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( authorized_account ); }
};
/**
* @brief Update an existing withdraw permission
* @ingroup operations
*
* This oeration is used to update the settings for an existing withdrawal permission. The accounts to withdraw to
* and from may never be updated. The fields which may be updated are the withdrawal limit (both amount and asset
* type may be updated), the withdrawal period length, the remaining number of periods until expiration, and the
* starting time of the new period.
*
* Fee is paid by withdraw_from_account, which is required to authorize this operation
*/
struct withdraw_permission_update_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// This account pays the fee. Must match permission_to_update->withdraw_from_account
account_id_type withdraw_from_account;
/// The account authorized to make withdrawals. Must match permission_to_update->authorized_account
account_id_type authorized_account;
/// ID of the permission which is being updated
withdraw_permission_id_type permission_to_update;
/// New maximum amount the withdrawer is allowed to charge per withdrawal period
asset withdrawal_limit;
/// New length of the period between withdrawals
uint32_t withdrawal_period_sec;
/// New beginning of the next withdrawal period; must be in the future
time_point_sec period_start_time;
/// The new number of withdrawal periods for which this permission will be valid
uint32_t periods_until_expiration;
account_id_type fee_payer()const { return withdraw_from_account; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( authorized_account ); }
};
/**
* @brief Withdraw from an account which has published a withdrawal permission
* @ingroup operations
*
* This operation is used to withdraw from an account which has authorized such a withdrawal. It may be executed at
* most once per withdrawal period for the given permission. On execution, amount_to_withdraw is transferred from
* withdraw_from_account to withdraw_to_account, assuming amount_to_withdraw is within the withdrawal limit. The
* withdrawal permission will be updated to note that the withdrawal for the current period has occurred, and
* further withdrawals will not be permitted until the next withdrawal period, assuming the permission has not
* expired. This operation may be executed at any time within the current withdrawal period.
*
* Fee is paid by withdraw_to_account, which is required to authorize this operation
*/
struct withdraw_permission_claim_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20*GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10;
};
/// Paid by withdraw_to_account
asset fee;
/// ID of the permission authorizing this withdrawal
withdraw_permission_id_type withdraw_permission;
/// Must match withdraw_permission->withdraw_from_account
account_id_type withdraw_from_account;
/// Must match withdraw_permision->authorized_account
account_id_type withdraw_to_account;
/// Amount to withdraw. Must not exceed withdraw_permission->withdrawal_limit
asset amount_to_withdraw;
/// Memo for withdraw_from_account. Should generally be encrypted with withdraw_from_account->memo_key
optional<memo_data> memo;
account_id_type fee_payer()const { return withdraw_to_account; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( withdraw_from_account ); }
};
/**
* @brief Delete an existing withdrawal permission
* @ingroup operations
*
* This operation cancels a withdrawal permission, thus preventing any future withdrawals using that permission.
*
* Fee is paid by withdraw_from_account, which is required to authorize this operation
*/
struct withdraw_permission_delete_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 0; };
asset fee;
/// Must match withdrawal_permission->withdraw_from_account. This account pays the fee.
account_id_type withdraw_from_account;
/// The account previously authorized to make withdrawals. Must match withdrawal_permission->authorized_account
account_id_type authorized_account;
/// ID of the permission to be revoked.
withdraw_permission_id_type withdrawal_permission;
account_id_type fee_payer()const { return withdraw_from_account; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( authorized_account ); }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::withdraw_permission_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::withdraw_permission_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::withdraw_permission_claim_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::withdraw_permission_delete_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::withdraw_permission_create_operation, (fee)(withdraw_from_account)(authorized_account)
(withdrawal_limit)(withdrawal_period_sec)(periods_until_expiration)(period_start_time) )
FC_REFLECT( graphene::chain::withdraw_permission_update_operation, (fee)(withdraw_from_account)(authorized_account)
(permission_to_update)(withdrawal_limit)(withdrawal_period_sec)(period_start_time)(periods_until_expiration) )
FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw_permission)(withdraw_from_account)(withdraw_to_account)(amount_to_withdraw)(memo) );
FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account)
(withdrawal_permission) )

View file

@ -0,0 +1,59 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* @brief Create a witness object, as a bid to hold a witness position on the network.
* @ingroup operations
*
* Accounts which wish to become witnesses may use this operation to create a witness object which stakeholders may
* vote on to approve its position as a witness.
*/
struct witness_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The account which owns the witness. This account pays the fee for this operation.
account_id_type witness_account;
string url;
public_key_type block_signing_key;
secret_hash_type initial_secret;
account_id_type fee_payer()const { return witness_account; }
void validate()const;
};
/**
* @ingroup operations
* Used to move witness pay from accumulated_income to their account balance.
*
* TODO: remove this operation, send witness pay into a vesting balance object and
* have the witness claim the funds from there.
*/
struct witness_withdraw_pay_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The account to pay. Must match from_witness->witness_account. This account pays the fee for this operation.
account_id_type to_account;
witness_id_type from_witness;
share_type amount;
account_id_type fee_payer()const { return to_account; }
void validate()const;
};
/// TODO: witness_resign_operation : public base_operation
} } // graphene::chain
FC_REFLECT( graphene::chain::witness_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_withdraw_pay_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) )
FC_REFLECT( graphene::chain::witness_withdraw_pay_operation, (fee)(from_witness)(to_account)(amount) )

View file

@ -0,0 +1,83 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
/**
* @defgroup workers The Blockchain Worker System
* @ingroup operations
*
* Graphene blockchains allow the creation of special "workers" which are elected positions paid by the blockchain
* for services they provide. There may be several types of workers, and the semantics of how and when they are paid
* are defined by the @ref worker_type_enum enumeration. All workers are elected by core stakeholder approval, by
* voting for or against them.
*
* Workers are paid from the blockchain's daily budget if their total approval (votes for - votes against) is
* positive, ordered from most positive approval to least, until the budget is exhausted. Payments are processed at
* the blockchain maintenance interval. If a worker does not have positive approval during payment processing, or if
* the chain's budget is exhausted before the worker is paid, that worker is simply not paid at that interval.
* Payment is not prorated based on percentage of the interval the worker was approved. If the chain attempts to pay
* a worker, but the budget is insufficient to cover its entire pay, the worker is paid the remaining budget funds,
* even though this does not fulfill his total pay. The worker will not receive extra pay to make up the difference
* later. Worker pay is placed in a vesting balance and vests over the number of days specified at the worker's
* creation.
*
* Once created, a worker is immutable and will be kept by the blockchain forever.
*
* @{
*/
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;
};
struct burn_worker_initializer
{};
struct refund_worker_initializer
{};
typedef static_variant<
refund_worker_initializer,
vesting_balance_worker_initializer,
burn_worker_initializer > worker_initializer;
/**
* @brief Create a new worker object
* @ingroup operations
*/
struct worker_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5000*GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type owner;
time_point_sec work_begin_date;
time_point_sec work_end_date;
share_type daily_pay;
string name;
string url;
/// This should be set to the initializer appropriate for the type of worker to be created.
worker_initializer initializer;
account_id_type fee_payer()const { return owner; }
void validate()const;
};
///@}
} }
FC_REFLECT( graphene::chain::vesting_balance_worker_initializer, (pay_vesting_period_days) )
FC_REFLECT( graphene::chain::burn_worker_initializer, )
FC_REFLECT( graphene::chain::refund_worker_initializer, )
FC_REFLECT_TYPENAME( graphene::chain::worker_initializer )
FC_REFLECT( graphene::chain::worker_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::worker_create_operation,
(fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(name)(url)(initializer) )

View file

@ -1,65 +0,0 @@
/*
* 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 <graphene/chain/evaluator.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class short_order_create_evaluator : public evaluator<short_order_create_evaluator>
{
public:
typedef short_order_create_operation operation_type;
object_id_type do_evaluate( const short_order_create_operation& o );
object_id_type do_apply( const short_order_create_operation& o );
const short_order_create_operation* _op = nullptr;
const account_object* _seller = nullptr;
const asset_object* _sell_asset = nullptr;
const asset_object* _receive_asset = nullptr;
share_type _priority_fee = 0;
};
class short_order_cancel_evaluator : public evaluator<short_order_cancel_evaluator>
{
public:
typedef short_order_cancel_operation operation_type;
asset do_evaluate( const short_order_cancel_operation& o );
asset do_apply( const short_order_cancel_operation& o );
const short_order_object* _order;
};
class call_order_update_evaluator : public evaluator<call_order_update_evaluator>
{
public:
typedef call_order_update_operation operation_type;
asset do_evaluate( const call_order_update_operation& o );
asset do_apply( const call_order_update_operation& o );
bool _closing_order = false;
const asset_object* _debt_asset = nullptr;
const account_object* _paying_account = nullptr;
const call_order_object* _order = nullptr;
};
} } // graphene::chain

View file

@ -16,46 +16,48 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/operations.hpp>
#include <graphene/chain/authority.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene { namespace chain {
class database;
struct signed_transaction;
/**
* Place holder for state tracked while processing a
* transaction. This class provides helper methods
* that are common to many different operations and
* also tracks which keys have signed the transaction
* Place holder for state tracked while processing a transaction. This class provides helper methods that are
* common to many different operations and also tracks which keys have signed the transaction
*/
class transaction_evaluation_state
{
public:
transaction_evaluation_state( database* db = nullptr, bool skip_authority_check = false )
:_db(db),_skip_authority_check(skip_authority_check){}
transaction_evaluation_state( database* db = nullptr )
:_db(db){}
bool check_authority( const account_object&, authority::classification auth_class = authority::active, int depth = 0 );
bool check_authority(const account_object&,
authority::classification auth_class = authority::active,
int depth = 0);
bool check_authority(const authority&,
authority::classification auth_class = authority::active,
int depth = 0);
database& db()const { FC_ASSERT( _db ); return *_db; }
bool signed_by( key_id_type id )const;
bool signed_by(const public_key_type& k);
bool signed_by(const address& k);
/** derived from signatures on transaction
flat_set<address> signed_by;
*/
/** cached approval (accounts and keys) */
flat_set< pair<object_id_type,authority::classification> > approved_by;
/// cached approval (accounts and keys)
flat_set<pair<object_id_type,authority::classification>> approved_by;
/// Used to look up new objects using transaction relative IDs
vector<operation_result> operation_results;
/**
* Used to lookup new objects using transaction relative IDs
* When an address is referenced via check authority it is flagged as being used, all addresses must be
* flagged as being used or the transaction will fail.
*/
vector<operation_result> operation_results;
const signed_transaction* _trx = nullptr;
database* _db = nullptr;
bool _skip_authority_check = false;
bool _is_proposed_trx = false;
flat_map<public_key_type, bool> _sigs;
const signed_transaction* _trx = nullptr;
database* _db = nullptr;
bool _is_proposed_trx = false;
};
} } // namespace graphene::chain

View file

@ -18,7 +18,7 @@
#pragma once
#include <fc/io/raw.hpp>
#include <graphene/chain/transaction.hpp>
#include <graphene/chain/protocol/transaction.hpp>
#include <graphene/db/index.hpp>
#include <graphene/db/generic_index.hpp>
#include <fc/uint128.hpp>
@ -34,12 +34,9 @@ namespace graphene { namespace chain {
using boost::multi_index_container;
using namespace boost::multi_index;
/**
* The purpose of this object is to enable the detection
* of duplicate transactions. When a transaction is
* included in a block a transaction_object is
* added. At the end of block processing all
* transaction_objects that have expired can
* be removed from the index.
* The purpose of this object is to enable the detection of duplicate transactions. When a transaction is included
* in a block a transaction_object is added. At the end of block processing all transaction_objects that have
* expired can be removed from the index.
*/
class transaction_object : public abstract_object<transaction_object>
{
@ -52,7 +49,6 @@ namespace graphene { namespace chain {
transaction_id_type trx_id;
};
struct by_expiration;
struct by_id;
struct by_trx_id;
@ -66,7 +62,6 @@ namespace graphene { namespace chain {
> transaction_multi_index_type;
typedef generic_index<transaction_object, transaction_multi_index_type> transaction_index;
} }
FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(expiration) )

View file

@ -16,8 +16,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operations.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
@ -31,4 +31,13 @@ namespace graphene { namespace chain {
void_result do_apply( const transfer_operation& o );
};
class override_transfer_evaluator : public evaluator<override_transfer_evaluator>
{
public:
typedef override_transfer_operation operation_type;
void_result do_evaluate( const override_transfer_operation& o );
void_result do_apply( const override_transfer_operation& o );
};
} } // graphene::chain

View file

@ -29,7 +29,7 @@ class vesting_balance_create_evaluator : public evaluator<vesting_balance_create
public:
typedef vesting_balance_create_operation operation_type;
object_id_type do_evaluate( const vesting_balance_create_operation& op );
void_result do_evaluate( const vesting_balance_create_operation& op );
object_id_type do_apply( const vesting_balance_create_operation& op );
};

View file

@ -17,13 +17,16 @@
*/
#pragma once
#include <algorithm>
#include <graphene/chain/protocol/asset.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
#include <fc/static_variant.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/db/object.hpp>
#include <algorithm>
namespace graphene { namespace chain {
using namespace graphene::db;
@ -38,20 +41,29 @@ namespace graphene { namespace chain {
asset _amount)
: balance(_balance), now(_now), amount(_amount) {}
asset balance;
asset balance;
fc::time_point_sec now;
asset amount;
asset amount;
};
/**
* Linear vesting balance.
* @brief Linear vesting balance with cliff
*
* This vesting balance type is used to mimic traditional stock vesting contracts where
* each day a certain amount vests until it is fully matured.
*
* @note New funds may not be added to a linear vesting balance.
*/
struct linear_vesting_policy
{
uint32_t vesting_seconds; // must be greater than zero
fc::time_point_sec begin_date;
share_type begin_balance; // same asset as balance
share_type total_withdrawn; // same asset as balance
/// This is the time at which funds begin vesting.
fc::time_point_sec begin_timestamp;
/// No amount may be withdrawn before this many seconds of the vesting period have elapsed.
uint32_t vesting_cliff_seconds = 0;
/// Duration of the vesting period, in seconds. Must be greater than 0 and greater than vesting_cliff_seconds.
uint32_t vesting_duration_seconds = 0;
/// The total amount of asset to vest.
share_type begin_balance;
asset get_allowed_withdraw(const vesting_policy_context& ctx)const;
bool is_deposit_allowed(const vesting_policy_context& ctx)const;
@ -63,10 +75,19 @@ namespace graphene { namespace chain {
void on_withdraw(const vesting_policy_context& ctx);
};
/**
* @brief defines vesting in terms of coin-days accrued which allows for dynamic deposit/withdraw
*
* The economic effect of this vesting policy is to require a certain amount of "interest" to accrue
* before the full balance may be withdrawn. Interest accrues as coindays (balance * length held). If
* some of the balance is withdrawn, the remaining balance must be held longer.
*/
struct cdd_vesting_policy
{
uint32_t vesting_seconds;
uint32_t vesting_seconds = 0;
fc::uint128_t coin_seconds_earned;
/** while coindays may accrue over time, none may be claimed before first_claim date */
fc::time_point_sec start_claim;
fc::time_point_sec coin_seconds_earned_last_update;
/**
@ -98,8 +119,7 @@ namespace graphene { namespace chain {
> vesting_policy;
/**
* Timelocked balance object is a balance that is locked by the
* blockchain for a period of time.
* Vesting balance object is a balance that is locked by the blockchain for a period of time.
*/
class vesting_balance_object : public abstract_object<vesting_balance_object>
{
@ -107,15 +127,17 @@ namespace graphene { namespace chain {
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = vesting_balance_object_type;
account_id_type owner;
asset balance;
vesting_policy policy;
/// Account which owns and may withdraw from this vesting balance
account_id_type owner;
/// Total amount remaining in this vesting balance
/// Includes the unvested funds, and the vested funds which have not yet been withdrawn
asset balance;
/// The vesting policy stores details on when funds vest, and controls when they may be withdrawn
vesting_policy policy;
vesting_balance_object() {}
/**
* Used to increase existing vesting balances.
*/
///@brief Deposit amount into vesting balance, requiring it to vest before withdrawal
void deposit(const fc::time_point_sec& now, const asset& amount);
bool is_deposit_allowed(const fc::time_point_sec& now, const asset& amount)const;
@ -124,32 +146,48 @@ namespace graphene { namespace chain {
bool is_deposit_vested_allowed(const fc::time_point_sec& now, const asset& amount)const;
/**
* Used to remove a vesting balance from the VBO. As well
* as the balance field, coin_seconds_earned and
* Used to remove a vesting balance from the VBO. As well as the
* balance field, coin_seconds_earned and
* coin_seconds_earned_last_update fields are updated.
*
* The money doesn't "go" anywhere; the caller is responsible
* for crediting it to the proper account.
* The money doesn't "go" anywhere; the caller is responsible for
* crediting it to the proper account.
*/
void withdraw(const fc::time_point_sec& now, const asset& amount);
bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const;
};
/**
* @ingroup object_index
*/
typedef multi_index_container<
vesting_balance_object,
indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >
>
> vesting_balance_multi_index_type;
/**
* @ingroup object_index
*/
typedef generic_index<vesting_balance_object, vesting_balance_multi_index_type> vesting_balance_index;
} } // graphene::chain
FC_REFLECT(graphene::chain::linear_vesting_policy,
(vesting_seconds)
(begin_date)
(begin_timestamp)
(vesting_cliff_seconds)
(vesting_duration_seconds)
(begin_balance)
(total_withdrawn)
)
FC_REFLECT(graphene::chain::cdd_vesting_policy,
(vesting_seconds)
(start_claim)
(coin_seconds_earned)
(coin_seconds_earned_last_update)
)
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy )
FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object),
(owner)
(balance)

View file

@ -26,7 +26,7 @@ class withdraw_permission_create_evaluator : public evaluator<withdraw_permissio
public:
typedef withdraw_permission_create_operation operation_type;
object_id_type do_evaluate( const operation_type& op );
void_result do_evaluate( const operation_type& op );
object_id_type do_apply( const operation_type& op );
};

View file

@ -16,8 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/authority.hpp>
#include <graphene/chain/asset.hpp>
#include <graphene/chain/protocol/authority.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {

View file

@ -26,7 +26,7 @@ namespace graphene { namespace chain {
public:
typedef witness_create_operation operation_type;
object_id_type do_evaluate( const witness_create_operation& o );
void_result do_evaluate( const witness_create_operation& o );
object_id_type do_apply( const witness_create_operation& o );
};

View file

@ -16,8 +16,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/asset.hpp>
#include <graphene/chain/protocol/asset.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
@ -30,23 +31,37 @@ namespace graphene { namespace chain {
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = witness_object_type;
account_id_type witness_account;
key_id_type signing_key;
secret_hash_type next_secret;
secret_hash_type last_secret;
share_type accumulated_income;
vote_id_type vote_id;
account_id_type witness_account;
public_key_type signing_key;
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
share_type accumulated_income;
vote_id_type vote_id;
string url;
witness_object() : vote_id(vote_id_type::witness) {}
};
struct by_account;
using witness_multi_index_type = multi_index_container<
witness_object,
indexed_by<
hashed_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
hashed_unique< tag<by_account>,
member<witness_object, account_id_type, &witness_object::witness_account>
>
>
>;
using witness_index = generic_index<witness_object, witness_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
(witness_account)
(signing_key)
(next_secret)
(last_secret)
(next_secret_hash)
(previous_secret)
(accumulated_income)
(vote_id) )
(vote_id)
(url) )

View file

@ -19,8 +19,9 @@
// needed to serialize witness_scheduler
#include <fc/container/deque.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/types.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/witness_scheduler.hpp>
#include <graphene/chain/witness_scheduler_rng.hpp>
@ -56,7 +57,13 @@ class witness_schedule_object : public abstract_object<witness_schedule_object>
witness_scheduler scheduler;
uint32_t last_scheduling_block;
uint64_t slots_since_genesis = 0;
fc::array< char, GRAPHENE_RNG_SEED_LENGTH > rng_seed;
fc::array< char, sizeof(secret_hash_type) > rng_seed;
/**
* Not necessary for consensus, but used for figuring out the participation rate.
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
*/
fc::uint128 recent_slots_filled;
};
} }
@ -77,4 +84,5 @@ FC_REFLECT_DERIVED( graphene::chain::witness_schedule_object, (graphene::chain::
(last_scheduling_block)
(slots_since_genesis)
(rng_seed)
(recent_slots_filled)
)

View file

@ -40,6 +40,7 @@ class generic_witness_scheduler
public:
void check_invariant() const
{
#ifndef NDEBUG
CountType tokens = _ineligible_no_turn.size() + _eligible.size();
CountType turns = _eligible.size();
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
@ -47,7 +48,7 @@ class generic_witness_scheduler
assert( _tokens == tokens );
assert( _turns == turns );
#endif
flat_set< WitnessID > witness_set;
// make sure each witness_id occurs only once among the three states
@ -312,6 +313,40 @@ class generic_witness_scheduler
return true;
}
/**
* Reset the schedule, then re-schedule the given witness as the
* first witness.
*/
void reset_schedule( WitnessID first_witness )
{
_schedule.clear();
for( const WitnessID& wid : _ineligible_no_turn )
{
_eligible.push_back( wid );
}
_turns += _ineligible_no_turn.size();
_ineligible_no_turn.clear();
for( const auto& item : _ineligible_waiting_for_token )
{
_eligible.push_back( item.first );
_turns += (item.second ? 0 : 1);
}
_tokens += _ineligible_waiting_for_token.size();
_ineligible_waiting_for_token.clear();
if( debug ) check_invariant();
auto it = std::find( _eligible.begin(), _eligible.end(), first_witness );
assert( it != _eligible.end() );
_schedule.push_back( *it );
_ineligible_waiting_for_token.emplace_back( *it, false );
_eligible.erase( it );
_turns--;
_tokens--;
if( debug ) check_invariant();
return;
}
// keep track of total turns / tokens in existence
CountType _turns = 0;
CountType _tokens = 0;

Some files were not shown because too many files have changed in this diff Show more