Merge branch 'github_master'
Conflicts: CMakeLists.txt libraries/fc libraries/wallet/include/graphene/wallet/wallet.hpp
This commit is contained in:
commit
8927e800af
240 changed files with 19852 additions and 11830 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -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
5
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
133
README.md
|
|
@ -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
2
docs
|
|
@ -1 +1 @@
|
|||
Subproject commit 1b53a8eca77783d073ce7cc95991447c3f34b927
|
||||
Subproject commit 97435c1a622e41e0a5fc1be72aaadea62e1b7adb
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -167,7 +163,7 @@ namespace graphene { namespace app {
|
|||
else
|
||||
{
|
||||
result.reserve(assets.size());
|
||||
|
||||
|
||||
std::transform(assets.begin(), assets.end(), std::back_inserter(result),
|
||||
[this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
|
||||
}
|
||||
|
|
@ -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>();
|
||||
|
|
@ -258,7 +238,7 @@ namespace graphene { namespace app {
|
|||
|
||||
auto itr = assets_by_symbol.lower_bound(lower_bound_symbol);
|
||||
|
||||
if( lower_bound_symbol == "" )
|
||||
if( lower_bound_symbol == "" )
|
||||
itr = assets_by_symbol.begin();
|
||||
|
||||
while(limit-- && itr != assets_by_symbol.end())
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
69
libraries/chain/balance_evaluator.cpp
Normal file
69
libraries/chain/balance_evaluator.cpp
Normal 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
|
||||
225
libraries/chain/block_database.cpp
Normal file
225
libraries/chain/block_database.cpp
Normal 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>();
|
||||
}
|
||||
} }
|
||||
|
|
@ -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
|
||||
12
libraries/chain/database.cpp
Normal file
12
libraries/chain/database.cpp
Normal 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"
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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() )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
27
libraries/chain/include/graphene/chain/balance_evaluator.hpp
Normal file
27
libraries/chain/include/graphene/chain/balance_evaluator.hpp
Normal 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
|
||||
52
libraries/chain/include/graphene/chain/balance_object.hpp
Normal file
52
libraries/chain/include/graphene/chain/balance_object.hpp
Normal 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) )
|
||||
|
|
@ -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) )
|
||||
|
|
@ -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
|
||||
|
|
@ -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) )
|
||||
|
|
@ -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))
|
||||
///@}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<
|
||||
|
|
|
|||
129
libraries/chain/include/graphene/chain/genesis_state.hpp
Normal file
129
libraries/chain/include/graphene/chain/genesis_state.hpp
Normal 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))
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
||||
|
|
@ -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) )
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
|
|
|
|||
10
libraries/chain/include/graphene/chain/protocol/README.md
Normal file
10
libraries/chain/include/graphene/chain/protocol/README.md
Normal 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.
|
||||
257
libraries/chain/include/graphene/chain/protocol/account.hpp
Normal file
257
libraries/chain/include/graphene/chain/protocol/account.hpp
Normal 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) )
|
||||
|
|
@ -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; }
|
||||
75
libraries/chain/include/graphene/chain/protocol/assert.hpp
Normal file
75
libraries/chain/include/graphene/chain/protocol/assert.hpp
Normal 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) )
|
||||
|
|
@ -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 )
|
||||
|
||||
461
libraries/chain/include/graphene/chain/protocol/asset_ops.hpp
Normal file
461
libraries/chain/include/graphene/chain/protocol/asset_ops.hpp
Normal 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) );
|
||||
|
||||
|
|
@ -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) )
|
||||
36
libraries/chain/include/graphene/chain/protocol/balance.hpp
Normal file
36
libraries/chain/include/graphene/chain/protocol/balance.hpp
Normal 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) )
|
||||
105
libraries/chain/include/graphene/chain/protocol/base.hpp
Normal file
105
libraries/chain/include/graphene/chain/protocol/base.hpp
Normal 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, )
|
||||
|
|
@ -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) )
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
|
@ -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) );
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/config.hpp>
|
||||
35
libraries/chain/include/graphene/chain/protocol/custom.hpp
Normal file
35
libraries/chain/include/graphene/chain/protocol/custom.hpp
Normal 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) )
|
||||
|
|
@ -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) )
|
||||
|
||||
152
libraries/chain/include/graphene/chain/protocol/market.hpp
Normal file
152
libraries/chain/include/graphene/chain/protocol/market.hpp
Normal 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) )
|
||||
68
libraries/chain/include/graphene/chain/protocol/memo.hpp
Normal file
68
libraries/chain/include/graphene/chain/protocol/memo.hpp
Normal 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) )
|
||||
117
libraries/chain/include/graphene/chain/protocol/operations.hpp
Normal file
117
libraries/chain/include/graphene/chain/protocol/operations.hpp
Normal 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) )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
153
libraries/chain/include/graphene/chain/protocol/proposal.hpp
Normal file
153
libraries/chain/include/graphene/chain/protocol/proposal.hpp
Normal 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) )
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/block.hpp>
|
||||
|
|
@ -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) )
|
||||
93
libraries/chain/include/graphene/chain/protocol/transfer.hpp
Normal file
93
libraries/chain/include/graphene/chain/protocol/transfer.hpp
Normal 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) )
|
||||
|
||||
|
|
@ -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) )
|
||||
99
libraries/chain/include/graphene/chain/protocol/vesting.hpp
Normal file
99
libraries/chain/include/graphene/chain/protocol/vesting.hpp
Normal 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 )
|
||||
|
|
@ -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) )
|
||||
|
||||
59
libraries/chain/include/graphene/chain/protocol/witness.hpp
Normal file
59
libraries/chain/include/graphene/chain/protocol/witness.hpp
Normal 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) )
|
||||
83
libraries/chain/include/graphene/chain/protocol/worker.hpp
Normal file
83
libraries/chain/include/graphene/chain/protocol/worker.hpp
Normal 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) )
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue