This commit is contained in:
kstdl 2017-05-25 12:13:59 +03:00
commit 342e6db610
383 changed files with 139822 additions and 0 deletions

41
.gitignore vendored Normal file
View file

@ -0,0 +1,41 @@
*.a
*.sw*
*.cmake
CMakeCache.txt
CMakeFiles
Makefile
compile_commands.json
moc_*
*.moc
hardfork.hpp
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
tests/app_test
tests/chain_bench
tests/chain_test
tests/intense_test
tests/performance_test
doxygen
wallet.json
witness_node_data_dir
*.wallet
programs/witness_node/object_database/*
object_database/*
*.pyc
*.pyo

8
.gitmodules vendored Normal file
View file

@ -0,0 +1,8 @@
[submodule "docs"]
path = docs
url = https://github.com/cryptonomex/graphene.wiki.git
ignore = dirty
[submodule "libraries/fc"]
path = libraries/fc
url = https://github.com/PBSA/peerplays-0.1-fc.git
ignore = dirty

213
CMakeLists.txt Normal file
View file

@ -0,0 +1,213 @@
# Defines BitShares library target.
project( BitShares )
cmake_minimum_required( VERSION 2.8.12 )
set( BLOCKCHAIN_NAME "BitShares" )
set( CLI_CLIENT_EXECUTABLE_NAME graphene_client )
set( GUI_CLIENT_EXECUTABLE_NAME BitShares )
set( CUSTOM_URL_SCHEME "gcs" )
set( INSTALLER_APP_ID "68ad7005-8eee-49c9-95ce-9eed97e5b347" )
# http://stackoverflow.com/a/18369825
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
message(FATAL_ERROR "GCC version must be at least 4.8!")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3)
message(FATAL_ERROR "Clang version must be at least 3.3!")
endif()
endif()
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" )
set(CMAKE_EXPORT_COMPILE_COMMANDS "ON")
set(GRAPHENE_EGENESIS_JSON "${CMAKE_CURRENT_SOURCE_DIR}/genesis.json" CACHE PATH "location of the genesis.json to embed in the executable" )
#set (ENABLE_INSTALLER 1)
#set (USE_PCH 1)
if (USE_PCH)
include (cotire)
endif(USE_PCH)
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/CMakeModules" )
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/GitVersionGen" )
include( GetGitRevisionDescription )
get_git_head_revision( GIT_REFSPEC GIT_SHA2 )
SET(BOOST_COMPONENTS)
LIST(APPEND BOOST_COMPONENTS thread
date_time
system
filesystem
program_options
signals
serialization
chrono
unit_test_framework
context
locale)
SET( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
IF( WIN32 )
SET(BOOST_ROOT $ENV{BOOST_ROOT})
set(Boost_USE_MULTITHREADED ON)
set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries
ENDIF(WIN32)
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})
FIND_PACKAGE(Boost 1.54 REQUIRED COMPONENTS coroutine)
LIST(APPEND BOOST_COMPONENTS coroutine)
SET(Boost_LIBRARIES ${BOOST_LIBRARIES_TEMP} ${Boost_LIBRARIES})
ENDIF()
if( WIN32 )
message( STATUS "Configuring BitShares on WIN32")
set( DB_VERSION 60 )
set( BDB_STATIC_LIBS 1 )
set( ZLIB_LIBRARIES "" )
SET( DEFAULT_EXECUTABLE_INSTALL_DIR bin/ )
set(CRYPTO_LIB)
#looks like this flag can have different default on some machines.
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
# Probably cmake has a bug and vcxproj generated for executable in Debug conf. has disabled debug info
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /DEBUG")
# On windows tcl should be installed to the directory pointed by setenv.bat script
SET(TCL_INCLUDE_PATH $ENV{TCL_ROOT}/include)
MESSAGE(STATUS "tcl INCLUDE PATH: ${TCL_INCLUDE_PATH}")
FIND_PACKAGE(TCL)
MESSAGE(STATUS "tcl_library: ${TCL_LIBRARY}")
SET(TCL_LIBS "optimized;${TCL_LIBRARY};debug;")
get_filename_component(TCL_LIB_PATH "${TCL_LIBRARY}" PATH)
get_filename_component(TCL_LIB_NAME "${TCL_LIBRARY}" NAME_WE)
get_filename_component(TCL_LIB_EXT "${TCL_LIBRARY}" EXT)
SET(TCL_LIBS "${TCL_LIBS}${TCL_LIB_PATH}/${TCL_LIB_NAME}g${TCL_LIB_EXT}")
SET(TCL_LIBRARY ${TCL_LIBS})
else( WIN32 ) # Apple AND Linux
find_library(READLINE_LIBRARIES NAMES readline)
find_path(READLINE_INCLUDE_DIR readline/readline.h)
#if(NOT READLINE_INCLUDE_DIR OR NOT READLINE_LIBRARIES)
# MESSAGE(FATAL_ERROR "Could not find lib readline.")
#endif()
if( APPLE )
# Apple Specific Options Here
message( STATUS "Configuring BitShares on OS X" )
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -stdlib=libc++ -Wall" )
else( APPLE )
# Linux Specific Options Here
message( STATUS "Configuring BitShares on Linux" )
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -Wall" )
set( rt_library rt )
set( pthread_library pthread)
if ( NOT DEFINED crypto_library )
# I'm not sure why this is here, I guess someone has openssl and can't detect it with find_package()?
# if you have a normal install, you can define crypto_library to the empty string to avoid a build error
set( crypto_library crypto)
endif ()
if ( FULL_STATIC_BUILD )
set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc")
endif ( FULL_STATIC_BUILD )
endif( APPLE )
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp" )
endif()
if( "${CMAKE_GENERATOR}" STREQUAL "Ninja" )
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics" )
endif()
endif()
# based on http://www.delorie.com/gnu/docs/gdb/gdb_70.html
# uncomment this line to tell GDB about macros (slows compile times)
# set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-2 -g3" )
endif( WIN32 )
find_package( BerkeleyDB )
set(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL "Build BitShares for code coverage analysis")
if(ENABLE_COVERAGE_TESTING)
SET(CMAKE_CXX_FLAGS "--coverage ${CMAKE_CXX_FLAGS}")
endif()
add_subdirectory( libraries )
set(BUILD_BITSHARES_PROGRAMS TRUE CACHE BOOL "Build bitshares executables (witness node, cli wallet, etc)")
add_subdirectory( programs )
set(BUILD_BITSHARES_TESTS TRUE CACHE BOOL "Build bitshares unit tests")
if( BUILD_BITSHARES_TESTS )
add_subdirectory( tests )
endif( BUILD_BITSHARES_TESTS )
if (ENABLE_INSTALLER)
set(VERSION_MAJOR 0)
set(VERSION_MINOR 1)
set(VERSION_PATCH 0)
include(InstallRequiredSystemLibraries)
set(CPACK_OUTPUT_FILE_PREFIX ${CMAKE_BINARY_DIR}/packages)
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install)
SET(CPACK_PACKAGE_DIRECTORY "${CMAKE_INSTALL_PREFIX}")
set(CPACK_PACKAGE_NAME "graphene")
set(CPACK_PACKAGE_VENDOR "Cryptonomex, Inc.")
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}")
set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
set(CPACK_PACKAGE_DESCRIPTION "A client for the BitShares network")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A client for the BitShares network")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "BitShares ${CPACK_PACKAGE_VERSION}")
if(WIN32)
SET(CPACK_GENERATOR "ZIP;NSIS")
set(CPACK_PACKAGE_NAME "BitShares") # override above
set(CPACK_NSIS_EXECUTABLES_DIRECTORY .)
set(CPACK_NSIS_PACKAGE_NAME "BitShares v${CPACK_PACKAGE_VERSION}")
set(CPACK_NSIS_DISPLAY_NAME "${CPACK_NSIS_PACKAGE_NAME}")
set(CPACK_NSIS_DEFINES " !define MUI_STARTMENUPAGE_DEFAULTFOLDER \\\"BitShares\\\"")
# it seems like windows zip files usually don't have a single directory inside them, unix tgz frequently do
SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
endif(WIN32)
if(APPLE)
set(CPACK_GENERATOR "DragNDrop")
endif()
if(LINUX)
# Linux gets a .tgz
SET(CPACK_GENERATOR "TGZ")
SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 1)
endif(LINUX)
include(CPack)
endif(ENABLE_INSTALLER)

22
Dockerfile Normal file
View file

@ -0,0 +1,22 @@
# This will build the witness_node in a docker image. Make sure you've already
# checked out the submodules before building.
FROM l3iggs/archlinux:latest
MAINTAINER Nathan Hourt <nathan@followmyvote.com>
RUN pacman -Syu --noconfirm gcc make autoconf automake cmake ninja boost libtool git
ADD . /bitshares-2
WORKDIR /bitshares-2
RUN cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .
RUN ninja witness_node || ninja -j 1 witness_node
RUN mkdir /data_dir
ADD docker/default_config.ini /default_config.ini
ADD docker/launch /launch
RUN chmod a+x /launch
VOLUME /data_dir
EXPOSE 8090 9090
ENTRYPOINT ["/launch"]

2362
Doxyfile Normal file

File diff suppressed because it is too large Load diff

17
HEADER Normal file
View file

@ -0,0 +1,17 @@
/*
* 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.
*/

22
LICENSE.md Normal file
View file

@ -0,0 +1,22 @@
Copyright (c) 2015 Cryptonomex, Inc., and contributors.
The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

240
README.md Normal file
View file

@ -0,0 +1,240 @@
Intro for new developers
------------------------
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
cmake -DCMAKE_BUILD_TYPE=Debug .
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:
rpc-endpoint = 127.0.0.1:8090
Then, in a separate terminal window, start the command-line wallet `cli_wallet`:
./programs/cli_wallet/cli_wallet
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://github.com/cryptonomex/graphene/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
Code coverage testing
---------------------
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
------------
We use the Boost unit test framework for unit testing. Most unit
tests reside in the `chain_test` build target.
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.6.0"' '"1.6.1"' '"1.6.2"' '"1.6.3"' '"1.6.4"' '"1.6.5"' '"1.6.6"' '"1.6.7"' '"1.6.8"' '"1.6.9"' '"1.6.10"' '"1.6.11"' '"1.6.12"' '"1.6.13"' '"1.6.14"' '"1.6.15"' '"1.6.16"' '"1.6.17"' '"1.6.18"' '"1.6.19"' '"1.6.20"' '"1.6.21"' '"1.6.22"' '"1.6.23"' '"1.6.24"' '"1.6.25"' '"1.6.26"' '"1.6.27"' '"1.6.28"' '"1.6.29"' '"1.6.30"' '"1.6.31"' '"1.6.32"' '"1.6.33"' '"1.6.34"' '"1.6.35"' '"1.6.36"' '"1.6.37"' '"1.6.38"' '"1.6.39"' '"1.6.40"' '"1.6.41"' '"1.6.42"' '"1.6.43"' '"1.6.44"' '"1.6.45"' '"1.6.46"' '"1.6.47"' '"1.6.48"' '"1.6.49"' '"1.6.50"' '"1.6.51"' '"1.6.52"' '"1.6.53"' '"1.6.54"' '"1.6.55"' '"1.6.56"' '"1.6.57"' '"1.6.58"' '"1.6.59"' '"1.6.60"' '"1.6.61"' '"1.6.62"' '"1.6.63"' '"1.6.64"' '"1.6.65"' '"1.6.66"' '"1.6.67"' '"1.6.68"' '"1.6.69"' '"1.6.70"' '"1.6.71"' '"1.6.72"' '"1.6.73"' '"1.6.74"' '"1.6.75"' '"1.6.76"' '"1.6.77"' '"1.6.78"' '"1.6.79"' '"1.6.80"' '"1.6.81"' '"1.6.82"' '"1.6.83"' '"1.6.84"' '"1.6.85"' '"1.6.86"' '"1.6.87"' '"1.6.88"' '"1.6.89"' '"1.6.90"' '"1.6.91"' '"1.6.92"' '"1.6.93"' '"1.6.94"' '"1.6.95"' '"1.6.96"' '"1.6.97"' '"1.6.98"' '"1.6.99"' '"1.6.100"'
```
Running specific tests
----------------------
- `tests/chain_tests -t block_tests/name_of_test`
Using the API
-------------
We provide several different API's. Each API has its own ID.
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.
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.2.0"]]]}
< {"id":1,"result":[{"id":"1.2.0","annotations":[],"membership_expiration_date":"1969-12-31T23:59:59","registrar":"1.2.0","referrer":"1.2.0","lifetime_referrer":"1.2.0","network_fee_percentage":2000,"lifetime_referrer_fee_percentage":8000,"referrer_rewards_percentage":0,"name":"committee-account","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[],"address_auths":[]},"active":{"weight_threshold":6,"account_auths":[["1.2.5",1],["1.2.6",1],["1.2.7",1],["1.2.8",1],["1.2.9",1],["1.2.10",1],["1.2.11",1],["1.2.12",1],["1.2.13",1],["1.2.14",1]],"key_auths":[],"address_auths":[]},"options":{"memo_key":"GPH1111111111111111111111111111111114T1Anm","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"extensions":[]},"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.2.0"]]], "id": 1}' http://127.0.0.1:8090/rpc
{"id":1,"result":[{"id":"1.2.0","annotations":[],"membership_expiration_date":"1969-12-31T23:59:59","registrar":"1.2.0","referrer":"1.2.0","lifetime_referrer":"1.2.0","network_fee_percentage":2000,"lifetime_referrer_fee_percentage":8000,"referrer_rewards_percentage":0,"name":"committee-account","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[],"address_auths":[]},"active":{"weight_threshold":6,"account_auths":[["1.2.5",1],["1.2.6",1],["1.2.7",1],["1.2.8",1],["1.2.9",1],["1.2.10",1],["1.2.11",1],["1.2.12",1],["1.2.13",1],["1.2.14",1]],"key_auths":[],"address_auths":[]},"options":{"memo_key":"GPH1111111111111111111111111111111114T1Anm","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"extensions":[]},"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.2.0"]], "id": 1}' http://127.0.0.1:8090/rpc
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, while allowing any other user to access the three public API's
necessary to use the wallet:
{
"permission_map" :
[
[
"bytemaster",
{
"password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=",
"password_salt_b64" : "INDdM6iCi/8=",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api"]
}
],
[
"*",
{
"password_hash_b64" : "*",
"password_salt_b64" : "*",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_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_node",[]]}
{"id":3, "method":"call", "params":[2,"add_node",["127.0.0.1:9090"]]}
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_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
from the [database](https://bitshares.github.io/doxygen/classgraphene_1_1chain_1_1database.html);
it is fairly simple to write API methods to expose database methods.
Running private testnet
-----------------------
See the [documentation](https://github.com/cryptonomex/graphene/wiki/private-testnet) if you want to run a private testnet.
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.
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?
Yes. External programs may connect to the CLI wallet and make its calls over a websockets API. To do this, run the wallet in
server mode, i.e. `cli_wallet -s "127.0.0.1:9999"` and then have the external program connect to it over the specified port
(in this example, port 9999).
- Is there a way to access methods which require login over HTTP?
No. Login is inherently a stateful process (logging in changes what the server will do for certain requests, that's kind
of the point of having it). If you need to track state across HTTP RPC calls, you must maintain a session across multiple
connections. This is a famous source of security vulnerabilities for HTTP applications. Additionally, HTTP is not really
designed for "server push" notifications, and we would have to figure out a way to queue notifications for a polling client.
Websockets solves all these problems. If you need to access Graphene's stateful methods, you need to use Websockets.
- What is the meaning of `a.b.c` numbers?
The first number specifies the *space*. Space 1 is for protocol objects, 2 is for implementation objects.
Protocol space objects can appear on the wire, for example in the binary form of transactions.
Implementation space objects cannot appear on the wire and solely exist for implementation
purposes, such as optimization or internal bookkeeping.
The second number specifies the *type*. The type of the object determines what fields it has. For a
complete list of type ID's, see `enum object_type` and `enum impl_object_type` in
[types.hpp](https://github.com/cryptonomex/graphene/blob/master/libraries/chain/include/graphene/chain/protocol/types.hpp).
The third number specifies the *instance*. The instance of the object is different for each individual
object.
- The answer to the previous question was really confusing. Can you make it clearer?
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.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.2` for accounts,
`1.3` for assets). The third number identifies the particular thing.
- How do I get the `network_add_nodes` command to work? Why is it so complicated?
You need to follow the instructions in the "Accessing restricted API's" section to
allow a username/password access to the `network_node` API. Then you need
to pass the username/password to the `cli_wallet` on the command line or in a config file.
It's set up this way so that the default configuration is secure even if the RPC port is
publicly accessible. It's fine if your `witness_node` allows the general public to query
the database or broadcast transactions (in fact, this is how the hosted web UI works). It's
less fine if your `witness_node` allows the general public to control which p2p nodes it's
connecting to. Therefore the API to add p2p connections needs to be set up with proper access
controls.

112
Vagrantfile vendored Normal file
View file

@ -0,0 +1,112 @@
# Configures Ubuntu 14.04 VM to be used with BitShares 2.0 (Graphene)
# Downloads and builds all necessary software to run witness node and web GUI
# Use with Vagrant (http://docs.vagrantup.com/v2/getting-started/index.html)
# or just execute the shell script below.
# Vagrant setup supports the following providers: Virtual Box, Digital Ocean, Amazon EC2
$script = <<SCRIPT
# ------ shell script begin ------
echo_msg() {
/bin/echo -e "\e[1;36m*** $1 ***\e[0m"
}
echo_msg "Current user: `id`"
echo_msg "Current dir: `pwd`"
echo_msg "updating system.."
sudo apt-get -y update
sudo apt-get -yfV dist-upgrade
echo_msg "installing required packages.."
sudo apt-get install -yfV git libreadline-dev uuid-dev g++ libdb++-dev libdb-dev zip
sudo apt-get install -yfV libssl-dev openssl build-essential python-dev autotools-dev libicu-dev build-essential
sudo apt-get install -yfV libbz2-dev automake doxygen cmake ncurses-dev libtool nodejs nodejs-legacy npm mc
sudo apt-get -y autoremove
[ ! -d "bts" ] && mkdir bts && cd bts
[ ! -d "tmp" ] && mkdir tmp
[ ! -d "build" ] && mkdir build
if [ ! -d "tmp/boost_1_57_0" ]; then
echo_msg "building boost.."
cd tmp/
wget -nv 'http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.bz2/download'
tar -xf download
cd boost_1_57_0/
./bootstrap.sh --prefix=/usr/local/ > /dev/null
sudo ./b2 install > /dev/null
cd ~/bts
fi
if [ ! -d "graphene" ]; then
echo_msg "building bitshares graphene toolkit.."
git clone https://github.com/cryptonomex/graphene.git
cd graphene
git submodule update --init --recursive
cmake .
make
cd ~/bts
fi
if [ ! -d "graphene-ui" ]; then
echo_msg "installing ui dependencies.."
git clone https://github.com/cryptonomex/graphene-ui.git
cd graphene-ui/dl
npm install --silent
cd ../web
npm install --silent
npm run-script build
cd ~/bts
fi
# ------ shell script end ------
SCRIPT
Dir["*.sh"].each {|s| File.open(s,"r"){|f| $script << f.read()} } # includes additional .sh files (plug-ins)
Vagrant.configure(2) do |config|
config.vm.box = 'ubuntu_trusty_x64'
config.vm.provision 'shell', inline: $script, privileged: false
config.ssh.username = 'vagrant'
# to use with Digital Ocean please install this plugin https://github.com/smdahlen/vagrant-digitalocean
# note: due to bug in vagrant-digitalocean you need to run provision separetly:
# vagrant up --provider digital_ocean --no-provision
# vagrant provision
config.vm.provider :digital_ocean do |provider, override|
override.vm.hostname = 'graphene'
override.ssh.private_key_path = ENV['VAGRANT_KEY_PATH']
override.vm.box = 'digital_ocean'
override.vm.box_url = 'https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box'
provider.setup = true
provider.region = 'nyc2'
provider.image = 'ubuntu-14-04-x64'
provider.size = '4GB' # 2GB may be not enought to compile graphene toolkit
provider.token = ENV['DIGITALOCEAN_TOKEN']
provider.ssh_key_name = 'vagrant'
end
config.vm.provider :aws do |aws, override|
aws.access_key_id = ENV['AWS_ACCESS_KEY']
aws.secret_access_key = ENV['AWS_SECRET_KEY']
aws.keypair_name = $1 if ENV['VAGRANT_KEY_PATH'] =~ /([\w]+)[\.\w]*$/
aws.region = "us-east-1"
aws.ami = 'ami-018c9568'
aws.instance_type = 'm1.small'
aws.security_groups = [ 'bitsharesxt' ]
override.vm.hostname = 'bitsharesxt-aws'
override.ssh.username = 'ubuntu'
override.ssh.private_key_path = ENV['VAGRANT_KEY_PATH']
override.vm.box = 'dummy'
end
config.vm.provider 'virtualbox' do |v|
v.customize ['modifyvm', :id, '--memory', '4096']
v.customize ['modifyvm', :id, '--cpus', 4]
end
end

74
docker/default_config.ini Normal file
View file

@ -0,0 +1,74 @@
# Endpoint for P2P node to listen on
p2p-endpoint = 0.0.0.0:9090
# P2P nodes to connect to on startup (may specify multiple times)
# seed-node =
# JSON array of P2P nodes to connect to on startup
# seed-nodes =
# Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.
# checkpoint =
# Endpoint for websocket RPC to listen on
rpc-endpoint = 0.0.0.0:8090
# Endpoint for TLS websocket RPC to listen on
# rpc-tls-endpoint =
# The TLS certificate file for this server
# server-pem =
# Password for this certificate
# server-pem-password =
# File to read Genesis State from
# genesis-json =
# Block signing key to use for init witnesses, overrides genesis file
# dbg-init-key =
# JSON file specifying API permissions
# api-access =
# Enable block production, even if the chain is stale.
enable-stale-production = false
# Percent of witnesses (0-99) that must be participating in order to produce blocks
required-participation = false
# ID of witness controlled by this node (e.g. "1.6.5", quotes are required, may specify multiple times)
# witness-id =
# Tuple of [PublicKey, WIF private key] (may specify multiple times)
# private-key = ["BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]
# Account ID to track history for (may specify multiple times)
# track-account =
# Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers
bucket-size = [15,60,300,3600,86400]
# How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)
history-per-size = 1000
# declare an appender named "stderr" that writes messages to the console
[log.console_appender.stderr]
stream=std_error
# declare an appender named "p2p" that writes messages to p2p.log
[log.file_appender.p2p]
filename=logs/p2p/p2p.log
# filename can be absolute or relative to this config file
# route any messages logged to the default logger to the "stderr" logger we
# declared above, if they are info level are higher
[logger.default]
level=info
appenders=stderr
# route messages sent to the "p2p" logger to the p2p appender declared above
[logger.p2p]
level=debug
appenders=p2p

11
docker/launch Normal file
View file

@ -0,0 +1,11 @@
#!/bin/bash
[ -e /data_dir/config.ini ] || cp /default_config.ini /data_dir/config.ini
[ -e /data_dir/pre_exec ] && bash /data_dir/pre_exec
if [ -e /data_dir/extra_args ]; then
/bitshares-2/programs/witness_node/witness_node --data-dir /data_dir `cat /data_dir/extra_args`
else
/bitshares-2/programs/witness_node/witness_node --data-dir /data_dir
fi
[ -e /data_dir/post_exec ] && bash /data_dir/post_exec

1
docs Submodule

@ -0,0 +1 @@
Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f

640
genesis.json Normal file
View file

@ -0,0 +1,640 @@
{
"initial_timestamp": "2017-05-24T08:09:05",
"max_core_supply": "546053259451",
"initial_parameters": {
"current_fees": {
"parameters": [[
0,{
"fee": 1000,
"price_per_kbyte": 1000
}
],[
1,{
"fee": 50
}
],[
2,{
"fee": 0
}
],[
3,{
"fee": 500000000000
}
],[
4,{}
],[
5,{
"basic_fee": 1000,
"premium_fee": 1000000,
"price_per_kbyte": 1000
}
],[
6,{
"fee": 1000,
"price_per_kbyte": 1000
}
],[
7,{
"fee": 3000
}
],[
8,{
"membership_annual_fee": 500000000000,
"membership_lifetime_fee": 500000
}
],[
9,{
"fee": 200000
}
],[
10,{
"symbol3": "500000000000",
"symbol4": "500000000000",
"long_symbol": 5000000,
"price_per_kbyte": 1000
}
],[
11,{
"fee": 100000,
"price_per_kbyte": 1000
}
],[
12,{
"fee": 500000000000
}
],[
13,{
"fee": 500000000000
}
],[
14,{
"fee": 1000,
"price_per_kbyte": 1000
}
],[
15,{
"fee": 1000
}
],[
16,{
"fee": 3000
}
],[
17,{
"fee": 500000000000
}
],[
18,{
"fee": 500000000000
}
],[
19,{
"fee": 500000000000
}
],[
20,{
"fee": 1000000
}
],[
21,{
"fee": 2000
}
],[
22,{
"fee": 1000,
"price_per_kbyte": 1000
}
],[
23,{
"fee": 1000,
"price_per_kbyte": 1000
}
],[
24,{
"fee": 0
}
],[
25,{
"fee": 3000
}
],[
26,{
"fee": 200
}
],[
27,{
"fee": 200,
"price_per_kbyte": 1000
}
],[
28,{
"fee": 0
}
],[
29,{
"fee": 100000
}
],[
30,{
"fee": 2000
}
],[
31,{
"fee": 0
}
],[
32,{
"fee": 100000
}
],[
33,{
"fee": 50000
}
],[
34,{
"fee": 500000000000
}
],[
35,{
"fee": 1000,
"price_per_kbyte": 1000
}
],[
36,{
"fee": 1000
}
],[
37,{}
],[
38,{
"fee": 20000,
"price_per_kbyte": 1000
}
],[
39,{
"fee": 500000000000,
"price_per_output": 500000000000
}
],[
40,{
"fee": 500000000000,
"price_per_output": 500000000000
}
],[
41,{
"fee": 500000000000
}
],[
42,{}
],[
43,{
"fee": 3000
}
],[
44,{}
],[
45,{
"fee": 1000
}
],[
46,{
"fee": 5000
}
],[
47,{
"fee": 0
}
],[
48,{
"fee": 1000
}
],[
49,{}
]
],
"scale": 10000
},
"block_interval": 3,
"maintenance_interval": 3600,
"maintenance_skip_slots": 3,
"committee_proposal_review_period": 3600,
"maximum_transaction_size": 98304,
"maximum_block_size": 2097192,
"maximum_time_until_expiration": 86400,
"maximum_proposal_lifetime": 2419200,
"maximum_asset_whitelist_authorities": 10,
"maximum_asset_feed_publishers": 10,
"maximum_witness_count": 1001,
"maximum_committee_count": 1001,
"maximum_authority_membership": 10,
"reserve_percent_of_fee": 10000,
"network_percent_of_fee": 10000,
"lifetime_referrer_percent_of_fee": 0,
"cashback_vesting_period_seconds": 9999999,
"cashback_vesting_threshold": 500000000000,
"count_non_member_votes": true,
"allow_non_member_whitelists": true,
"witness_pay_per_block": 700,
"worker_budget_per_day": "0",
"max_predicate_opcode": 1,
"fee_liquidation_threshold": 500000000000,
"accounts_per_fee_scale": 1000,
"account_fee_scale_bitshifts": 0,
"max_authority_depth": 2,
"witness_schedule_algorithm": 1,
"min_round_delay": 1,
"max_round_delay": 300,
"min_time_per_commit_move": 15,
"max_time_per_commit_move": 15,
"min_time_per_reveal_move": 6,
"max_time_per_reveal_move": 6,
"rake_fee_percentage": 350,
"maximum_registration_deadline": 2592000,
"maximum_players_in_tournament": 256,
"maximum_tournament_whitelist_length": 1000,
"maximum_tournament_start_time_in_future": 5184000,
"maximum_tournament_start_delay": 259200,
"maximum_tournament_number_of_wins": 25,
"extensions": []
},
"initial_bts_accounts": [
{
"name": "bts-1",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY7uWdZSmuLioy2X3QrDdYbFYRkrJkkk1brWYpKgz1k9vMFFDL1u", 1]]
},
"active_authority": {
"key_auths": [["PPY7uWdZSmuLioy2X3QrDdYbFYRkrJkkk1brWYpKgz1k9vMFFDL1u", 1]]
}
},
{
"name": "bts-2",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY85SpcwFd8xP9ocJRSUHckFMkrqp6TExDqtrsheNWvFWszZYo7X", 1]]
},
"active_authority": {
"key_auths": [["PPY85SpcwFd8xP9ocJRSUHckFMkrqp6TExDqtrsheNWvFWszZYo7X", 1]]
}
},
{
"name": "bts-3",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY6dqcLw6GNgyKDBqgU52TzZ6zvsxisP5nMKSs2J8WLJR5wLPyDc", 1]]
},
"active_authority": {
"key_auths": [["PPY6dqcLw6GNgyKDBqgU52TzZ6zvsxisP5nMKSs2J8WLJR5wLPyDc", 1]]
}
},
{
"name": "bts-4",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY574FdeqJjGv3GXd1YPdJnLWQk61PYPDzHJcKPZuAT2BDdP9A86", 1]]
},
"active_authority": {
"key_auths": [["PPY574FdeqJjGv3GXd1YPdJnLWQk61PYPDzHJcKPZuAT2BDdP9A86", 1]]
}
},
{
"name": "bts-5",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY8jx67bhMkfsZo554sR3tiVW5Ujkt91kfUcmPK54MqWcYdFgm74", 1]]
},
"active_authority": {
"key_auths": [["PPY8jx67bhMkfsZo554sR3tiVW5Ujkt91kfUcmPK54MqWcYdFgm74", 1]]
}
},
{
"name": "bts-6",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY6fsh8fQLvtxP1pDSYRT26PvZFaNnnLxsnbVXU3bLGvUMzy94rB", 1],
["PPY716a2CfM86Vg6dg9jY83rognjzUoekSoCuQZn66kFiu8RD7i4w", 1]]
},
"active_authority": {
"key_auths": [["PPY6fsh8fQLvtxP1pDSYRT26PvZFaNnnLxsnbVXU3bLGvUMzy94rB", 1],
["PPY716a2CfM86Vg6dg9jY83rognjzUoekSoCuQZn66kFiu8RD7i4w", 1]]
}
},
{
"name": "bts-7",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY5M3sbj817awumetoGmyUrpACbdsKBemJNHbRr4dheLxdLjd1te", 1],
["PPY8Rg7jbF7FDPCrBjvXP9CUH2GBSWDJyh1P91PHszjsZqL73YBSy", 1]]
},
"active_authority": {
"key_auths": [["PPY5M3sbj817awumetoGmyUrpACbdsKBemJNHbRr4dheLxdLjd1te", 1],
["PPY8Rg7jbF7FDPCrBjvXP9CUH2GBSWDJyh1P91PHszjsZqL73YBSy", 1]]
}
},
{
"name": "bts-8",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY8bFWUv8QWod9h5CoR78BYDPrWxpRnqbG9t9ncjwfaCigZfumHy", 1],
["PPY8Gzi7xENM4EohQDRvySSPawN1raGhg1j3t86MvL8W3QrN7jKFX", 1]]
},
"active_authority": {
"key_auths": [["PPY8bFWUv8QWod9h5CoR78BYDPrWxpRnqbG9t9ncjwfaCigZfumHy", 1],
["PPY8Gzi7xENM4EohQDRvySSPawN1raGhg1j3t86MvL8W3QrN7jKFX", 1]]
}
},
{
"name": "bts-9",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY4xuGsVMzznyvWams8XvemFHdgg9iP7BxKzqftbeyxc4qMqv18u", 1],
["PPY7YZyxogivxKVcHe3UW8v4qeM5FryTsM3YmqJiXnML82Y7RigtY", 1]]
},
"active_authority": {
"key_auths": [["PPY4xuGsVMzznyvWams8XvemFHdgg9iP7BxKzqftbeyxc4qMqv18u", 1],
["PPY7YZyxogivxKVcHe3UW8v4qeM5FryTsM3YmqJiXnML82Y7RigtY", 1]]
}
},
{
"name": "bts-10",
"core_balance": 10000000,
"owner_authority": {
"key_auths": [["PPY7nLR7Y9KybkikF5VfvW2s2NwEh4M7DGG3tktbQGtYJNkEzTpcG", 1],
["PPY7eq1gy1oGW73AR5Y2HWz3GVuusPBTKcMVsoHEpwaihHoc6FC8x", 1]]
},
"active_authority": {
"key_auths": [["PPY7nLR7Y9KybkikF5VfvW2s2NwEh4M7DGG3tktbQGtYJNkEzTpcG", 1],
["PPY7eq1gy1oGW73AR5Y2HWz3GVuusPBTKcMVsoHEpwaihHoc6FC8x", 1]]
}
}
],
"initial_accounts": [{
"name": "init0",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init1",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init2",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init3",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init4",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init5",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init6",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init7",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init8",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init9",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "init10",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},{
"name": "testnet1",
"owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},
{
"name": "bal1",
"owner_key": "PPY6UYXNtsYN4rdSLu4hNgoVNer42h6QAiYTzo8irMQ5TcnSAM7nf",
"active_key": "PPY6UYXNtsYN4rdSLu4hNgoVNer42h6QAiYTzo8irMQ5TcnSAM7nf",
"is_lifetime_member": true
},
{
"name": "bal2",
"owner_key": "PPY7v8ujQ5Jr2wqVYwkV2HFN2j9uXYSuxPoruLwc1BDHMoNUFLuoy",
"active_key": "PPY7v8ujQ5Jr2wqVYwkV2HFN2j9uXYSuxPoruLwc1BDHMoNUFLuoy",
"is_lifetime_member": true
},
{
"name": "bal3",
"owner_key": "PPY7bHmq9MdL86c798ZX8rBv63Vj3UEyM7bhJebt2NW6X1CtpnvZ1",
"active_key": "PPY7bHmq9MdL86c798ZX8rBv63Vj3UEyM7bhJebt2NW6X1CtpnvZ1",
"is_lifetime_member": true
},
{
"name": "bal4",
"owner_key": "PPY5ECEkU8pVPYxEsauYdzsVjd5EHadGfhmgXWtWwFYB63TpmjZH9",
"active_key": "PPY5ECEkU8pVPYxEsauYdzsVjd5EHadGfhmgXWtWwFYB63TpmjZH9",
"is_lifetime_member": true
},
{
"name": "bal5",
"owner_key": "PPY8TMhq6b1rNVVMoowNzifXQctU48XtqoCENRGvCgXwRvvGjeaxx",
"active_key": "PPY8TMhq6b1rNVVMoowNzifXQctU48XtqoCENRGvCgXwRvvGjeaxx",
"is_lifetime_member": true
},
{
"name": "bal6",
"owner_key": "PPY8PoqAsCGMx56F2tjFgAKyy7Cx4nShnWaqvScytUFfeyK1Mbva7",
"active_key": "PPY8PoqAsCGMx56F2tjFgAKyy7Cx4nShnWaqvScytUFfeyK1Mbva7",
"is_lifetime_member": true
},
{
"name": "bal7",
"owner_key": "PPY6roVqWT5D1jNpEuWeBm5mRZAw7EoCgkXtT9unDqRSBiyUTaGk1",
"active_key": "PPY6roVqWT5D1jNpEuWeBm5mRZAw7EoCgkXtT9unDqRSBiyUTaGk1",
"is_lifetime_member": true
},
{
"name": "bal8",
"owner_key": "PPY6hkoqreUEr7JuUjZnKHiwTpruAnh3v4FTEA2stvnzgcS76qn8E",
"active_key": "PPY6hkoqreUEr7JuUjZnKHiwTpruAnh3v4FTEA2stvnzgcS76qn8E",
"is_lifetime_member": true
},
{
"name": "bal9",
"owner_key": "PPY5ogXvz1sdxV2gMgEs4Bnhb84dBML4Zto8yCGxLDqLnho1axjs8",
"active_key": "PPY5ogXvz1sdxV2gMgEs4Bnhb84dBML4Zto8yCGxLDqLnho1axjs8",
"is_lifetime_member": true
},
{
"name": "bal10",
"owner_key": "PPY6UvHq2Bdnk8imjnd7LYrbMZrXEoFDC5pjJ6wphifdqf6twNMb8",
"active_key": "PPY6UvHq2Bdnk8imjnd7LYrbMZrXEoFDC5pjJ6wphifdqf6twNMb8",
"is_lifetime_member": true
}
],
"initial_assets": [],
"initial_balances": [{
"owner": "PPYFAbAx7yuxt725qSZvfwWqkdCwp9ZnUama",
"asset_symbol": "PPY2T",
"amount": "1000000000000000"
},{
"owner": "PPYKdXdwpLonR1tzdLPC9KN75wikAD3vUwcd",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPY3qHup5gAshs6SBeNvExcVZj5UT542y71q",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPY97cZPDbQtVWeHHpohdd6vEWCuHkT699S8",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPYEuVv6Au277VXHb9htmwAvJvXD8M6aAXKV",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPYEQwfsiZRioNYKFhpxJhAB16uYguP5ykES",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPYZA1GZca2TFrazRaAvgPp1W73bQFMmyzp",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPYNvMhLLnVjiVCaFKmvMhx3hP54FaTBgXWM",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPY2TVnFbsdYLxjSEg5kiAc7dqupL8huVJhP",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPYPvAShbMSgVeW9PYxDQBBSQfGk1zRnUz3F",
"asset_symbol": "PPY2T",
"amount": "10000000"
},{
"owner": "PPY3HqXEv9hfqS2pZkYRb8nG6JqAmpj6W66M",
"asset_symbol": "PPY2T",
"amount": "10000000"
}
],
"initial_vesting_balances": [
{
"owner": "PPYKdXdwpLonR1tzdLPC9KN75wikAD3vUwcd",
"asset_symbol": "PPY2T",
"amount": "10000000",
"begin_timestamp": "2017-05-24T12:00:00",
"vesting_duration_seconds": "7200",
"begin_balance": "10000000"
},{
"owner": "PPY3qHup5gAshs6SBeNvExcVZj5UT542y71q",
"asset_symbol": "PPY2T",
"amount": "10000000",
"begin_timestamp": "2017-05-23T12:00:00",
"vesting_duration_seconds": "28800",
"begin_balance": "10000000"
},{
"owner": "PPY97cZPDbQtVWeHHpohdd6vEWCuHkT699S8",
"asset_symbol": "PPY2T",
"amount": "10000000",
"begin_timestamp": "2017-05-23T12:00:00",
"vesting_duration_seconds": "50400",
"begin_balance": "10000000"
},{
"owner": "PPYEuVv6Au277VXHb9htmwAvJvXD8M6aAXKV",
"asset_symbol": "PPY2T",
"amount": "10000000",
"begin_timestamp": "2017-05-23T12:00:00",
"vesting_duration_seconds": "72000",
"begin_balance": "10000000"
},{
"owner": "PPYQ1R7C1zJHkgTkBJ7VSdNUQTE4kYVAHVbJ",
"asset_symbol": "PPY2T",
"amount": "10000000",
"begin_timestamp": "2017-05-23T12:00:00",
"vesting_duration_seconds": "86400",
"begin_balance": "10000000"
}
],
"initial_active_witnesses": 11,
"initial_witness_candidates": [{
"owner_name": "init0",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init1",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init2",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init3",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init4",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init5",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init6",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init7",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init8",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init9",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
},{
"owner_name": "init10",
"block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
}
],
"initial_committee_candidates": [{
"owner_name": "init0"
},{
"owner_name": "init1"
},{
"owner_name": "init2"
},{
"owner_name": "init3"
},{
"owner_name": "init4"
},{
"owner_name": "init5"
},{
"owner_name": "init6"
}
],
"initial_worker_candidates": [],
"initial_chain_id": "aa34045518f1469a28fa4578240d5f039afa9959c0b95ce3b39674efa691fb21",
"immutable_parameters": {
"min_committee_member_count": 11,
"min_witness_count": 11,
"num_special_accounts": 0,
"num_special_assets": 0
}
}

8828
genesis/genesis.json Normal file

File diff suppressed because it is too large Load diff

1
gui_version Normal file
View file

@ -0,0 +1 @@
2.0.160208

12
libraries/CMakeLists.txt Normal file
View file

@ -0,0 +1,12 @@
add_subdirectory( fc )
add_subdirectory( db )
add_subdirectory( deterministic_openssl_rand )
add_subdirectory( chain )
add_subdirectory( egenesis )
add_subdirectory( net )
#add_subdirectory( p2p )
add_subdirectory( time )
add_subdirectory( utilities )
add_subdirectory( app )
add_subdirectory( plugins )
add_subdirectory( wallet )

View file

@ -0,0 +1,30 @@
file(GLOB HEADERS "include/graphene/app/*.hpp")
file(GLOB EGENESIS_HEADERS "../egenesis/include/graphene/app/*.hpp")
add_library( graphene_app
api.cpp
application.cpp
database_api.cpp
impacted.cpp
plugin.cpp
${HEADERS}
${EGENESIS_HEADERS}
)
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness )
target_include_directories( graphene_app
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
if(MSVC)
set_source_files_properties( application.cpp api.cpp database_api.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
INSTALL( TARGETS
graphene_app
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

564
libraries/app/api.cpp Normal file
View file

@ -0,0 +1,564 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <cctype>
#include <graphene/app/api.hpp>
#include <graphene/app/api_access.hpp>
#include <graphene/app/application.hpp>
#include <graphene/app/impacted.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/get_config.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace app {
login_api::login_api(application& a)
:_app(a)
{
}
login_api::~login_api()
{
}
bool login_api::login(const string& user, const string& password)
{
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 login_api::enable_api( const std::string& api_name )
{
if( api_name == "database_api" )
{
_database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ) );
}
else if( api_name == "network_broadcast_api" )
{
_network_broadcast_api = std::make_shared< network_broadcast_api >( std::ref( _app ) );
}
else if( api_name == "history_api" )
{
_history_api = std::make_shared< history_api >( _app );
}
else if( api_name == "network_node_api" )
{
_network_node_api = std::make_shared< network_node_api >( std::ref(_app) );
}
else if( api_name == "crypto_api" )
{
_crypto_api = std::make_shared< crypto_api >();
}
else if( api_name == "debug_api" )
{
// can only enable this API if the plugin was loaded
if( _app.get_plugin( "debug_witness" ) )
_debug_api = std::make_shared< graphene::debug_witness::debug_api >( std::ref(_app) );
}
return;
}
network_broadcast_api::network_broadcast_api(application& a):_app(a)
{
_applied_block_connection = _app.chain_database()->applied_block.connect([this](const signed_block& b){ on_applied_block(b); });
}
void network_broadcast_api::on_applied_block( const signed_block& b )
{
if( _callbacks.size() )
{
/// we need to ensure the database_api is not deleted for the life of the async operation
auto capture_this = shared_from_this();
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);
if( itr != _callbacks.end() )
{
auto block_num = b.block_num();
auto& callback = _callbacks.find(id)->second;
fc::async( [capture_this,this,id,block_num,trx_num,trx,callback](){ callback( 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);
}
void network_broadcast_api::broadcast_block( const signed_block& b )
{
_app.chain_database()->push_block(b);
_app.p2p_node()->broadcast( net::block_message( b ));
}
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 )
{
}
fc::variant_object network_node_api::get_info() const
{
fc::mutable_variant_object result = _app.p2p_node()->network_get_info();
result["connection_count"] = _app.p2p_node()->get_connection_count();
return result;
}
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();
}
std::vector<net::potential_peer_record> network_node_api::get_potential_peers() const
{
return _app.p2p_node()->get_potential_peers();
}
fc::variant_object network_node_api::get_advanced_node_parameters() const
{
return _app.p2p_node()->get_advanced_node_parameters();
}
void network_node_api::set_advanced_node_parameters(const fc::variant_object& params)
{
return _app.p2p_node()->set_advanced_node_parameters(params);
}
fc::api<network_broadcast_api> login_api::network_broadcast()const
{
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
{
FC_ASSERT(_database_api);
return *_database_api;
}
fc::api<history_api> login_api::history() const
{
FC_ASSERT(_history_api);
return *_history_api;
}
fc::api<crypto_api> login_api::crypto() const
{
FC_ASSERT(_crypto_api);
return *_crypto_api;
}
fc::api<graphene::debug_witness::debug_api> login_api::debug() const
{
FC_ASSERT(_debug_api);
return *_debug_api;
}
#if 0
vector<account_id_type> get_relevant_accounts( const object* obj )
{
vector<account_id_type> result;
if( obj->id.space() == protocol_ids )
{
switch( (object_type)obj->id.type() )
{
case null_object_type:
case base_object_type:
case OBJECT_TYPE_COUNT:
return result;
case account_object_type:{
result.push_back( obj->id );
break;
} case asset_object_type:{
const auto& aobj = dynamic_cast<const asset_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->issuer );
break;
} case force_settlement_object_type:{
const auto& aobj = dynamic_cast<const force_settlement_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case committee_member_object_type:{
const auto& aobj = dynamic_cast<const committee_member_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->committee_member_account );
break;
} case witness_object_type:{
const auto& aobj = dynamic_cast<const witness_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->witness_account );
break;
} case limit_order_object_type:{
const auto& aobj = dynamic_cast<const limit_order_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->seller );
break;
} case call_order_object_type:{
const auto& aobj = dynamic_cast<const call_order_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->borrower );
break;
} case custom_object_type:{
break;
} case proposal_object_type:{
const auto& aobj = dynamic_cast<const proposal_object*>(obj);
assert( aobj != nullptr );
flat_set<account_id_type> impacted;
transaction_get_impacted_accounts( aobj->proposed_transaction, impacted );
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case operation_history_object_type:{
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
assert( aobj != nullptr );
flat_set<account_id_type> impacted;
operation_get_impacted_accounts( aobj->op, impacted );
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case withdraw_permission_object_type:{
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->withdraw_from_account );
result.push_back( aobj->authorized_account );
break;
} case vesting_balance_object_type:{
const auto& aobj = dynamic_cast<const vesting_balance_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case worker_object_type:{
const auto& aobj = dynamic_cast<const worker_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->worker_account );
break;
} case balance_object_type:{
/** these are free from any accounts */
break;
} case tournament_object_type:{
const tournament_object* tournament_obj = dynamic_cast<const tournament_object*>(obj);
assert(tournament_obj);
const tournament_details_object& details = tournament_obj->tournament_details_id(*_app.chain_database());
flat_set<account_id_type> impacted = details.registered_players;
impacted.insert(tournament_obj->creator);
std::copy(impacted.begin(), impacted.end(), std::back_inserter(result));
break;
}
}
}
else if( obj->id.space() == implementation_ids )
{
switch( (impl_object_type)obj->id.type() )
{
case impl_global_property_object_type:
break;
case impl_dynamic_global_property_object_type:
break;
case impl_reserved0_object_type:
break;
case impl_asset_dynamic_data_type:
break;
case impl_asset_bitasset_data_type:
break;
case impl_account_balance_object_type:{
const auto& aobj = dynamic_cast<const account_balance_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case impl_account_statistics_object_type:{
const auto& aobj = dynamic_cast<const account_statistics_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case impl_transaction_object_type:{
const auto& aobj = dynamic_cast<const transaction_object*>(obj);
assert( aobj != nullptr );
flat_set<account_id_type> impacted;
transaction_get_impacted_accounts( aobj->trx, impacted );
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case impl_blinded_balance_object_type:{
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
assert( aobj != nullptr );
result.reserve( aobj->owner.account_auths.size() );
for( const auto& a : aobj->owner.account_auths )
result.push_back( a.first );
break;
} case impl_block_summary_object_type:
break;
case impl_account_transaction_history_object_type:
break;
case impl_chain_property_object_type:
break;
case impl_witness_schedule_object_type:
break;
case impl_budget_record_object_type:
break;
case impl_special_authority_object_type:
break;
case impl_buyback_object_type:
break;
case impl_fba_accumulator_object_type:
break;
}
}
return result;
} // end get_relevant_accounts( obj )
#endif
vector<order_history_object> history_api::get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const
{
FC_ASSERT(_app.chain_database());
const auto& db = *_app.chain_database();
if( a > b ) std::swap(a,b);
const auto& history_idx = db.get_index_type<graphene::market_history::history_index>().indices().get<by_key>();
history_key hkey;
hkey.base = a;
hkey.quote = b;
hkey.sequence = std::numeric_limits<int64_t>::min();
uint32_t count = 0;
auto itr = history_idx.lower_bound( hkey );
vector<order_history_object> result;
while( itr != history_idx.end() && count < limit)
{
if( itr->key.base != a || itr->key.quote != b ) break;
result.push_back( *itr );
++itr;
++count;
}
return result;
}
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();
FC_ASSERT( limit <= 100 );
vector<operation_history_object> result;
const auto& stats = account(db).statistics(db);
if( stats.most_recent_op == account_transaction_history_id_type() ) return result;
const account_transaction_history_object* node = &stats.most_recent_op(db);
if( start == operation_history_id_type() )
start = node->operation_id;
while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)
{
if( node->operation_id.instance.value <= start.instance.value )
result.push_back( node->operation_id(db) );
if( node->next == account_transaction_history_id_type() )
node = nullptr;
else node = &node->next(db);
}
return result;
}
vector<operation_history_object> history_api::get_relative_account_history( account_id_type account,
uint32_t stop,
unsigned limit,
uint32_t start) const
{
FC_ASSERT( _app.chain_database() );
const auto& db = *_app.chain_database();
FC_ASSERT(limit <= 100);
vector<operation_history_object> result;
if( start == 0 )
start = account(db).statistics(db).total_ops;
else start = min( account(db).statistics(db).total_ops, start );
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
const auto& by_seq_idx = hist_idx.indices().get<by_seq>();
auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) );
auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) );
--itr;
while ( itr != itr_stop && result.size() < limit )
{
result.push_back( itr->operation_id(db) );
--itr;
}
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(200);
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() < 200 )
{
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) ) }
crypto_api::crypto_api(){};
blind_signature crypto_api::blind_sign( const extended_private_key_type& key, const blinded_hash& hash, int i )
{
return fc::ecc::extended_private_key( key ).blind_sign( hash, i );
}
signature_type crypto_api::unblind_signature( const extended_private_key_type& key,
const extended_public_key_type& bob,
const blind_signature& sig,
const fc::sha256& hash,
int i )
{
return fc::ecc::extended_private_key( key ).unblind_signature( extended_public_key( bob ), sig, hash, i );
}
commitment_type crypto_api::blind( const blind_factor_type& blind, uint64_t value )
{
return fc::ecc::blind( blind, value );
}
blind_factor_type crypto_api::blind_sum( const std::vector<blind_factor_type>& blinds_in, uint32_t non_neg )
{
return fc::ecc::blind_sum( blinds_in, non_neg );
}
bool crypto_api::verify_sum( const std::vector<commitment_type>& commits_in, const std::vector<commitment_type>& neg_commits_in, int64_t excess )
{
return fc::ecc::verify_sum( commits_in, neg_commits_in, excess );
}
verify_range_result crypto_api::verify_range( const commitment_type& commit, const std::vector<char>& proof )
{
verify_range_result result;
result.success = fc::ecc::verify_range( result.min_val, result.max_val, commit, proof );
return result;
}
std::vector<char> crypto_api::range_proof_sign( uint64_t min_value,
const commitment_type& commit,
const blind_factor_type& commit_blind,
const blind_factor_type& nonce,
int8_t base10_exp,
uint8_t min_bits,
uint64_t actual_value )
{
return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value );
}
verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind( const blind_factor_type& nonce,
const commitment_type& commit,
const std::vector<char>& proof )
{
verify_range_proof_rewind_result result;
result.success = fc::ecc::verify_range_proof_rewind( result.blind_out,
result.value_out,
result.message_out,
nonce,
result.min_val,
result.max_val,
const_cast< commitment_type& >( commit ),
proof );
return result;
}
range_proof_info crypto_api::range_get_info( const std::vector<char>& proof )
{
return fc::ecc::range_get_info( proof );
}
} } // graphene::app

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

250
libraries/app/impacted.cpp Normal file
View file

@ -0,0 +1,250 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/protocol/authority.hpp>
#include <graphene/app/impacted.hpp>
namespace graphene { namespace app {
using namespace fc;
using namespace graphene::chain;
// TODO: Review all of these, especially no-ops
struct get_impacted_account_visitor
{
flat_set<account_id_type>& _impacted;
get_impacted_account_visitor( flat_set<account_id_type>& impact ):_impacted(impact) {}
typedef void result_type;
void operator()( const transfer_operation& op )
{
_impacted.insert( op.to );
}
void operator()( const asset_claim_fees_operation& op ){}
void operator()( const limit_order_create_operation& op ) {}
void operator()( const limit_order_cancel_operation& op )
{
_impacted.insert( op.fee_paying_account );
}
void operator()( const call_order_update_operation& op ) {}
void operator()( const fill_order_operation& op )
{
_impacted.insert( op.account_id );
}
void operator()( const account_create_operation& op )
{
_impacted.insert( op.registrar );
_impacted.insert( op.referrer );
add_authority_accounts( _impacted, op.owner );
add_authority_accounts( _impacted, op.active );
}
void operator()( const account_update_operation& op )
{
_impacted.insert( op.account );
if( op.owner )
add_authority_accounts( _impacted, *(op.owner) );
if( op.active )
add_authority_accounts( _impacted, *(op.active) );
}
void operator()( const account_whitelist_operation& op )
{
_impacted.insert( op.account_to_list );
}
void operator()( const account_upgrade_operation& op ) {}
void operator()( const account_transfer_operation& op )
{
_impacted.insert( op.new_owner );
}
void operator()( const asset_create_operation& op ) {}
void operator()( const asset_update_operation& op )
{
if( op.new_issuer )
_impacted.insert( *(op.new_issuer) );
}
void operator()( const asset_update_bitasset_operation& op ) {}
void operator()( const asset_update_dividend_operation& op ) {}
void operator()( const asset_dividend_distribution_operation& op )
{
_impacted.insert( op.account_id );
}
void operator()( const asset_update_feed_producers_operation& op ) {}
void operator()( const asset_issue_operation& op )
{
_impacted.insert( op.issue_to_account );
}
void operator()( const asset_reserve_operation& op ) {}
void operator()( const asset_fund_fee_pool_operation& op ) {}
void operator()( const asset_settle_operation& op ) {}
void operator()( const asset_global_settle_operation& op ) {}
void operator()( const asset_publish_feed_operation& op ) {}
void operator()( const witness_create_operation& op )
{
_impacted.insert( op.witness_account );
}
void operator()( const witness_update_operation& op )
{
_impacted.insert( op.witness_account );
}
void operator()( const proposal_create_operation& op )
{
vector<authority> other;
for( const auto& proposed_op : op.proposed_ops )
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other );
for( auto& o : other )
add_authority_accounts( _impacted, o );
}
void operator()( const proposal_update_operation& op ) {}
void operator()( const proposal_delete_operation& op ) {}
void operator()( const withdraw_permission_create_operation& op )
{
_impacted.insert( op.authorized_account );
}
void operator()( const withdraw_permission_update_operation& op )
{
_impacted.insert( op.authorized_account );
}
void operator()( const withdraw_permission_claim_operation& op )
{
_impacted.insert( op.withdraw_from_account );
}
void operator()( const withdraw_permission_delete_operation& op )
{
_impacted.insert( op.authorized_account );
}
void operator()( const committee_member_create_operation& op )
{
_impacted.insert( op.committee_member_account );
}
void operator()( const committee_member_update_operation& op )
{
_impacted.insert( op.committee_member_account );
}
void operator()( const committee_member_update_global_parameters_operation& op ) {}
void operator()( const vesting_balance_create_operation& op )
{
_impacted.insert( op.owner );
}
void operator()( const vesting_balance_withdraw_operation& op ) {}
void operator()( const worker_create_operation& op ) {}
void operator()( const custom_operation& op ) {}
void operator()( const assert_operation& op ) {}
void operator()( const balance_claim_operation& op ) {}
void operator()( const override_transfer_operation& op )
{
_impacted.insert( op.to );
_impacted.insert( op.from );
_impacted.insert( op.issuer );
}
void operator()( const transfer_to_blind_operation& op )
{
_impacted.insert( op.from );
for( const auto& out : op.outputs )
add_authority_accounts( _impacted, out.owner );
}
void operator()( const blind_transfer_operation& op )
{
for( const auto& in : op.inputs )
add_authority_accounts( _impacted, in.owner );
for( const auto& out : op.outputs )
add_authority_accounts( _impacted, out.owner );
}
void operator()( const transfer_from_blind_operation& op )
{
_impacted.insert( op.to );
for( const auto& in : op.inputs )
add_authority_accounts( _impacted, in.owner );
}
void operator()( const asset_settle_cancel_operation& op )
{
_impacted.insert( op.account );
}
void operator()( const fba_distribute_operation& op )
{
_impacted.insert( op.account_id );
}
void operator()( const tournament_create_operation& op )
{
_impacted.insert( op.creator );
_impacted.insert( op.options.whitelist.begin(), op.options.whitelist.end() );
}
void operator()( const tournament_join_operation& op )
{
_impacted.insert( op.payer_account_id );
_impacted.insert( op.player_account_id );
}
void operator()( const tournament_leave_operation& op )
{
//if account canceling registration is not the player, it must be the payer
if (op.canceling_account_id != op.player_account_id)
_impacted.erase( op.canceling_account_id );
_impacted.erase( op.player_account_id );
}
void operator()( const game_move_operation& op )
{
_impacted.insert( op.player_account_id );
}
void operator()( const tournament_payout_operation& op )
{
_impacted.insert( op.payout_account_id );
}
};
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
{
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
op.visit( vtor );
}
void transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result )
{
for( const auto& op : tx.operations )
operation_get_impacted_accounts( op, result );
}
} }

View file

@ -0,0 +1,352 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/database_api.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <graphene/net/node.hpp>
#include <fc/api.hpp>
#include <fc/optional.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/network/ip.hpp>
#include <boost/container/flat_set.hpp>
#include <functional>
#include <map>
#include <string>
#include <vector>
namespace graphene { namespace app {
using namespace graphene::chain;
using namespace graphene::market_history;
using namespace fc::ecc;
using namespace std;
class application;
struct verify_range_result
{
bool success;
uint64_t min_val;
uint64_t max_val;
};
struct verify_range_proof_rewind_result
{
bool success;
uint64_t min_val;
uint64_t max_val;
uint64_t value_out;
fc::ecc::blind_factor_type blind_out;
string message_out;
};
/**
* @brief The history_api class implements the RPC API for account history
*
* This API contains methods to access account histories
*/
class history_api
{
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(),
unsigned limit = 100,
operation_history_id_type start = operation_history_id_type())const;
/**
* @breif Get operations relevant to the specified account referenced
* by an event numbering specific to the account. The current number of operations
* for the account can be found in the account statistics (or use 0 for start).
* @param account The account whose history should be queried
* @param stop Sequence number of earliest operation. 0 is default and will
* query 'limit' number of operations.
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start Sequence number of the most recent operation to retrieve.
* 0 is default, which will start querying from the most recent operation.
* @return A list of operations performed by account, ordered from most recent to oldest.
*/
vector<operation_history_object> get_relative_account_history( account_id_type account,
uint32_t stop = 0,
unsigned limit = 100,
uint32_t start = 0) const;
vector<order_history_object> get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const;
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_broadcast_api class allows broadcasting of transactions.
*/
class network_broadcast_api : public std::enable_shared_from_this<network_broadcast_api>
{
public:
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
* @param trx The transaction to broadcast
*
* The transaction will be checked for validity in the local database prior to broadcasting. If it fails to
* 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);
void broadcast_block( const signed_block& block );
/**
* @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 Return general network information, such as p2p port
*/
fc::variant_object get_info() const;
/**
* @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
*/
std::vector<net::peer_status> get_connected_peers() const;
/**
* @brief Get advanced node parameters, such as desired and max
* number of connections
*/
fc::variant_object get_advanced_node_parameters() const;
/**
* @brief Set advanced node parameters, such as desired and max
* number of connections
* @param params a JSON object containing the name/value pairs for the parameters to set
*/
void set_advanced_node_parameters(const fc::variant_object& params);
/**
* @brief Return list of potential peers
*/
std::vector<net::potential_peer_record> get_potential_peers() const;
private:
application& _app;
};
class crypto_api
{
public:
crypto_api();
fc::ecc::blind_signature blind_sign( const extended_private_key_type& key, const fc::ecc::blinded_hash& hash, int i );
signature_type unblind_signature( const extended_private_key_type& key,
const extended_public_key_type& bob,
const fc::ecc::blind_signature& sig,
const fc::sha256& hash,
int i );
fc::ecc::commitment_type blind( const fc::ecc::blind_factor_type& blind, uint64_t value );
fc::ecc::blind_factor_type blind_sum( const std::vector<blind_factor_type>& blinds_in, uint32_t non_neg );
bool verify_sum( const std::vector<commitment_type>& commits_in, const std::vector<commitment_type>& neg_commits_in, int64_t excess );
verify_range_result verify_range( const fc::ecc::commitment_type& commit, const std::vector<char>& proof );
std::vector<char> range_proof_sign( uint64_t min_value,
const commitment_type& commit,
const blind_factor_type& commit_blind,
const blind_factor_type& nonce,
int8_t base10_exp,
uint8_t min_bits,
uint64_t actual_value );
verify_range_proof_rewind_result verify_range_proof_rewind( const blind_factor_type& nonce,
const fc::ecc::commitment_type& commit,
const std::vector<char>& proof );
range_proof_info range_get_info( const std::vector<char>& proof );
};
/**
* @brief The login_api class implements the bottom layer of the RPC API
*
* All other APIs must be requested from this API.
*/
class login_api
{
public:
login_api(application& a);
~login_api();
/**
* @brief Authenticate to the RPC server
* @param user Username to login with
* @param password Password to login with
* @return True if logged in successfully; false otherwise
*
* @note This must be called prior to requesting other APIs. Other APIs may not be accessible until the client
* has sucessfully authenticated.
*/
bool login(const string& user, const string& password);
/// @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;
/// @brief Retrieve the cryptography API
fc::api<crypto_api> crypto()const;
/// @brief Retrieve the debug API (if available)
fc::api<graphene::debug_witness::debug_api> debug()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_broadcast_api> > _network_broadcast_api;
optional< fc::api<network_node_api> > _network_node_api;
optional< fc::api<history_api> > _history_api;
optional< fc::api<crypto_api> > _crypto_api;
optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;
};
}} // graphene::app
FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation,
(id)(block_num)(trx_num)(trx) )
FC_REFLECT( graphene::app::verify_range_result,
(success)(min_val)(max_val) )
FC_REFLECT( graphene::app::verify_range_proof_rewind_result,
(success)(min_val)(max_val)(value_out)(blind_out)(message_out) )
//FC_REFLECT_TYPENAME( fc::ecc::compact_signature );
//FC_REFLECT_TYPENAME( fc::ecc::commitment_type );
FC_API(graphene::app::history_api,
(get_account_history)
(get_relative_account_history)
(get_fill_order_history)
(get_market_history)
(get_market_history_buckets)
)
FC_API(graphene::app::network_broadcast_api,
(broadcast_transaction)
(broadcast_transaction_with_callback)
(broadcast_block)
)
FC_API(graphene::app::network_node_api,
(get_info)
(add_node)
(get_connected_peers)
(get_potential_peers)
(get_advanced_node_parameters)
(set_advanced_node_parameters)
)
FC_API(graphene::app::crypto_api,
(blind_sign)
(unblind_signature)
(blind)
(blind_sum)
(verify_sum)
(verify_range)
(range_proof_sign)
(verify_range_proof_rewind)
(range_get_info)
)
FC_API(graphene::app::login_api,
(login)
(network_broadcast)
(database)
(history)
(network_node)
(crypto)
(debug)
)

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/reflect/reflect.hpp>
#include <map>
#include <string>
#include <vector>
namespace graphene { namespace app {
struct api_access_info
{
std::string password_hash_b64;
std::string password_salt_b64;
std::vector< std::string > allowed_apis;
};
struct api_access
{
std::map< std::string, api_access_info > permission_map;
};
} } // graphene::app
FC_REFLECT( graphene::app::api_access_info,
(password_hash_b64)
(password_salt_b64)
(allowed_apis)
)
FC_REFLECT( graphene::app::api_access,
(permission_map)
)

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/api_access.hpp>
#include <graphene/net/node.hpp>
#include <graphene/chain/database.hpp>
#include <boost/program_options.hpp>
namespace graphene { namespace app {
namespace detail { class application_impl; }
using std::string;
class abstract_plugin;
class application
{
public:
application();
~application();
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();
void shutdown_plugins();
template<typename PluginType>
std::shared_ptr<PluginType> register_plugin()
{
auto plug = std::make_shared<PluginType>();
plug->plugin_set_app(this);
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);
if( !plugin_cfg_options.options().empty() )
_cfg_options.add(plugin_cfg_options);
add_plugin( plug->plugin_name(), plug );
return plug;
}
std::shared_ptr<abstract_plugin> get_plugin( const string& name )const;
template<typename PluginType>
std::shared_ptr<PluginType> get_plugin( const string& name ) const
{
std::shared_ptr<abstract_plugin> abs_plugin = get_plugin( name );
std::shared_ptr<PluginType> result = std::dynamic_pointer_cast<PluginType>( abs_plugin );
FC_ASSERT( result != std::shared_ptr<PluginType>() );
return result;
}
net::node_ptr p2p_node();
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;
void set_api_access_info(const string& username, api_access_info&& permissions);
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;
boost::program_options::options_description _cli_options;
boost::program_options::options_description _cfg_options;
};
} }

View file

@ -0,0 +1,689 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/full_account.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <fc/api.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
#include <fc/network/ip.hpp>
#include <boost/container/flat_set.hpp>
#include <functional>
#include <map>
#include <memory>
#include <vector>
namespace graphene { namespace app {
using namespace graphene::chain;
using namespace graphene::market_history;
using namespace std;
class database_api_impl;
struct order
{
double price;
double quote;
double base;
};
struct order_book
{
string base;
string quote;
vector< order > bids;
vector< order > asks;
};
struct market_ticker
{
string base;
string quote;
double latest;
double lowest_ask;
double highest_bid;
double percent_change;
double base_volume;
double quote_volume;
};
struct market_volume
{
string base;
string quote;
double base_volume;
double quote_volume;
};
struct market_trade
{
fc::time_point_sec date;
double price;
double amount;
double value;
};
/**
* @brief The database_api class implements the RPC API for the chain database.
*
* 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_broadcast_api.
*/
class database_api
{
public:
database_api(graphene::chain::database& db);
~database_api();
/////////////
// Objects //
/////////////
/**
* @brief Get the objects corresponding to the provided IDs
* @param ids IDs of the objects to retrieve
* @return The objects retrieved, in the order they are mentioned in ids
*
* If any of the provided IDs does not map to an object, a null variant is returned in its position.
*/
fc::variants get_objects(const vector<object_id_type>& ids)const;
///////////////////
// Subscriptions //
///////////////////
void set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter );
void set_pending_transaction_callback( std::function<void(const variant&)> cb );
void set_block_applied_callback( std::function<void(const variant& block_id)> cb );
/**
* @brief Stop receiving any notifications
*
* This unsubscribes from all subscribed markets and objects.
*/
void cancel_all_subscriptions();
/////////////////////////////
// Blocks and transactions //
/////////////////////////////
/**
* @brief Retrieve a block header
* @param block_num Height of the block whose header should be returned
* @return header of the referenced block, or null if no matching block was found
*/
optional<block_header> get_block_header(uint32_t block_num)const;
/**
* @brief Retrieve a full, signed block
* @param block_num Height of the block to be returned
* @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;
/**
* If the transaction has not expired, this method will return the transaction for the given ID or
* it will return NULL if it is not known. Just because it is not known does not mean it wasn't
* included in the blockchain.
*/
optional<signed_transaction> get_recent_transaction_by_id( const transaction_id_type& id )const;
/////////////
// Globals //
/////////////
/**
* @brief Retrieve the @ref chain_property_object associated with the chain
*/
chain_property_object get_chain_properties()const;
/**
* @brief Retrieve the current @ref global_property_object
*/
global_property_object get_global_properties()const;
/**
* @brief Retrieve compile-time constants
*/
fc::variant_object get_config()const;
/**
* @brief Get the chain ID
*/
chain_id_type get_chain_id()const;
/**
* @brief Retrieve the current @ref dynamic_global_property_object
*/
dynamic_global_property_object get_dynamic_global_properties()const;
//////////
// Keys //
//////////
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
//////////////
// Accounts //
//////////////
/**
* @brief Get a list of accounts by ID
* @param account_ids IDs of the accounts to retrieve
* @return The accounts corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
/**
* @brief Fetch all objects relevant to the specified accounts and subscribe to updates
* @param callback Function to call with updates
* @param names_or_ids Each item must be the name or ID of an account to retrieve
* @return Map of string from @ref names_or_ids to the corresponding account
*
* This function fetches all relevant objects for the given accounts, and subscribes to updates to the given
* accounts. If any of the strings in @ref names_or_ids cannot be tied to an account, that input will be
* ignored. All other accounts will be retrieved and subscribed.
*
*/
std::map<string,full_account> get_full_accounts( const vector<string>& names_or_ids, bool subscribe );
optional<account_object> get_account_by_name( string name )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;
/**
* @brief Get a list of accounts by name
* @param account_names Names of the accounts to retrieve
* @return The accounts holding the provided names
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<account_object>> lookup_account_names(const vector<string>& account_names)const;
/**
* @brief Get names and IDs for registered accounts
* @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 account names to corresponding IDs
*/
map<string,account_id_type> lookup_accounts(const string& lower_bound_name, uint32_t limit)const;
//////////////
// Balances //
//////////////
/**
* @brief Get an account's balances in various assets
* @param id ID of the account to get balances for
* @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in
* @return Balances of the account
*/
vector<asset> get_account_balances(account_id_type id, const flat_set<asset_id_type>& assets)const;
/// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID.
vector<asset> get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets)const;
/** @return all unclaimed balance objects for a set of addresses */
vector<balance_object> get_balance_objects( const vector<address>& addrs )const;
vector<asset> get_vested_balances( const vector<balance_id_type>& objs )const;
vector<vesting_balance_object> get_vesting_balances( account_id_type account_id )const;
/**
* @brief Get the total number of accounts registered with the blockchain
*/
uint64_t get_account_count()const;
////////////
// Assets //
////////////
/**
* @brief Get a list of assets by ID
* @param asset_ids IDs of the assets to retrieve
* @return The assets corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<asset_object>> get_assets(const vector<asset_id_type>& asset_ids)const;
/**
* @brief Get assets alphabetically by symbol name
* @param lower_bound_symbol Lower bound of symbol names to retrieve
* @param limit Maximum number of assets to fetch (must not exceed 100)
* @return The assets found
*/
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
/**
* @brief Get a list of assets by symbol
* @param asset_symbols Symbols or stringified IDs of the assets to retrieve
* @return The assets corresponding to the provided symbols or IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
/////////////////////
// Markets / feeds //
/////////////////////
/**
* @brief Get limit orders in a given market
* @param a ID of asset being sold
* @param b ID of asset being purchased
* @param limit Maximum number of orders to retrieve
* @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 call orders in a given asset
* @param a ID of asset being called
* @param limit Maximum number of orders to retrieve
* @return The call orders, ordered from earliest to be called to latest
*/
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
/**
* @brief Get forced settlement orders in a given asset
* @param a ID of asset being settled
* @param limit Maximum number of orders to retrieve
* @return The settle orders, ordered from earliest settlement date to latest
*/
vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const;
/**
* @return all open margin positions for a given account id.
*/
vector<call_order_object> get_margin_positions( const account_id_type& id )const;
/**
* @brief Request notification when the active orders in the market between two assets changes
* @param callback Callback method which is called when the market changes
* @param a First asset ID
* @param b Second asset ID
*
* Callback will be passed a variant containing a vector<pair<operation, operation_result>>. The vector will
* contain, in order, the operations which changed the market, and their results.
*/
void subscribe_to_market(std::function<void(const variant&)> callback,
asset_id_type a, asset_id_type b);
/**
* @brief Unsubscribe from updates to a given market
* @param a First asset ID
* @param b Second asset ID
*/
void unsubscribe_from_market( asset_id_type a, asset_id_type b );
/**
* @brief Returns the ticker for the market assetA:assetB
* @param a String name of the first asset
* @param b String name of the second asset
* @return The market ticker for the past 24 hours.
*/
market_ticker get_ticker( const string& base, const string& quote )const;
/**
* @brief Returns the 24 hour volume for the market assetA:assetB
* @param a String name of the first asset
* @param b String name of the second asset
* @return The market volume over the past 24 hours
*/
market_volume get_24_volume( const string& base, const string& quote )const;
/**
* @brief Returns the order book for the market base:quote
* @param base String name of the first asset
* @param quote String name of the second asset
* @param depth of the order book. Up to depth of each asks and bids, capped at 50. Prioritizes most moderate of each
* @return Order book of the market
*/
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const;
/**
* @brief Returns recent trades for the market assetA:assetB
* Note: Currentlt, timezone offsets are not supported. The time must be UTC.
* @param a String name of the first asset
* @param b String name of the second asset
* @param stop Stop time as a UNIX timestamp
* @param limit Number of trasactions to retrieve, capped at 100
* @param start Start time as a UNIX timestamp
* @return Recent transactions in the market
*/
vector<market_trade> get_trade_history( const string& base, const string& quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit = 100 )const;
///////////////
// Witnesses //
///////////////
/**
* @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 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 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 the total number of witnesses registered with the blockchain
*/
uint64_t get_witness_count()const;
///////////////////////
// Committee members //
///////////////////////
/**
* @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;
/**
* @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 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;
/// WORKERS
/**
* Return the worker objects associated with this account.
*/
vector<worker_object> get_workers_by_account(account_id_type account)const;
///////////
// Votes //
///////////
/**
* @brief Given a set of votes, return the objects they are voting for.
*
* This will be a mixture of committee_member_object, witness_objects, and worker_objects
*
* The results will be in the same order as the votes. Null will be returned for
* any vote ids that are not found.
*/
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
////////////////////////////
// Authority / validation //
////////////////////////////
/// @brief Get a hexdump of the serialized binary form of a transaction
std::string get_transaction_hex(const signed_transaction& trx)const;
/**
* This API will take a partially signed transaction and a set of public keys that the owner has the ability to sign for
* and return the minimal subset of public keys that should add signatures to the transaction.
*/
set<public_key_type> get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const;
/**
* This method will return the set of all public keys that could possibly sign for a given transaction. This call can
* be used by wallets to filter their set of public keys to just the relevant subset prior to calling @ref get_required_signatures
* to get the minimum subset.
*/
set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;
set<address> get_potential_address_signatures( const signed_transaction& trx )const;
/**
* @return true of the @ref trx has all of the required signatures, otherwise throws an exception
*/
bool verify_authority( const signed_transaction& trx )const;
/**
* @return true if the signers have enough authority to authorize an account
*/
bool verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const;
/**
* Validates a transaction against the current state without broadcasting it on the network.
*/
processed_transaction validate_transaction( const signed_transaction& trx )const;
/**
* For each operation calculate the required fee in the specified asset type. If the asset type does
* not have a valid core_exchange_rate
*/
vector< fc::variant > get_required_fees( const vector<operation>& ops, asset_id_type id )const;
///////////////////////////
// Proposed transactions //
///////////////////////////
/**
* @return the set of proposed transactions relevant to the specified account id.
*/
vector<proposal_object> get_proposed_transactions( account_id_type id )const;
//////////////////////
// Blinded balances //
//////////////////////
/**
* @return the set of blinded balance objects by commitment ID
*/
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
/////////////////
// Tournaments //
/////////////////
/**
* @return the list of tournaments in the given state
*/
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
vector<tournament_object> get_tournaments(tournament_id_type stop = tournament_id_type(),
unsigned limit = 100,
tournament_id_type start = tournament_id_type());
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop = tournament_id_type(),
unsigned limit = 100,
tournament_id_type start = tournament_id_type(),
tournament_state state = tournament_state::accepting_registrations);
/**
* @return the list of tournaments that a given account is registered to play in
*/
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
private:
std::shared_ptr< database_api_impl > my;
};
} }
FC_REFLECT( graphene::app::order, (price)(quote)(base) );
FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );
FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) );
FC_API(graphene::app::database_api,
// Objects
(get_objects)
// Subscriptions
(set_subscribe_callback)
(set_pending_transaction_callback)
(set_block_applied_callback)
(cancel_all_subscriptions)
// Blocks and transactions
(get_block_header)
(get_block)
(get_transaction)
(get_recent_transaction_by_id)
// Globals
(get_chain_properties)
(get_global_properties)
(get_config)
(get_chain_id)
(get_dynamic_global_properties)
// Keys
(get_key_references)
// Accounts
(get_accounts)
(get_full_accounts)
(get_account_by_name)
(get_account_references)
(lookup_account_names)
(lookup_accounts)
(get_account_count)
// Balances
(get_account_balances)
(get_named_account_balances)
(get_balance_objects)
(get_vested_balances)
(get_vesting_balances)
// Assets
(get_assets)
(list_assets)
(lookup_asset_symbols)
// Markets / feeds
(get_order_book)
(get_limit_orders)
(get_call_orders)
(get_settle_orders)
(get_margin_positions)
(subscribe_to_market)
(unsubscribe_from_market)
(get_ticker)
(get_24_volume)
(get_trade_history)
// Witnesses
(get_witnesses)
(get_witness_by_account)
(lookup_witness_accounts)
(get_witness_count)
// Committee members
(get_committee_members)
(get_committee_member_by_account)
(lookup_committee_member_accounts)
// workers
(get_workers_by_account)
// Votes
(lookup_vote_ids)
// Authority / validation
(get_transaction_hex)
(get_required_signatures)
(get_potential_signatures)
(get_potential_address_signatures)
(verify_authority)
(verify_account_authority)
(validate_transaction)
(get_required_fees)
// Proposed transactions
(get_proposed_transactions)
// Blinded balances
(get_blinded_balances)
// Tournaments
(get_tournaments_in_state)
(get_tournaments_by_state)
(get_tournaments )
(get_registered_tournaments)
)

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
namespace graphene { namespace app {
using namespace graphene::chain;
struct full_account
{
account_object account;
account_statistics_object statistics;
string registrar_name;
string referrer_name;
string lifetime_referrer_name;
vector<variant> votes;
optional<vesting_balance_object> cashback_balance;
vector<account_balance_object> balances;
vector<vesting_balance_object> vesting_balances;
vector<limit_order_object> limit_orders;
vector<call_order_object> call_orders;
vector<proposal_object> proposals;
vector<pending_dividend_payout_balance_for_holder_object> pending_dividend_payments;
};
} }
FC_REFLECT( graphene::app::full_account,
(account)
(statistics)
(registrar_name)
(referrer_name)
(lifetime_referrer_name)
(votes)
(cashback_balance)
(balances)
(vesting_balances)
(limit_orders)
(call_orders)
(proposals)
(pending_dividend_payments)
)

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/container/flat.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/protocol/transaction.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace app {
void operation_get_impacted_accounts(
const graphene::chain::operation& op,
fc::flat_set<graphene::chain::account_id_type>& result );
void transaction_get_impacted_accounts(
const graphene::chain::transaction& tx,
fc::flat_set<graphene::chain::account_id_type>& result
);
} } // graphene::app

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/application.hpp>
#include <boost/program_options.hpp>
#include <fc/io/json.hpp>
namespace graphene { namespace app {
class abstract_plugin
{
public:
virtual ~abstract_plugin(){}
virtual std::string plugin_name()const = 0;
/**
* @brief Perform early startup routines and register plugin indexes, callbacks, etc.
*
* Plugins MUST supply a method initialize() which will be called early in the application startup. This method
* should contain early setup code such as initializing variables, adding indexes to the database, registering
* callback methods from the database, adding APIs, etc., as well as applying any options in the @ref options map
*
* This method is called BEFORE the database is open, therefore any routines which require any chain state MUST
* NOT be called by this method. These routines should be performed in startup() instead.
*
* @param options The options passed to the application, via configuration files or command line
*/
virtual void plugin_initialize( const boost::program_options::variables_map& options ) = 0;
/**
* @brief Begin normal runtime operations
*
* Plugins MUST supply a method startup() which will be called at the end of application startup. This method
* should contain code which schedules any tasks, or requires chain state.
*/
virtual void plugin_startup() = 0;
/**
* @brief Cleanly shut down the plugin.
*
* This is called to request a clean shutdown (e.g. due to SIGINT or SIGTERM).
*/
virtual void plugin_shutdown() = 0;
/**
* @brief Register the application instance with the plugin.
*
* This is called by the framework to set the application.
*/
virtual void plugin_set_app( application* a ) = 0;
/**
* @brief Fill in command line parameters used by the plugin.
*
* @param command_line_options All options this plugin supports taking on the command-line
* @param config_file_options All options this plugin supports storing in a configuration file
*
* This method populates its arguments with any
* command-line and configuration file options the plugin supports.
* If a plugin does not need these options, it
* may simply provide an empty implementation of this method.
*/
virtual void plugin_set_program_options(
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options
) = 0;
};
/**
* Provides basic default implementations of abstract_plugin functions.
*/
class plugin : public abstract_plugin
{
public:
plugin();
virtual ~plugin() override;
virtual std::string plugin_name()const 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(
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options
) override;
chain::database& database() { return *app().chain_database(); }
application& app()const { assert(_app); return *_app; }
protected:
net::node& p2p_node() { return *app().p2p_node(); }
private:
application* _app = nullptr;
};
/// @group Some useful tools for boost::program_options arguments using vectors of JSON strings
/// @{
template<typename T>
T dejsonify(const string& s)
{
return fc::json::from_string(s).as<T>();
}
#define DEFAULT_VALUE_VECTOR(value) default_value({fc::json::to_string(value)}, fc::json::to_string(value))
#define LOAD_VALUE_SET(options, name, container, type) \
if( options.count(name) ) { \
const std::vector<std::string>& ops = options[name].as<std::vector<std::string>>(); \
std::transform(ops.begin(), ops.end(), std::inserter(container, container.end()), &graphene::app::dejsonify<type>); \
}
/// @}
} } //graphene::app

75
libraries/app/plugin.cpp Normal file
View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/app/plugin.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
namespace graphene { namespace app {
plugin::plugin()
{
_app = nullptr;
return;
}
plugin::~plugin()
{
return;
}
std::string plugin::plugin_name()const
{
return "<unknown plugin>";
}
void plugin::plugin_initialize( const boost::program_options::variables_map& options )
{
return;
}
void plugin::plugin_startup()
{
return;
}
void plugin::plugin_shutdown()
{
return;
}
void plugin::plugin_set_app( application* app )
{
_app = app;
return;
}
void plugin::plugin_set_program_options(
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options
)
{
return;
}
} } // graphene::app

View file

@ -0,0 +1,116 @@
add_custom_target( build_hardfork_hpp
COMMAND cat-parts "${CMAKE_CURRENT_SOURCE_DIR}/hardfork.d" "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp" )
set_source_files_properties( "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp" PROPERTIES GENERATED TRUE )
add_dependencies( build_hardfork_hpp cat-parts )
file(GLOB HEADERS "include/graphene/chain/*.hpp")
if( GRAPHENE_DISABLE_UNITY_BUILD )
set( GRAPHENE_DB_FILES
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
)
message( STATUS "Graphene database unity build disabled" )
else( GRAPHENE_DISABLE_UNITY_BUILD )
set( GRAPHENE_DB_FILES
database.cpp )
message( STATUS "Graphene database unity build enabled" )
endif( GRAPHENE_DISABLE_UNITY_BUILD )
## SORT .cpp by most likely to change / break compile
add_library( graphene_chain
# As database takes the longest to compile, start it first
${GRAPHENE_DB_FILES}
fork_database.cpp
protocol/types.cpp
protocol/address.cpp
protocol/authority.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
protocol/confidential.cpp
protocol/vote.cpp
protocol/tournament.cpp
genesis_state.cpp
get_config.cpp
pts_address.cpp
evaluator.cpp
balance_evaluator.cpp
account_evaluator.cpp
assert_evaluator.cpp
witness_evaluator.cpp
committee_member_evaluator.cpp
asset_evaluator.cpp
transfer_evaluator.cpp
proposal_evaluator.cpp
market_evaluator.cpp
vesting_balance_evaluator.cpp
tournament_evaluator.cpp
tournament_object.cpp
match_object.cpp
game_object.cpp
withdraw_permission_evaluator.cpp
worker_evaluator.cpp
confidential_evaluator.cpp
special_authority.cpp
buyback.cpp
account_object.cpp
asset_object.cpp
fba_object.cpp
proposal_object.cpp
vesting_balance_object.cpp
block_database.cpp
is_authorized_asset.cpp
${HEADERS}
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
)
add_dependencies( graphene_chain build_hardfork_hpp )
target_link_libraries( graphene_chain fc graphene_db )
target_include_directories( graphene_chain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" )
if(MSVC)
set_source_files_properties( db_init.cpp db_block.cpp database.cpp block_database.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
INSTALL( TARGETS
graphene_chain
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -0,0 +1,409 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/buyback.hpp>
#include <graphene/chain/buyback_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/internal_exceptions.hpp>
#include <graphene/chain/special_authority.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <algorithm>
namespace graphene { namespace chain {
void verify_authority_accounts( const database& db, const authority& a )
{
const auto& chain_params = db.get_global_properties().parameters;
GRAPHENE_ASSERT(
a.num_auths() <= chain_params.maximum_authority_membership,
internal_verify_auth_max_auth_exceeded,
"Maximum authority membership exceeded" );
for( const auto& acnt : a.account_auths )
{
GRAPHENE_ASSERT( db.find_object( acnt.first ) != nullptr,
internal_verify_auth_account_not_found,
"Account ${a} specified in authority does not exist",
("a", acnt.first) );
}
}
void verify_account_votes( const database& db, const account_options& options )
{
// ensure account's votes satisfy requirements
// NB only the part of vote checking that requires chain state is here,
// the rest occurs in account_options::validate()
const auto& gpo = db.get_global_properties();
const auto& chain_params = gpo.parameters;
FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count,
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
uint32_t max_vote_id = gpo.next_available_vote_id;
bool has_worker_votes = false;
for( auto id : options.votes )
{
FC_ASSERT( id < max_vote_id );
has_worker_votes |= (id.type() == vote_id_type::worker);
}
if( has_worker_votes && (db.head_block_time() >= HARDFORK_607_TIME) )
{
const auto& against_worker_idx = db.get_index_type<worker_index>().indices().get<by_vote_against>();
for( auto id : options.votes )
{
if( id.type() == vote_id_type::worker )
{
FC_ASSERT( against_worker_idx.find( id ) == against_worker_idx.end() );
}
}
}
}
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try {
database& d = db();
if( d.head_block_time() < HARDFORK_516_TIME )
{
FC_ASSERT( !op.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
}
if( d.head_block_time() < HARDFORK_599_TIME )
{
FC_ASSERT( !op.extensions.value.null_ext.valid() );
FC_ASSERT( !op.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
FC_ASSERT( !op.extensions.value.buyback_options.valid() );
}
FC_ASSERT( d.find_object(op.options.voting_account), "Invalid proxy account specified." );
FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );
try
{
verify_authority_accounts( d, op.owner );
verify_authority_accounts( d, op.active );
}
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded )
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found )
if( op.extensions.value.owner_special_authority.valid() )
evaluate_special_authority( d, *op.extensions.value.owner_special_authority );
if( op.extensions.value.active_special_authority.valid() )
evaluate_special_authority( d, *op.extensions.value.active_special_authority );
if( op.extensions.value.buyback_options.valid() )
evaluate_buyback_account_options( d, *op.extensions.value.buyback_options );
verify_account_votes( d, op.options );
auto& acnt_indx = d.get_index_type<account_index>();
if( op.name.size() )
{
auto current_account_itr = acnt_indx.indices().get<by_name>().find( op.name );
FC_ASSERT( current_account_itr == acnt_indx.indices().get<by_name>().end() );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type account_create_evaluator::do_apply( const account_create_operation& o )
{ try {
database& d = db();
uint16_t referrer_percent = o.referrer_percent;
bool has_small_percent = (
(db().head_block_time() <= HARDFORK_453_TIME)
&& (o.referrer != o.registrar )
&& (o.referrer_percent != 0 )
&& (o.referrer_percent <= 0x100)
);
if( has_small_percent )
{
if( referrer_percent >= 100 )
{
wlog( "between 100% and 0x100%: ${o}", ("o", o) );
}
referrer_percent = referrer_percent*100;
if( referrer_percent > GRAPHENE_100_PERCENT )
referrer_percent = GRAPHENE_100_PERCENT;
}
const auto& new_acnt_object = db().create<account_object>( [&]( account_object& obj ){
obj.registrar = o.registrar;
obj.referrer = o.referrer;
obj.lifetime_referrer = o.referrer(db()).lifetime_referrer;
auto& params = db().get_global_properties().parameters;
obj.network_fee_percentage = params.network_percent_of_fee;
obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee;
obj.referrer_rewards_percentage = referrer_percent;
obj.name = o.name;
obj.owner = o.owner;
obj.active = o.active;
obj.options = o.options;
obj.statistics = db().create<account_statistics_object>([&](account_statistics_object& s){s.owner = obj.id;}).id;
if( o.extensions.value.owner_special_authority.valid() )
obj.owner_special_authority = *(o.extensions.value.owner_special_authority);
if( o.extensions.value.active_special_authority.valid() )
obj.active_special_authority = *(o.extensions.value.active_special_authority);
if( o.extensions.value.buyback_options.valid() )
{
obj.allowed_assets = o.extensions.value.buyback_options->markets;
obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy );
}
});
if( has_small_percent )
{
wlog( "Account affected by #453 registered in block ${n}: ${na} reg=${reg} ref=${ref}:${refp} ltr=${ltr}:${ltrp}",
("n", db().head_block_num()) ("na", new_acnt_object.id)
("reg", o.registrar) ("ref", o.referrer) ("ltr", new_acnt_object.lifetime_referrer)
("refp", new_acnt_object.referrer_rewards_percentage) ("ltrp", new_acnt_object.lifetime_referrer_fee_percentage) );
wlog( "Affected account object is ${o}", ("o", new_acnt_object) );
}
const auto& dynamic_properties = db().get_dynamic_global_properties();
db().modify(dynamic_properties, [](dynamic_global_property_object& p) {
++p.accounts_registered_this_interval;
});
const auto& global_properties = db().get_global_properties();
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->get<account_create_operation>().basic_fee <<= p.parameters.account_fee_scale_bitshifts;
});
if( o.extensions.value.owner_special_authority.valid()
|| o.extensions.value.active_special_authority.valid() )
{
db().create< special_authority_object >( [&]( special_authority_object& sa )
{
sa.account = new_acnt_object.id;
} );
}
if( o.extensions.value.buyback_options.valid() )
{
asset_id_type asset_to_buy = o.extensions.value.buyback_options->asset_to_buy;
d.create< buyback_object >( [&]( buyback_object& bo )
{
bo.asset_to_buy = asset_to_buy;
} );
d.modify( asset_to_buy(d), [&]( asset_object& a )
{
a.buyback_account = new_acnt_object.id;
} );
}
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();
if( d.head_block_time() < HARDFORK_516_TIME )
{
FC_ASSERT( !o.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
}
if( d.head_block_time() < HARDFORK_599_TIME )
{
FC_ASSERT( !o.extensions.value.null_ext.valid() );
FC_ASSERT( !o.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
}
try
{
if( o.owner ) verify_authority_accounts( d, *o.owner );
if( o.active ) verify_authority_accounts( d, *o.active );
}
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded )
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found )
if( o.extensions.value.owner_special_authority.valid() )
evaluate_special_authority( d, *o.extensions.value.owner_special_authority );
if( o.extensions.value.active_special_authority.valid() )
evaluate_special_authority( d, *o.extensions.value.active_special_authority );
acnt = &o.account(d);
if( o.new_options.valid() )
verify_account_votes( d, *o.new_options );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result account_update_evaluator::do_apply( const account_update_operation& o )
{ try {
database& d = db();
bool sa_before, sa_after;
d.modify( *acnt, [&](account_object& a){
if( o.owner )
{
a.owner = *o.owner;
a.top_n_control_flags = 0;
}
if( o.active )
{
a.active = *o.active;
a.top_n_control_flags = 0;
}
if( o.new_options ) a.options = *o.new_options;
sa_before = a.has_special_authority();
if( o.extensions.value.owner_special_authority.valid() )
{
a.owner_special_authority = *(o.extensions.value.owner_special_authority);
a.top_n_control_flags = 0;
}
if( o.extensions.value.active_special_authority.valid() )
{
a.active_special_authority = *(o.extensions.value.active_special_authority);
a.top_n_control_flags = 0;
}
sa_after = a.has_special_authority();
});
if( sa_before & (!sa_after) )
{
const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get<by_account>();
auto sa_it = sa_idx.find( o.account );
assert( sa_it != sa_idx.end() );
d.remove( *sa_it );
}
else if( (!sa_before) & sa_after )
{
d.create< special_authority_object >( [&]( special_authority_object& sa )
{
sa.account = o.account;
} );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_operation& o)
{ try {
database& d = db();
listed_account = &o.account_to_list(d);
if( !d.get_global_properties().parameters.allow_non_member_whitelists )
FC_ASSERT(o.authorizing_account(d).is_lifetime_member());
return void_result();
} 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) {
if( o.new_listing & o.white_listed )
a.whitelisting_accounts.insert(o.authorizing_account);
else
a.whitelisting_accounts.erase(o.authorizing_account);
if( o.new_listing & o.black_listed )
a.blacklisting_accounts.insert(o.authorizing_account);
else
a.blacklisting_accounts.erase(o.authorizing_account);
});
/** for tracking purposes only, this state is not needed to evaluate */
d.modify( o.authorizing_account(d), [&]( account_object& a ) {
if( o.new_listing & o.white_listed )
a.whitelisted_accounts.insert( o.account_to_list );
else
a.whitelisted_accounts.erase( o.account_to_list );
if( o.new_listing & o.black_listed )
a.blacklisted_accounts.insert( o.account_to_list );
else
a.blacklisted_accounts.erase( o.account_to_list );
});
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;
} else if( a.is_annual_member(d.head_block_time()) ) {
// Renew an annual subscription that's still in effect.
FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650),
"May not extend annual membership more than a decade into the future.");
a.membership_expiration_date += fc::days(365);
} else {
// Upgrade from basic account.
FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
a.statistics(d).process_fees(a, d);
assert(a.is_basic_account(d.head_block_time()));
a.referrer = a.get_id();
a.membership_expiration_date = d.head_block_time() + fc::days(365);
}
});
return {};
} FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
} } // graphene::chain

View file

@ -0,0 +1,270 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/hardfork.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();
}
void account_balance_object::adjust_balance(const asset& delta)
{
assert(delta.asset_id == asset_type);
balance += delta.amount;
}
void account_statistics_object::process_fees(const account_object& a, database& d) const
{
if( pending_fees > 0 || pending_vested_fees > 0 )
{
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
const auto& props = d.get_global_properties();
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 );
};
pay_out_fees(a, pending_fees, true);
pay_out_fees(a, pending_vested_fees, false);
d.modify(*this, [&](account_statistics_object& s) {
s.lifetime_fees_paid += pending_fees + pending_vested_fees;
s.pending_fees = 0;
s.pending_vested_fees = 0;
});
}
}
void account_statistics_object::pay_fee( share_type core_fee, share_type cashback_vesting_threshold )
{
if( core_fee > cashback_vesting_threshold )
pending_fees += core_fee;
else
pending_vested_fees += core_fee;
}
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);
for( auto auth : a.active.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);
for( auto auth : a.active.key_auths )
result.insert(auth.first);
result.insert( a.options.memo_key );
return result;
}
set<address> account_member_index::get_address_members(const account_object& a)const
{
set<address> result;
for( auto auth : a.owner.address_auths )
result.insert(auth.first);
for( auto auth : a.active.address_auths )
result.insert(auth.first);
result.insert( a.options.memo_key );
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);
auto address_members = get_address_members(a);
for( auto item : address_members )
account_to_address_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 address_members = get_address_members(a);
for( auto item : address_members )
account_to_address_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_address_members = get_address_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);
}
{
set<address> after_address_members = get_address_members(a);
vector<address> removed; removed.reserve(before_address_members.size());
std::set_difference(before_address_members.begin(), before_address_members.end(),
after_address_members.begin(), after_address_members.end(),
std::inserter(removed, removed.end()));
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_address_memberships[*itr].erase(after.id);
vector<address> added; added.reserve(after_address_members.size());
std::set_difference(after_address_members.begin(), after_address_members.end(),
before_address_members.begin(), before_address_members.end(),
std::inserter(added, added.end()));
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_address_memberships[*itr].insert(after.id);
}
}
void account_referrer_index::object_inserted( const object& obj )
{
}
void account_referrer_index::object_removed( const object& obj )
{
}
void account_referrer_index::about_to_modify( const object& before )
{
}
void account_referrer_index::object_modified( const object& after )
{
}
} } // graphene::chain

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/assert_evaluator.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/database.hpp>
#include <sstream>
namespace graphene { namespace chain {
struct predicate_evaluator
{
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 operator()( const block_id_predicate& p )const
{
FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id );
}
};
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();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result assert_evaluator::do_apply( const assert_operation& o )
{ try {
// assert_operation is always a no-op
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -0,0 +1,651 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/asset_evaluator.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <functional>
#include <boost/algorithm/string/case_conv.hpp>
namespace graphene { namespace chain {
void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op )
{ try {
database& d = db();
const auto& chain_parameters = d.get_global_properties().parameters;
FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
// Check that all authorities do exist
for( auto id : op.common_options.whitelist_authorities )
d.get_object(id);
for( auto id : op.common_options.blacklist_authorities )
d.get_object(id);
auto& asset_indx = d.get_index_type<asset_index>().indices().get<by_symbol>();
auto asset_symbol_itr = asset_indx.find( op.symbol );
FC_ASSERT( asset_symbol_itr == asset_indx.end() );
if( d.head_block_time() > HARDFORK_385_TIME )
{
if( d.head_block_time() <= HARDFORK_409_TIME )
{
auto dotpos = op.symbol.find( '.' );
if( dotpos != std::string::npos )
{
auto prefix = op.symbol.substr( 0, dotpos );
auto asset_symbol_itr = asset_indx.find( op.symbol );
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
("s",op.symbol)("p",prefix) );
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
}
}
else
{
auto dotpos = op.symbol.rfind( '.' );
if( dotpos != std::string::npos )
{
auto prefix = op.symbol.substr( 0, dotpos );
auto asset_symbol_itr = asset_indx.find( prefix );
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
("s",op.symbol)("p",prefix) );
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
}
}
}
else
{
auto dotpos = op.symbol.find( '.' );
if( dotpos != std::string::npos )
wlog( "Asset ${s} has a name which requires hardfork 385", ("s",op.symbol) );
}
core_fee_paid -= core_fee_paid.value/2;
if( op.bitasset_opts )
{
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.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 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 = core_fee_paid; //op.calculate_fee(db().current_fee_schedule()).value / 2;
});
asset_bitasset_data_id_type bit_asset_id;
if( op.bitasset_opts.valid() )
bit_asset_id = db().create<asset_bitasset_data_object>( [&]( asset_bitasset_data_object& a ) {
a.options = *op.bitasset_opts;
a.is_prediction_market = op.is_prediction_market;
}).id;
auto next_asset_id = db().get_index_type<asset_index>().get_next_id();
const asset_object& new_asset =
db().create<asset_object>( [&]( asset_object& a ) {
a.issuer = op.issuer;
a.symbol = op.symbol;
a.precision = op.precision;
a.options = op.common_options;
if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 )
a.options.core_exchange_rate.quote.asset_id = next_asset_id;
else
a.options.core_exchange_rate.base.asset_id = next_asset_id;
a.dynamic_asset_data_id = dyn_asset.id;
if( op.bitasset_opts.valid() )
a.bitasset_data_id = bit_asset_id;
});
assert( new_asset.id == 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 {
const database& d = db();
const asset_object& a = o.asset_to_issue.asset_id(d);
FC_ASSERT( o.issuer == a.issuer );
FC_ASSERT( !a.is_market_issued(), "Cannot manually issue a market-issued asset." );
to_account = &o.issue_to_account(d);
FC_ASSERT( is_authorized_asset( d, *to_account, a ) );
asset_dyn_data = &a.dynamic_asset_data_id(d);
FC_ASSERT( (asset_dyn_data->current_supply + o.asset_to_issue.amount) <= a.options.max_supply );
return void_result();
} 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 ){
data.current_supply += o.asset_to_issue.amount;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& o )
{ try {
const database& d = db();
const asset_object& a = o.amount_to_reserve.asset_id(d);
GRAPHENE_ASSERT(
!a.is_market_issued(),
asset_reserve_invalid_on_mia,
"Cannot reserve ${sym} because it is a market-issued asset",
("sym", a.symbol)
);
from_account = &o.payer(d);
FC_ASSERT( is_authorized_asset( d, *from_account, a ) );
asset_dyn_data = &a.dynamic_asset_data_id(d);
FC_ASSERT( (asset_dyn_data->current_supply - o.amount_to_reserve.amount) >= 0 );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
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_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 {
database& d = db();
const asset_object& a = o.asset_id(d);
asset_dyn_data = &a.dynamic_asset_data_id(d);
return void_result();
} 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 ) {
data.fee_pool += o.amount;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
{ try {
database& d = db();
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.");
}
}
if( (d.head_block_time() < HARDFORK_572_TIME) || (a.dynamic_asset_data_id(d).current_supply != 0) )
{
// new issuer_permissions must be subset of old issuer permissions
FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions),
"Cannot reinstate previously revoked issuer permissions on an asset.");
}
// changed flags must be subset of old issuer permissions
FC_ASSERT(!((o.new_options.flags ^ a.options.flags) & ~a.options.issuer_permissions),
"Flag change is forbidden by issuer permissions");
asset_to_update = &a;
FC_ASSERT( o.issuer == a.issuer, "", ("o.issuer", o.issuer)("a.issuer", a.issuer) );
const auto& chain_parameters = d.get_global_properties().parameters;
FC_ASSERT( o.new_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
for( auto id : o.new_options.whitelist_authorities )
d.get_object(id);
FC_ASSERT( o.new_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
for( auto id : o.new_options.blacklist_authorities )
d.get_object(id);
return void_result();
} 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
if( o.new_options.flags & disable_force_settle && asset_to_update->can_force_settle() )
{
const auto& idx = d.get_index_type<force_settlement_index>().indices().get<by_expiration>();
// Funky iteration code because we're removing objects as we go. We have to re-initialize itr every loop instead
// of simply incrementing it.
for( auto itr = idx.lower_bound(o.asset_to_update);
itr != idx.end() && itr->settlement_asset_id() == o.asset_to_update;
itr = idx.lower_bound(o.asset_to_update) )
d.cancel_order(*itr);
}
d.modify(*asset_to_update, [&](asset_object& a) {
if( o.new_issuer )
a.issuer = *o.new_issuer;
a.options = o.new_options;
});
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);
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)
{ 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_dividend_evaluator::do_evaluate(const asset_update_dividend_operation& o)
{ try {
database& d = db();
const asset_object& a = o.asset_to_update(d);
asset_to_update = &a;
FC_ASSERT( o.issuer == a.issuer, "", ("o.issuer", o.issuer)("a.issuer", a.issuer) );
auto& params = db().get_global_properties().parameters;
if (o.new_options.payout_interval &&
*o.new_options.payout_interval < params.maintenance_interval)
FC_THROW("New payout interval may not be less than the maintenance interval",
("new_payout_interval", o.new_options.payout_interval)("maintenance_interval", params.maintenance_interval));
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_update_dividend_evaluator::do_apply( const asset_update_dividend_operation& op )
{ try {
database& d = db();
if (!asset_to_update->dividend_data_id)
{
// this was not a dividend-paying asset, we're converting it to a dividend-paying asset
std::string dividend_distribution_account_name(boost::to_lower_copy(asset_to_update->symbol) + "-dividend-distribution");
const auto& new_acnt_object = db().create<account_object>( [&]( account_object& obj ){
obj.registrar = op.issuer;
obj.referrer = op.issuer;
obj.lifetime_referrer = op.issuer(db()).lifetime_referrer;
auto& params = db().get_global_properties().parameters;
obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
obj.lifetime_referrer_fee_percentage = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE;
obj.referrer_rewards_percentage = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE;
obj.name = dividend_distribution_account_name;
obj.owner.weight_threshold = 1;
obj.active.weight_threshold = 1;
obj.statistics = db().create<account_statistics_object>([&](account_statistics_object& s){s.owner = obj.id;}).id;
});
const asset_dividend_data_object& dividend_data = d.create<asset_dividend_data_object>( [&]( asset_dividend_data_object& dividend_data_obj ) {
dividend_data_obj.options = op.new_options;
dividend_data_obj.dividend_distribution_account = new_acnt_object.id;
});
d.modify(*asset_to_update, [&](asset_object& a) {
a.dividend_data_id = dividend_data.id;
});
}
else
{
const asset_dividend_data_object& dividend_data = asset_to_update->dividend_data(d);
d.modify(dividend_data, [&]( asset_dividend_data_object& dividend_data_obj ) {
dividend_data_obj.options = op.new_options;
// whenever new options are set, clear out the scheduled payout/distribution times
// this will reset and cause the next distribution to happen at the next maintenance
// interval and a payout at the next_payout_time
dividend_data_obj.last_scheduled_payout_time.reset();
dividend_data_obj.last_scheduled_distribution_time.reset();
});
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
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 );
for( auto id : o.new_feed_producers )
d.get_object(id);
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.options.flags & committee_fed_asset), "Cannot set feed producers on a committee-fed asset.");
FC_ASSERT(!(a.options.flags & witness_fed_asset), "Cannot set feed producers on a witness-fed 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
//publishers who are being kept.
//First, remove any old publishers who are no longer publishers
for( auto itr = a.feeds.begin(); itr != a.feeds.end(); )
{
if( !o.new_feed_producers.count(itr->first) )
itr = a.feeds.erase(itr);
else
++itr;
}
//Now, add any new publishers
for( auto itr = o.new_feed_producers.begin(); itr != o.new_feed_producers.end(); ++itr )
if( !a.feeds.count(*itr) )
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());
FC_ASSERT(asset_to_settle->can_global_settle());
FC_ASSERT(asset_to_settle->issuer == op.issuer );
FC_ASSERT(asset_to_settle->dynamic_data(d).current_supply > 0);
const auto& idx = d.get_index_type<call_order_index>().indices().get<by_collateral>();
assert( !idx.empty() );
auto itr = idx.lower_bound(boost::make_tuple(price::min(asset_to_settle->bitasset_data(d).options.short_backing_asset,
op.asset_to_settle)));
assert( itr != idx.end() && itr->debt_type() == op.asset_to_settle );
const call_order_object& least_collateralized_short = *itr;
FC_ASSERT(least_collateralized_short.get_debt() * op.settle_price <= least_collateralized_short.get_collateral(),
"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) ) }
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());
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 void_result();
} FC_CAPTURE_AND_RETHROW( (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);
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& 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(base.is_market_issued());
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 );
if( d.head_block_time() > HARDFORK_480_TIME )
{
if( !o.feed.core_exchange_rate.is_null() )
{
FC_ASSERT( o.feed.core_exchange_rate.quote.asset_id == asset_id_type() );
}
}
else
{
if( (!o.feed.settlement_price.is_null()) && (!o.feed.core_exchange_rate.is_null()) )
{
FC_ASSERT( o.feed.settlement_price.quote.asset_id == o.feed.core_exchange_rate.quote.asset_id );
}
}
//Verify that the publisher is authoritative to publish a feed
if( base.options.flags & witness_fed_asset )
{
FC_ASSERT( d.get(GRAPHENE_WITNESS_ACCOUNT).active.account_auths.count(o.publisher) );
}
else if( base.options.flags & committee_fed_asset )
{
FC_ASSERT( d.get(GRAPHENE_COMMITTEE_ACCOUNT).active.account_auths.count(o.publisher) );
}
else
{
FC_ASSERT(bitasset.feeds.count(o.publisher));
}
return void_result();
} FC_CAPTURE_AND_RETHROW((o)) }
void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_operation& o)
{ try {
database& d = db();
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(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)) }
void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o )
{ try {
FC_ASSERT( db().head_block_time() > HARDFORK_413_TIME );
FC_ASSERT( o.amount_to_claim.asset_id(db()).issuer == o.issuer, "Asset fees may only be claimed by the issuer" );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result asset_claim_fees_evaluator::do_apply( const asset_claim_fees_operation& o )
{ try {
database& d = db();
const asset_object& a = o.amount_to_claim.asset_id(d);
const asset_dynamic_data_object& addo = a.dynamic_asset_data_id(d);
FC_ASSERT( o.amount_to_claim.amount <= addo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("addo",addo) );
d.modify( addo, [&]( asset_dynamic_data_object& _addo ) {
_addo.accumulated_fees -= o.amount_to_claim.amount;
});
d.adjust_balance( o.issuer, o.amount_to_claim );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <fc/uint128.hpp>
#include <cmath>
using namespace graphene::chain;
share_type asset_bitasset_data_object::max_force_settlement_volume(share_type current_supply) const
{
if( options.maximum_force_settlement_volume == 0 )
return 0;
if( options.maximum_force_settlement_volume == GRAPHENE_100_PERCENT )
return current_supply + force_settled_volume;
fc::uint128 volume = current_supply.value + force_settled_volume.value;
volume *= options.maximum_force_settlement_volume;
volume /= GRAPHENE_100_PERCENT;
return volume.to_uint64();
}
void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point_sec current_time)
{
current_feed_publication_time = current_time;
vector<std::reference_wrapper<const price_feed>> current_feeds;
for( const pair<account_id_type, pair<time_point_sec,price_feed>>& f : feeds )
{
if( (current_time - f.second.first).to_seconds() < options.feed_lifetime_sec &&
f.second.first != time_point_sec() )
{
current_feeds.emplace_back(f.second.second);
current_feed_publication_time = std::min(current_feed_publication_time, f.second.first);
}
}
// 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;
}
if( current_feeds.size() == 1 )
{
current_feed = std::move(current_feeds.front());
return;
}
// *** Begin Median Calculations ***
price_feed median_feed;
const auto median_itr = current_feeds.begin() + current_feeds.size() / 2;
#define CALCULATE_MEDIAN_VALUE(r, data, field_name) \
std::nth_element( current_feeds.begin(), median_itr, current_feeds.end(), \
[](const price_feed& a, const price_feed& b) { \
return a.field_name < b.field_name; \
}); \
median_feed.field_name = median_itr->get().field_name;
BOOST_PP_SEQ_FOR_EACH( CALCULATE_MEDIAN_VALUE, ~, GRAPHENE_PRICE_FEED_FIELDS )
#undef CALCULATE_MEDIAN_VALUE
// *** End Median Calculations ***
current_feed = median_feed;
}
asset asset_object::amount_from_string(string amount_string) const
{ try {
bool negative_found = false;
bool decimal_found = false;
for( const char c : amount_string )
{
if( isdigit( c ) )
continue;
if( c == '-' && !negative_found )
{
negative_found = true;
continue;
}
if( c == '.' && !decimal_found )
{
decimal_found = true;
continue;
}
FC_THROW( (amount_string) );
}
share_type satoshis = 0;
share_type scaled_precision = asset::scaled_precision( precision );
const auto decimal_pos = amount_string.find( '.' );
const string lhs = amount_string.substr( negative_found, decimal_pos );
if( !lhs.empty() )
satoshis += fc::safe<int64_t>(std::stoll(lhs)) *= scaled_precision;
if( decimal_found )
{
const size_t max_rhs_size = std::to_string( scaled_precision.value ).substr( 1 ).size();
string rhs = amount_string.substr( decimal_pos + 1 );
FC_ASSERT( rhs.size() <= max_rhs_size );
while( rhs.size() < max_rhs_size )
rhs += '0';
if( !rhs.empty() )
satoshis += std::stoll( rhs );
}
FC_ASSERT( satoshis <= GRAPHENE_MAX_SHARE_SUPPLY );
if( negative_found )
satoshis *= -1;
return amount(satoshis);
} FC_CAPTURE_AND_RETHROW( (amount_string) ) }
string asset_object::amount_to_string(share_type amount) const
{
share_type scaled_precision = 1;
for( uint8_t i = 0; i < precision; ++i )
scaled_precision *= 10;
assert(scaled_precision > 0);
string result = fc::to_string(amount.value / scaled_precision.value);
auto decimals = amount.value % scaled_precision.value;
if( decimals )
result += "." + fc::to_string(scaled_precision.value + decimals).erase(0,1);
return result;
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/balance_evaluator.hpp>
namespace graphene { namespace chain {
void_result balance_claim_evaluator::do_evaluate(const balance_claim_operation& op)
{
database& d = db();
balance = &op.balance_to_claim(d);
GRAPHENE_ASSERT(
op.balance_owner_key == balance->owner ||
pts_address(op.balance_owner_key, false, 56) == balance->owner ||
pts_address(op.balance_owner_key, true, 56) == balance->owner ||
pts_address(op.balance_owner_key, false, 0) == balance->owner ||
pts_address(op.balance_owner_key, true, 0) == balance->owner,
balance_claim_owner_mismatch,
"Balance owner key was specified as '${op}' but balance's actual owner is '${bal}'",
("op", op.balance_owner_key)
("bal", balance->owner)
);
if( !(d.get_node_properties().skip_flags & (database::skip_authority_check |
database::skip_transaction_signatures)) )
FC_ASSERT(op.total_claimed.asset_id == balance->asset_type());
if( balance->is_vesting_balance() )
{
GRAPHENE_ASSERT(
balance->vesting_policy->is_withdraw_allowed(
{ balance->balance,
d.head_block_time(),
op.total_claimed } ),
balance_claim_invalid_claim_amount,
"Attempted to claim ${c} from a vesting balance with ${a} available",
("c", op.total_claimed)("a", balance->available(d.head_block_time()))
);
GRAPHENE_ASSERT(
d.head_block_time() - balance->last_claim_date >= fc::days(1),
balance_claim_claimed_too_often,
"Genesis vesting balances may not be claimed more than once per day."
);
return {};
}
FC_ASSERT(op.total_claimed == balance->balance);
return {};
}
/**
* @note the fee is always 0 for this particular operation because once the
* balance is claimed it frees up memory and it cannot be used to spam the network
*/
void_result balance_claim_evaluator::do_apply(const balance_claim_operation& op)
{
database& d = db();
if( balance->is_vesting_balance() && op.total_claimed < balance->balance )
d.modify(*balance, [&](balance_object& b) {
b.vesting_policy->on_withdraw({b.balance, d.head_block_time(), op.total_claimed});
b.balance -= op.total_claimed;
b.last_claim_date = d.head_block_time();
});
else
d.remove(*balance);
d.adjust_balance(op.deposit_to_account, op.total_claimed);
return {};
}
} } // namespace graphene::chain

View file

@ -0,0 +1,282 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/block_database.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/io/raw.hpp>
#include <fc/smart_ref_impl.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 )
{
block_id_type id = _id;
if( id == block_id_type() )
{
id = b.id();
elog( "id argument of block_database::store() was not initialized for block ${id}", ("id", id) );
}
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
{
if( id == block_id_type() )
return false;
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 && e.block_size > 0;
}
block_id_type block_database::fetch_block_id( uint32_t block_num )const
{
assert( block_num != 0 );
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() <= int64_t(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) );
FC_ASSERT( e.block_id != block_id_type(), "Empty block_id in block_database (maybe corrupt on disk?)" );
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 );
if (e.block_size)
_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) );
uint64_t pos = _block_num_to_pos.tellg();
while( e.block_size == 0 && pos > 0 )
{
pos -= sizeof(index_entry);
_block_num_to_pos.seekg( pos );
_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>();
}
optional<block_id_type> block_database::last_id()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<block_id_type>();
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
uint64_t pos = _block_num_to_pos.tellg();
while( e.block_size == 0 && pos > 0 )
{
pos -= sizeof(index_entry);
_block_num_to_pos.seekg( pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
}
if( e.block_size == 0 )
return optional<block_id_type>();
return e.block_id;
}
catch (const fc::exception&)
{
}
catch (const std::exception&)
{
}
return optional<block_id_type>();
}
} }

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/protocol/buyback.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
void evaluate_buyback_account_options( const database& db, const buyback_account_options& bbo )
{
FC_ASSERT( db.head_block_time() >= HARDFORK_538_TIME );
const asset_object& a = bbo.asset_to_buy(db);
GRAPHENE_ASSERT( a.issuer == bbo.asset_to_buy_issuer,
account_create_buyback_incorrect_issuer, "Incorrect asset issuer specified in buyback_account_options", ("asset", a)("bbo", bbo) );
GRAPHENE_ASSERT( !a.buyback_account.valid(),
account_create_buyback_already_exists, "Cannot create buyback for asset which already has buyback", ("asset", a)("bbo", bbo) );
// TODO: Replace with chain parameter #554
GRAPHENE_ASSERT( bbo.markets.size() < GRAPHENE_DEFAULT_MAX_BUYBACK_MARKETS,
account_create_buyback_too_many_markets, "Too many buyback markets", ("asset", a)("bbo", bbo) );
}
} }

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#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/protocol/vote.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
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 = get_next_vote_id(p, vote_id_type::committee);
});
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_evaluator::do_evaluate( const committee_member_update_operation& op )
{ try {
FC_ASSERT(db().get(op.committee_member).committee_member_account == op.committee_member_account);
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result committee_member_update_evaluator::do_apply( const committee_member_update_operation& op )
{ try {
database& _db = db();
_db.modify(
_db.get(op.committee_member),
[&]( committee_member_object& com )
{
if( op.new_url.valid() )
com.url = *op.new_url;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result committee_member_update_global_parameters_evaluator::do_evaluate(const committee_member_update_global_parameters_operation& o)
{ try {
FC_ASSERT(trx_state->_is_proposed_trx);
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result committee_member_update_global_parameters_evaluator::do_apply(const committee_member_update_global_parameters_operation& o)
{ try {
db().modify(db().get_global_properties(), [&o](global_property_object& p) {
p.pending_parameters = o.new_parameters;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
{ try {
const auto& d = db();
const auto& atype = o.amount.asset_id(db());
FC_ASSERT( atype.allow_confidential() );
FC_ASSERT( !atype.is_transfer_restricted() );
FC_ASSERT( !(atype.options.flags & white_list) );
for( const auto& out : o.outputs )
{
for( const auto& a : out.owner.account_auths )
a.first(d); // verify all accounts exist and are valid
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o )
{ try {
db().adjust_balance( o.from, -o.amount );
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
db().modify( add, [&]( asset_dynamic_data_object& obj ){
obj.confidential_supply += o.amount.amount;
FC_ASSERT( obj.confidential_supply >= 0 );
});
for( const auto& out : o.outputs )
{
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
obj.asset_id = o.amount.asset_id;
obj.owner = out.owner;
obj.commitment = out.commitment;
});
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void transfer_to_blind_evaluator::pay_fee()
{
if( db().head_block_time() >= HARDFORK_563_TIME )
pay_fba_fee( fba_accumulator_id_transfer_to_blind );
else
generic_evaluator::pay_fee();
}
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
{ try {
const auto& d = db();
o.fee.asset_id(d); // verify fee is a legit asset
const auto& bbi = d.get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
FC_ASSERT( itr != cidx.end() );
FC_ASSERT( itr->asset_id == o.fee.asset_id );
FC_ASSERT( itr->owner == in.owner );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o )
{ try {
db().adjust_balance( o.fee_payer(), o.fee );
db().adjust_balance( o.to, o.amount );
const auto& bbi = db().get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
FC_ASSERT( itr != cidx.end() );
db().remove( *itr );
}
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
db().modify( add, [&]( asset_dynamic_data_object& obj ){
obj.confidential_supply -= o.amount.amount + o.fee.amount;
FC_ASSERT( obj.confidential_supply >= 0 );
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void transfer_from_blind_evaluator::pay_fee()
{
if( db().head_block_time() >= HARDFORK_563_TIME )
pay_fba_fee( fba_accumulator_id_transfer_from_blind );
else
generic_evaluator::pay_fee();
}
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
{ try {
const auto& d = db();
o.fee.asset_id(db()); // verify fee is a legit asset
const auto& bbi = db().get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& out : o.outputs )
{
for( const auto& a : out.owner.account_auths )
a.first(d); // verify all accounts exist and are valid
}
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
FC_ASSERT( itr->asset_id == o.fee.asset_id );
FC_ASSERT( itr->owner == in.owner );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o )
{ try {
db().adjust_balance( o.fee_payer(), o.fee ); // deposit the fee to the temp account
const auto& bbi = db().get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
db().remove( *itr );
}
for( const auto& out : o.outputs )
{
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
obj.asset_id = o.fee.asset_id;
obj.owner = out.owner;
obj.commitment = out.commitment;
});
}
const auto& add = o.fee.asset_id(db()).dynamic_asset_data_id(db());
db().modify( add, [&]( asset_dynamic_data_object& obj ){
obj.confidential_supply -= o.fee.amount;
FC_ASSERT( obj.confidential_supply >= 0 );
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void blind_transfer_evaluator::pay_fee()
{
if( db().head_block_time() >= HARDFORK_563_TIME )
pay_fba_fee( fba_accumulator_id_blind_transfer );
else
generic_evaluator::pay_fee();
}
} } // graphene::chain

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fc/smart_ref_impl.hpp>
#include "db_balance.cpp"
#include "db_block.cpp"
#include "db_debug.cpp"
#include "db_getter.cpp"
#include "db_init.cpp"
#include "db_maint.cpp"
#include "db_management.cpp"
#include "db_market.cpp"
#include "db_update.cpp"
#include "db_witness_schedule.cpp"

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
namespace graphene { namespace chain {
asset database::get_balance(account_id_type owner, asset_id_type asset_id) const
{
auto& index = get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto itr = index.find(boost::make_tuple(owner, asset_id));
if( itr == index.end() )
return asset(0, asset_id);
return itr->get_balance();
}
asset database::get_balance(const account_object& owner, const asset_object& asset_obj) const
{
return get_balance(owner.get_id(), asset_obj.get_id());
}
string database::to_pretty_string( const asset& a )const
{
return a.asset_id(*this).amount_to_pretty_string(a.amount);
}
void database::adjust_balance(account_id_type account, asset delta )
{ try {
if( delta.amount == 0 )
return;
auto& index = get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto itr = index.find(boost::make_tuple(account, delta.asset_id));
if(itr == index.end())
{
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 {
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);
});
}
} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }
optional< vesting_balance_id_type > database::deposit_lazy_vesting(
const optional< vesting_balance_id_type >& ovbid,
share_type amount, uint32_t req_vesting_seconds,
account_id_type req_owner,
bool require_vesting )
{
if( amount == 0 )
return optional< vesting_balance_id_type >();
fc::time_point_sec now = head_block_time();
while( true )
{
if( !ovbid.valid() )
break;
const vesting_balance_object& vbo = (*ovbid)(*this);
if( vbo.owner != req_owner )
break;
if( vbo.policy.which() != vesting_policy::tag< cdd_vesting_policy >::value )
break;
if( vbo.policy.get< cdd_vesting_policy >().vesting_seconds != req_vesting_seconds )
break;
modify( vbo, [&]( vesting_balance_object& _vbo )
{
if( require_vesting )
_vbo.deposit(now, amount);
else
_vbo.deposit_vested(now, amount);
} );
return optional< vesting_balance_id_type >();
}
const vesting_balance_object& vbo = create< vesting_balance_object >( [&]( vesting_balance_object& _vbo )
{
_vbo.owner = req_owner;
_vbo.balance = amount;
cdd_vesting_policy policy;
policy.vesting_seconds = req_vesting_seconds;
policy.coin_seconds_earned = require_vesting ? 0 : amount.value * policy.vesting_seconds;
policy.coin_seconds_earned_last_update = now;
_vbo.policy = policy;
} );
return vbo.id;
}
void database::deposit_cashback(const account_object& acct, share_type amount, bool require_vesting)
{
// If we don't have a VBO, or if it has the wrong maturity
// due to a policy change, cut it loose.
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;
}
optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(
acct.cashback_vb,
amount,
get_global_properties().parameters.cashback_vesting_period_seconds,
acct.id,
require_vesting );
if( new_vbid.valid() )
{
modify( acct, [&]( account_object& _acct )
{
_acct.cashback_vb = *new_vbid;
} );
}
return;
}
void database::deposit_witness_pay(const witness_object& wit, share_type amount)
{
if( amount == 0 )
return;
optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(
wit.pay_vb,
amount,
get_global_properties().parameters.witness_pay_vesting_seconds,
wit.witness_account,
true );
if( new_vbid.valid() )
{
modify( wit, [&]( witness_object& _wit )
{
_wit.pay_vb = *new_vbid;
} );
}
return;
}
} }

View file

@ -0,0 +1,729 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/db_with.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/global_property_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>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/evaluator.hpp>
#include <fc/smart_ref_impl.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.contains(id);
}
/**
* Only return true *if* the transaction has not expired or been invalidated. If this
* method is called with a VERY old transaction we will return false, they should
* query things by blocks if they are that old.
*/
bool database::is_known_transaction( const transaction_id_type& id )const
{
const auto& trx_idx = get_index_type<transaction_index>().indices().get<by_trx_id>();
return trx_idx.find( id ) != trx_idx.end();
}
block_id_type database::get_block_id_for_num( uint32_t block_num )const
{ try {
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
{
auto b = _fork_db.fetch_block( id );
if( !b )
return _block_id_to_block.fetch_optional(id);
return b->data;
}
optional<signed_block> database::fetch_block_by_number( uint32_t num )const
{
auto results = _fork_db.fetch_block_by_number(num);
if( results.size() == 1 )
return results[0]->data;
else
return _block_id_to_block.fetch_by_number(num);
return optional<signed_block>();
}
const signed_transaction& database::get_recent_transaction(const transaction_id_type& trx_id) const
{
auto& index = get_index_type<transaction_index>().indices().get<by_trx_id>();
auto itr = index.find(trx_id);
FC_ASSERT(itr != index.end());
return itr->trx;
}
std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of_fork) const
{
pair<fork_database::branch_type, fork_database::branch_type> branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork);
if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) )
{
edump( (head_of_fork)
(head_block_id())
(branches.first.size())
(branches.second.size()) );
assert(branches.first.back()->previous_id() == branches.second.back()->previous_id());
}
std::vector<block_id_type> result;
for (const item_ptr& fork_block : branches.second)
result.emplace_back(fork_block->id);
result.emplace_back(branches.first.back()->previous_id());
return result;
}
/**
* Push block "may fail" in which case every partial change is unwound. After
* push block is successful the block is appended to the chain database on disk.
*
* @return true if we switched forks as a result of this push.
*/
bool database::push_block(const signed_block& new_block, uint32_t skip)
{
// idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous));
bool result;
detail::with_skip_flags( *this, skip, [&]()
{
detail::without_pending_transactions( *this, std::move(_pending_tx),
[&]()
{
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) )
{
/// TODO: if the block is greater than the head block and before the next maitenance interval
// verify that the block signer is in the current set of active witnesses.
shared_ptr<fork_item> 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() )
{
//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() )
{
wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) );
auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());
// pop blocks until we hit the forked block
while( head_block_id() != branches.second.back()->data.previous )
pop_block();
// push all blocks on the new fork
for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr )
{
ilog( "pushing blocks from fork ${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->data.id()) );
optional<fc::exception> except;
try {
undo_database::session session = _undo_db.start_undo_session();
apply_block( (*ritr)->data, skip );
_block_id_to_block.store( (*ritr)->id, (*ritr)->data );
session.commit();
}
catch ( const fc::exception& e ) { except = e; }
if( except )
{
wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) );
// remove the rest of branches.first from the fork_db, those blocks are invalid
while( ritr != branches.first.rend() )
{
_fork_db.remove( (*ritr)->data.id() );
++ritr;
}
_fork_db.set_head( branches.second.front() );
// pop all blocks from the bad fork
while( head_block_id() != branches.second.back()->data.previous )
pop_block();
// restore all blocks from the good fork
for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr )
{
auto session = _undo_db.start_undo_session();
apply_block( (*ritr)->data, skip );
_block_id_to_block.store( new_block.id(), (*ritr)->data );
session.commit();
}
throw *except;
}
}
return true;
}
else return false;
}
}
try {
auto session = _undo_db.start_undo_session();
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()));
_fork_db.remove(new_block.id());
throw;
}
return false;
} FC_CAPTURE_AND_RETHROW( (new_block) ) }
/**
* Attempts to push the transaction into the pending queue
*
* When called to push a locally generated transaction, set the skip_block_size_check bit on the skip argument. This
* will allow the transaction to be pushed even if it causes the pending block size to exceed the maximum block size.
* Although the transaction will probably not propagate further now, as the peers are likely to have their pending
* queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending
* queues.
*/
processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip )
{ try {
processed_transaction result;
detail::with_skip_flags( *this, skip, [&]()
{
result = _push_transaction( trx );
} );
return result;
} FC_CAPTURE_AND_RETHROW( (trx) ) }
processed_transaction database::_push_transaction( const signed_transaction& trx )
{
// 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_tx_session.valid() )
_pending_tx_session = _undo_db.start_undo_session();
// Create a temporary undo session as a child of _pending_tx_session.
// The temporary session will be discarded by the destructor if
// _apply_transaction fails. If we make it to merge(), we
// apply the changes.
auto temp_session = _undo_db.start_undo_session();
auto processed_trx = _apply_transaction( trx );
_pending_tx.push_back(processed_trx);
notify_changed_objects();
// The transaction applied successfully. Merge its changes into the pending block session.
temp_session.merge();
// notify anyone listening to pending transactions
on_pending_transaction( trx );
return processed_trx;
}
processed_transaction database::validate_transaction( const signed_transaction& trx )
{
auto session = _undo_db.start_undo_session();
return _apply_transaction( trx );
}
processed_transaction database::push_proposal(const proposal_object& proposal)
{ try {
transaction_evaluation_state eval_state(this);
eval_state._is_proposed_trx = true;
eval_state.operation_results.reserve(proposal.proposed_transaction.operations.size());
processed_transaction ptrx(proposal.proposed_transaction);
eval_state._trx = &ptrx;
size_t old_applied_ops_size = _applied_ops.size();
try {
auto session = _undo_db.start_undo_session(true);
for( auto& op : proposal.proposed_transaction.operations )
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
remove(proposal);
session.merge();
} catch ( const fc::exception& e ) {
if( head_block_time() <= HARDFORK_483_TIME )
{
for( size_t i=old_applied_ops_size,n=_applied_ops.size(); i<n; i++ )
{
ilog( "removing failed operation from applied_ops: ${op}", ("op", *(_applied_ops[i])) );
_applied_ops[i].reset();
}
}
else
{
_applied_ops.resize( old_applied_ops_size );
}
elog( "e", ("e",e.to_detail_string() ) );
throw;
}
ptrx.operation_results = std::move(eval_state.operation_results);
return ptrx;
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
signed_block database::generate_block(
fc::time_point_sec when,
witness_id_type witness_id,
const fc::ecc::private_key& block_signing_private_key,
uint32_t skip /* = 0 */
)
{ try {
signed_block result;
detail::with_skip_flags( *this, skip, [&]()
{
result = _generate_block( when, witness_id, block_signing_private_key );
} );
return result;
} FC_CAPTURE_AND_RETHROW() }
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 );
FC_ASSERT( slot_num > 0 );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num );
FC_ASSERT( scheduled_witness == witness_id );
const auto& witness_obj = witness_id(*this);
if( !(skip & skip_witness_signature) )
FC_ASSERT( witness_obj.signing_key == block_signing_private_key.get_public_key() );
static const size_t max_block_header_size = fc::raw::pack_size( signed_block_header() ) + 4;
auto maximum_block_size = get_global_properties().parameters.maximum_block_size;
size_t total_block_size = max_block_header_size;
signed_block pending_block;
//
// The following code throws away existing pending_tx_session and
// rebuilds it by re-applying pending transactions.
//
// This rebuild is necessary because pending transactions' validity
// and semantics may have changed since they were received, because
// time-based semantics are evaluated based on the current block
// time. These changes can only be reflected in the database when
// the value of the "when" variable is known, which means we need to
// re-apply pending transactions in this method.
//
_pending_tx_session.reset();
_pending_tx_session = _undo_db.start_undo_session();
uint64_t postponed_tx_count = 0;
// pop pending state (reset to head block state)
for( const processed_transaction& tx : _pending_tx )
{
size_t new_total_size = total_block_size + fc::raw::pack_size( tx );
// postpone transaction if it would make block too big
if( new_total_size >= maximum_block_size )
{
postponed_tx_count++;
continue;
}
try
{
auto temp_session = _undo_db.start_undo_session();
processed_transaction ptx = _apply_transaction( tx );
temp_session.merge();
// We have to recompute pack_size(ptx) because it may be different
// than pack_size(tx) (i.e. if one or more results increased
// their size)
total_block_size += fc::raw::pack_size( ptx );
pending_block.transactions.push_back( ptx );
}
catch ( const fc::exception& e )
{
// Do nothing, transaction will not be re-applied
wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
wlog( "The transaction was ${t}", ("t", tx) );
}
}
if( postponed_tx_count > 0 )
{
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
}
_pending_tx_session.reset();
// We have temporarily broken the invariant that
// _pending_tx_session is the result of applying _pending_tx, as
// _pending_tx now consists of the set of postponed transactions.
// However, the push_block() call below will re-create the
// _pending_tx_session.
pending_block.previous = head_block_id();
pending_block.timestamp = when;
pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();
pending_block.witness = witness_id;
// 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 );
fc::raw::pack( next_enc, pending_block.previous_secret );
pending_block.next_secret_hash = secret_hash_type::hash(next_enc.result());
if( !(skip & skip_witness_signature) )
pending_block.sign( block_signing_private_key );
// TODO: Move this to _push_block() so session is restored.
if( !(skip & skip_block_size_check) )
{
FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
}
push_block( pending_block, skip );
return pending_block;
} FC_CAPTURE_AND_RETHROW( (witness_id) ) }
/**
* Removes the most recent block from the database and
* undoes any changes it made.
*/
void database::pop_block()
{ try {
_pending_tx_session.reset();
auto head_id = head_block_id();
optional<signed_block> head_block = fetch_block_by_id( head_id );
GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
_fork_db.pop_block();
_block_id_to_block.remove( head_id );
pop_undo();
_popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() );
} FC_CAPTURE_AND_RETHROW() }
void database::clear_pending()
{ try {
assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );
_pending_tx.clear();
_pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }
uint32_t database::push_applied_operation( const operation& op )
{
_applied_ops.emplace_back(op);
operation_history_object& oh = *(_applied_ops.back());
oh.block_num = _current_block_num;
oh.trx_in_block = _current_trx_in_block;
oh.op_in_trx = _current_op_in_trx;
oh.virtual_op = _current_virtual_op++;
return _applied_ops.size() - 1;
}
void database::set_applied_operation_result( uint32_t op_id, const operation_result& result )
{
assert( op_id < _applied_ops.size() );
if( _applied_ops[op_id] )
_applied_ops[op_id]->result = result;
else
{
elog( "Could not set operation result (head_block_num=${b})", ("b", head_block_num()) );
}
}
const vector<optional< operation_history_object > >& database::get_applied_operations() const
{
return _applied_ops;
}
//////////////////// private methods ////////////////////
void database::apply_block( const signed_block& next_block, uint32_t skip )
{
auto block_num = next_block.block_num();
if( _checkpoints.size() && _checkpoints.rbegin()->second != block_id_type() )
{
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()) );
if( _checkpoints.rbegin()->first >= block_num )
skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
}
detail::with_skip_flags( *this, skip, [&]()
{
_apply_block( next_block );
} );
return;
}
void database::_apply_block( const signed_block& next_block )
{ try {
uint32_t next_block_num = next_block.block_num();
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(), "", ("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();
const auto& dynamic_global_props = get<dynamic_global_property_object>(dynamic_global_property_id_type());
bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp);
_current_block_num = next_block_num;
_current_trx_in_block = 0;
for( const auto& trx : next_block.transactions )
{
/* We do not need to push the undo state for each transaction
* because they either all apply and are valid or the
* entire block fails to apply. We only need an "undo" state
* for transactions when validating broadcast transactions or
* when building a block.
*/
apply_transaction( trx, skip | skip_transaction_signatures );
++_current_trx_in_block;
}
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
update_witness_schedule(next_block);
update_global_dynamic_data(next_block);
update_signing_witness(signing_witness, next_block);
update_last_irreversible_block();
// Are we at the maintenance interval?
if( maint_needed )
perform_chain_maintenance(next_block, global_props);
create_block_summary(next_block);
clear_expired_transactions();
clear_expired_proposals();
clear_expired_orders();
update_expired_feeds();
update_withdraw_permissions();
update_tournaments();
// n.b., update_maintenance_flag() happens this late
// because get_slot_time() / get_slot_at_time() is needed above
// TODO: figure out if we could collapse this function into
// update_global_dynamic_data() as perhaps these methods only need
// to be called for header validation?
update_maintenance_flag( maint_needed );
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
update_witness_schedule();
if( !_node_property_object.debug_updates.empty() )
apply_debug_updates();
// notify observers that the block has been applied
applied_block( next_block ); //emit
_applied_ops.clear();
notify_changed_objects();
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
void database::notify_changed_objects()
{ try {
if( _undo_db.enabled() )
{
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);
for( const auto& item : head_undo.new_ids ) changed_ids.push_back(item);
vector<const object*> removed;
removed.reserve( head_undo.removed.size() );
for( const auto& item : head_undo.removed )
{
changed_ids.push_back( item.first );
removed.emplace_back( item.second.get() );
}
changed_objects(changed_ids);
}
} FC_CAPTURE_AND_RETHROW() }
processed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip)
{
processed_transaction result;
detail::with_skip_flags( *this, skip, [&]()
{
result = _apply_transaction(trx);
});
return result;
}
processed_transaction database::_apply_transaction(const signed_transaction& trx)
{ try {
uint32_t skip = get_node_properties().skip_flags;
if( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */
trx.validate();
auto& trx_idx = get_mutable_index_type<transaction_index>();
const chain_id_type& chain_id = get_chain_id();
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);
const chain_parameters& chain_parameters = get_global_properties().parameters;
eval_state._trx = &trx;
if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
{
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth );
}
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is
//expired, and TaPoS makes no sense as no blocks exist.
if( BOOST_LIKELY(head_block_num() > 0) )
{
if( !(skip & skip_tapos_check) )
{
const auto& tapos_block_summary = block_summary_id_type( trx.ref_block_num )(*this);
//Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1] );
}
fc::time_point_sec now = head_block_time();
FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "",
("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration));
FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) );
}
//Insert transaction into unique transactions database.
if( !(skip & skip_transaction_dupe_check) )
{
create<transaction_object>([&](transaction_object& transaction) {
transaction.trx_id = trx_id;
transaction.trx = trx;
});
}
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 )
{
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
++_current_op_in_trx;
}
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_asset>();
auto range = index.equal_range( boost::make_tuple( GRAPHENE_TEMP_ACCOUNT ) );
std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); });
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 )
assert( "Negative operation tag" && false );
if( u_which >= _operation_evaluators.size() )
assert( "No registered evaluator for this operation" && false );
unique_ptr<op_evaluator>& eval = _operation_evaluators[ u_which ];
if( !eval )
assert( "No registered evaluator for this operation" && false );
auto op_id = push_applied_operation( op );
auto result = eval->evaluate( eval_state, op, true );
set_applied_operation_result( op_id, result );
return result;
} FC_CAPTURE_AND_RETHROW( (op) ) }
const witness_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const
{
FC_ASSERT( head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous) );
FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("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_hash, "",
("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash)("null_secret_hash", secret_hash_type::hash( secret_hash_type())));
if( !(skip&skip_witness_signature) )
FC_ASSERT( next_block.validate_signee( witness.signing_key ) );
if( !(skip&skip_witness_schedule_check) )
{
uint32_t slot_num = get_slot_at_time( next_block.timestamp );
FC_ASSERT( slot_num > 0 );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num );
FC_ASSERT( next_block.witness == scheduled_witness, "Witness produced block at wrong time",
("block witness",next_block.witness)("scheduled",scheduled_witness)("slot_num",slot_num) );
}
return witness;
}
void database::create_block_summary(const signed_block& next_block)
{
block_summary_id_type sid(next_block.block_num() & 0xffff );
modify( sid(*this), [&](block_summary_object& p) {
p.block_id = next_block.id();
});
}
void database::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts )
{
for( const auto& i : checkpts )
_checkpoints[i.first] = i.second;
}
bool database::before_last_checkpoint()const
{
return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}
} }

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
namespace graphene { namespace chain {
/**
* This method dumps the state of the blockchain in a semi-human readable form for the
* purpose of tracking down funds and mismatches in currency allocation
*/
void database::debug_dump()
{
const auto& db = *this;
const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db);
const auto& balance_index = db.get_index_type<account_balance_index>().indices();
const simple_index<account_statistics_object>& statistics_index = db.get_index_type<simple_index<account_statistics_object>>();
map<asset_id_type,share_type> total_balances;
map<asset_id_type,share_type> total_debts;
share_type core_in_orders;
share_type reported_core_in_orders;
for( const account_balance_object& a : balance_index )
{
// idump(("balance")(a));
total_balances[a.asset_type] += a.balance;
}
for( const account_statistics_object& s : statistics_index )
{
// idump(("statistics")(s));
reported_core_in_orders += s.total_core_in_orders;
}
for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )
{
// idump(("limit_order")(o));
auto for_sale = o.amount_for_sale();
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 call_order_object& o : db.get_index_type<call_order_index>().indices() )
{
// idump(("call_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;
total_debts[o.get_debt().asset_id] += o.get_debt().amount;
}
for( const asset_object& asset_obj : db.get_index_type<asset_index>().indices() )
{
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;
// edump((total_balances[asset_obj.id])(asset_obj.dynamic_asset_data_id(db).current_supply ) );
}
if( total_balances[asset_id_type()].value != core_asset_data.current_supply.value )
{
edump( (total_balances[asset_id_type()].value)(core_asset_data.current_supply.value ));
}
/*
const auto& vbidx = db.get_index_type<simple_index<vesting_balance_object>>();
for( const auto& s : vbidx )
{
// idump(("vesting_balance")(s));
}
*/
}
void debug_apply_update( database& db, const fc::variant_object& vo )
{
static const uint8_t
db_action_nil = 0,
db_action_create = 1,
db_action_write = 2,
db_action_update = 3,
db_action_delete = 4;
// "_action" : "create" object must not exist, unspecified fields take defaults
// "_action" : "write" object may exist, is replaced entirely, unspecified fields take defaults
// "_action" : "update" object must exist, unspecified fields don't change
// "_action" : "delete" object must exist, will be deleted
// if _action is unspecified:
// - delete if object contains only ID field
// - otherwise, write
object_id_type oid;
uint8_t action = db_action_nil;
auto it_id = vo.find("id");
FC_ASSERT( it_id != vo.end() );
from_variant( it_id->value(), oid );
action = ( vo.size() == 1 ) ? db_action_delete : db_action_write;
from_variant( vo["id"], oid );
if( vo.size() == 1 )
action = db_action_delete;
auto it_action = vo.find("_action" );
if( it_action != vo.end() )
{
const std::string& str_action = it_action->value().get_string();
if( str_action == "create" )
action = db_action_create;
else if( str_action == "write" )
action = db_action_write;
else if( str_action == "update" )
action = db_action_update;
else if( str_action == "delete" )
action = db_action_delete;
}
auto& idx = db.get_index( oid );
switch( action )
{
case db_action_create:
/*
idx.create( [&]( object& obj )
{
idx.object_from_variant( vo, obj );
} );
*/
FC_ASSERT( false );
break;
case db_action_write:
db.modify( db.get_object( oid ), [&]( object& obj )
{
idx.object_default( obj );
idx.object_from_variant( vo, obj );
} );
break;
case db_action_update:
db.modify( db.get_object( oid ), [&]( object& obj )
{
idx.object_from_variant( vo, obj );
} );
break;
case db_action_delete:
db.remove( db.get_object( oid ) );
break;
default:
FC_ASSERT( false );
}
}
void database::apply_debug_updates()
{
block_id_type head_id = head_block_id();
auto it = _node_property_object.debug_updates.find( head_id );
if( it == _node_property_object.debug_updates.end() )
return;
for( const fc::variant_object& update : it->second )
debug_apply_update( *this, update );
}
void database::debug_update( const fc::variant_object& update )
{
block_id_type head_id = head_block_id();
auto it = _node_property_object.debug_updates.find( head_id );
if( it == _node_property_object.debug_updates.end() )
it = _node_property_object.debug_updates.emplace( head_id, std::vector< fc::variant_object >() ).first;
it->second.emplace_back( update );
optional<signed_block> head_block = fetch_block_by_id( head_id );
FC_ASSERT( head_block.valid() );
// What the last block does has been changed by adding to node_property_object, so we have to re-apply it
pop_block();
push_block( *head_block );
}
} }

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
const asset_object& database::get_core_asset() const
{
return get(asset_id_type());
}
const global_property_object& database::get_global_properties()const
{
return get( global_property_id_type() );
}
const chain_property_object& database::get_chain_properties()const
{
return get( chain_property_id_type() );
}
const dynamic_global_property_object&database::get_dynamic_global_properties() const
{
return get( dynamic_global_property_id_type() );
}
const fee_schedule& database::current_fee_schedule()const
{
return get_global_properties().parameters.current_fees;
}
time_point_sec database::head_block_time()const
{
return get( dynamic_global_property_id_type() ).time;
}
uint32_t database::head_block_num()const
{
return get( dynamic_global_property_id_type() ).head_block_number;
}
block_id_type database::head_block_id()const
{
return get( dynamic_global_property_id_type() ).head_block_id;
}
decltype( chain_parameters::block_interval ) database::block_interval( )const
{
return get_global_properties().parameters.block_interval;
}
const chain_id_type& database::get_chain_id( )const
{
return get_chain_properties().chain_id;
}
const node_property_object& database::get_node_properties()const
{
return _node_property_object;
}
node_property_object& database::node_properties()
{
return _node_property_object;
}
uint32_t database::last_non_undoable_block_num() const
{
return head_block_num() - _undo_db.size();
}
} }

863
libraries/chain/db_init.cpp Normal file
View file

@ -0,0 +1,863 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/budget_record_object.hpp>
#include <graphene/chain/buyback_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/special_authority_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/tournament_object.hpp>
#include <graphene/chain/match_object.hpp>
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/asset_evaluator.hpp>
#include <graphene/chain/assert_evaluator.hpp>
#include <graphene/chain/balance_evaluator.hpp>
#include <graphene/chain/committee_member_evaluator.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/custom_evaluator.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/proposal_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/tournament_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/smart_ref_impl.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 worker_object::space_id;
const uint8_t worker_object::type_id;
void database::initialize_evaluators()
{
_operation_evaluators.resize(255);
register_evaluator<account_create_evaluator>();
register_evaluator<account_update_evaluator>();
register_evaluator<account_upgrade_evaluator>();
register_evaluator<account_whitelist_evaluator>();
register_evaluator<committee_member_create_evaluator>();
register_evaluator<committee_member_update_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_reserve_evaluator>();
register_evaluator<asset_update_evaluator>();
register_evaluator<asset_update_bitasset_evaluator>();
register_evaluator<asset_update_dividend_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<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<vesting_balance_create_evaluator>();
register_evaluator<vesting_balance_withdraw_evaluator>();
register_evaluator<witness_create_evaluator>();
register_evaluator<witness_update_evaluator>();
register_evaluator<withdraw_permission_create_evaluator>();
register_evaluator<withdraw_permission_claim_evaluator>();
register_evaluator<withdraw_permission_update_evaluator>();
register_evaluator<withdraw_permission_delete_evaluator>();
register_evaluator<worker_create_evaluator>();
register_evaluator<balance_claim_evaluator>();
register_evaluator<transfer_to_blind_evaluator>();
register_evaluator<transfer_from_blind_evaluator>();
register_evaluator<blind_transfer_evaluator>();
register_evaluator<asset_claim_fees_evaluator>();
register_evaluator<tournament_create_evaluator>();
register_evaluator<tournament_join_evaluator>();
register_evaluator<game_move_evaluator>();
register_evaluator<tournament_leave_evaluator>();
}
void database::initialize_indexes()
{
reset_indexes();
_undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY );
//Protocol object indexes
add_index< primary_index<asset_index> >();
add_index< primary_index<force_settlement_index> >();
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<call_order_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<vesting_balance_index> >();
add_index< primary_index<worker_index> >();
add_index< primary_index<balance_index> >();
add_index< primary_index<blinded_balance_index> >();
add_index< primary_index<tournament_index> >();
auto tournament_details_idx = add_index< primary_index<tournament_details_index> >();
tournament_details_idx->add_secondary_index<tournament_players_index>();
add_index< primary_index<match_index> >();
add_index< primary_index<game_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<asset_dividend_data_object_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<chain_property_object > > >();
add_index< primary_index<simple_index<witness_schedule_object > > >();
add_index< primary_index<simple_index<budget_record_object > > >();
add_index< primary_index< special_authority_index > >();
add_index< primary_index< buyback_index > >();
add_index< primary_index< simple_index< fba_accumulator_object > > >();
add_index< primary_index<pending_dividend_payout_balance_for_holder_object_index > >();
add_index< primary_index<total_distributed_dividend_balance_object_index > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)
{ try {
FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." );
FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % GRAPHENE_DEFAULT_BLOCK_INTERVAL == 0,
"Genesis timestamp must be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL." );
FC_ASSERT(genesis_state.initial_witness_candidates.size() > 0,
"Cannot start a chain with zero witnesses.");
FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(),
"initial_active_witnesses is larger than the number of candidate witnesses.");
_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);
flat_index<block_summary_object>& bsi = get_mutable_index_type< flat_index<block_summary_object> >();
bsi.resize(0xffff+1);
// 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& 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.owner.weight_threshold = 1;
n.active.weight_threshold = 1;
n.name = "committee-account";
n.statistics = create<account_statistics_object>( [&](account_statistics_object& s){ s.owner = n.id; }).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& s){s.owner = a.id;}).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& s){s.owner = a.id;}).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& s){s.owner = a.id;}).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& s){s.owner = a.id;}).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);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "proxy-to-self";
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).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_PROXY_TO_SELF_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "test-dividend-distribution";
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_PROXY_TO_SELF_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = 0;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
}).get_id() == TOURNAMENT_RAKE_FEE_ACCOUNT_ID);
// Create more special accounts
while( true )
{
uint64_t id = get_index<account_object>().get_next_id().instance();
if( id >= genesis_state.immutable_parameters.num_special_accounts )
break;
const account_object& acct = create<account_object>([&](account_object& a) {
a.name = "special-account-" + std::to_string(id);
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = account_id_type(id);
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;
});
FC_ASSERT( acct.get_id() == account_id_type(id) );
remove( acct );
}
// Create core asset
const asset_dynamic_data_object& dyn_asset =
create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) {
a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY;
});
const asset_dividend_data_object& div_asset =
create<asset_dividend_data_object>([&](asset_dividend_data_object& a) {
a.options.minimum_distribution_interval = 3*24*60*60;
a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT;
a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
a.options.payout_interval = 30*24*60*60;
a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
});
const asset_object& core_asset =
create<asset_object>( [&]( asset_object& a ) {
a.symbol = GRAPHENE_SYMBOL;
a.options.max_supply = genesis_state.max_core_supply;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 0;
a.issuer = GRAPHENE_COMMITTEE_ACCOUNT;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
a.options.core_exchange_rate.quote.amount = 1;
a.options.core_exchange_rate.quote.asset_id = asset_id_type(0);
a.dynamic_asset_data_id = dyn_asset.id;
a.dividend_data_id = div_asset.id;
});
assert( asset_id_type(core_asset.id) == asset().asset_id );
assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) );
#ifdef _DEFAULT_DIVIDEND_ASSET
// Create default dividend asset
const asset_dynamic_data_object& dyn_asset1 =
create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) {
a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY;
});
const asset_dividend_data_object& div_asset1 =
create<asset_dividend_data_object>([&](asset_dividend_data_object& a) {
a.options.minimum_distribution_interval = 3*24*60*60;
a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT;
a.options.next_payout_time = genesis_state.initial_timestamp + fc::hours(1);
a.options.payout_interval = 7*24*60*60;
a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
});
const asset_object& default_asset =
create<asset_object>( [&]( asset_object& a ) {
a.symbol = "DEFAULT";
a.options.max_market_fee =
a.options.max_supply = genesis_state.max_core_supply;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 79;
a.issuer = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
a.options.core_exchange_rate.quote.amount = 1;
a.options.core_exchange_rate.quote.asset_id = asset_id_type(1);
a.dynamic_asset_data_id = dyn_asset1.id;
a.dividend_data_id = div_asset1.id;
});
assert( default_asset.id == asset_id_type(1) );
#endif
// Create more special assets
while( true )
{
uint64_t id = get_index<asset_object>().get_next_id().instance();
if( id >= genesis_state.immutable_parameters.num_special_assets )
break;
const asset_dynamic_data_object& dyn_asset =
create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) {
a.current_supply = 0;
});
const asset_object& asset_obj = create<asset_object>( [&]( asset_object& a ) {
a.symbol = "SPECIAL" + std::to_string( id );
a.options.max_supply = 0;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 0;
a.issuer = GRAPHENE_NULL_ACCOUNT;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
a.options.core_exchange_rate.quote.amount = 1;
a.options.core_exchange_rate.quote.asset_id = asset_id_type(0);
a.dynamic_asset_data_id = dyn_asset.id;
});
FC_ASSERT( asset_obj.get_id() == asset_id_type(id) );
remove( asset_obj );
}
chain_id_type chain_id = genesis_state.compute_chain_id();
// Create global properties
create<global_property_object>([&](global_property_object& p) {
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.dynamic_flags = 0;
p.witness_budget = 0;
p.recent_slots_filled = fc::uint128::max_value();
});
FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" );
FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, "min_committee_member_count must be odd" );
create<chain_property_object>([&](chain_property_object& p)
{
p.chain_id = chain_id;
p.immutable_parameters = genesis_state.immutable_parameters;
} );
create<block_summary_object>([&](block_summary_object&) {});
// Create initial accounts from graphene-based chains
// graphene accounts can refer to other accounts in their authorities, so
// we first create all accounts with dummy authorities, then go back and
// set up the authorities once the accounts all have ids assigned.
for( const auto& account : genesis_state.initial_bts_accounts )
{
account_create_operation cop;
cop.name = account.name;
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
cop.owner = authority(1, GRAPHENE_TEMP_ACCOUNT, 1);
account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());
}
// Create initial accounts
for( const auto& account : genesis_state.initial_accounts )
{
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() )
{
cop.active = cop.owner;
cop.options.memo_key = account.owner_key;
}
else
{
cop.active = authority(1, account.active_key, 1);
cop.options.memo_key = account.active_key;
}
account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());
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();
};
for( const auto& account : genesis_state.initial_bts_accounts )
{
account_update_operation op;
op.account = get_account_id(account.name);
authority owner_authority;
owner_authority.weight_threshold = account.owner_authority.weight_threshold;
for (const auto& value : account.owner_authority.account_auths)
owner_authority.account_auths.insert(std::make_pair(get_account_id(value.first), value.second));
owner_authority.key_auths = account.owner_authority.key_auths;
owner_authority.address_auths = account.owner_authority.address_auths;
op.owner = std::move(owner_authority);
authority active_authority;
active_authority.weight_threshold = account.active_authority.weight_threshold;
for (const auto& value : account.active_authority.account_auths)
active_authority.account_auths.insert(std::make_pair(get_account_id(value.first), value.second));
active_authority.key_auths = account.active_authority.key_auths;
active_authority.address_auths = account.active_authority.address_auths;
op.active = std::move(active_authority);
apply_operation(genesis_eval_state, op);
}
// Helper function to get asset ID by symbol
const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();
const auto get_asset_id = [&assets_by_symbol](const string& symbol) {
auto itr = assets_by_symbol.find(symbol);
// TODO: This is temporary for handling BTS snapshot
if( symbol == "BTS" )
itr = assets_by_symbol.find(GRAPHENE_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();
};
map<asset_id_type, share_type> total_supplies;
map<asset_id_type, share_type> total_debts;
// Create initial assets
for( const genesis_state_type::initial_asset_type& asset : genesis_state.initial_assets )
{
asset_id_type new_asset_id = get_index_type<asset_index>().get_next_id();
total_supplies[ new_asset_id ] = 0;
asset_dynamic_data_id_type dynamic_data_id;
optional<asset_bitasset_data_id_type> bitasset_data_id;
if( asset.is_bitasset )
{
int collateral_holder_number = 0;
total_debts[ new_asset_id ] = 0;
for( const auto& collateral_rec : asset.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>();
modify( owner_account_id(*this).statistics(*this), [&]( account_statistics_object& o ) {
o.total_core_in_orders = collateral_rec.collateral;
});
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, core_asset.id),
GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);
});
total_supplies[ asset_id_type(0) ] += collateral_rec.collateral;
total_debts[ new_asset_id ] += collateral_rec.debt;
++collateral_holder_number;
}
bitasset_data_id = create<asset_bitasset_data_object>([&](asset_bitasset_data_object& b) {
b.options.short_backing_asset = core_asset.id;
b.options.minimum_feeds = GRAPHENE_DEFAULT_MINIMUM_FEEDS;
}).id;
}
dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) {
d.accumulated_fees = asset.accumulated_fees;
}).id;
total_supplies[ new_asset_id ] += asset.accumulated_fees;
create<asset_object>([&](asset_object& a) {
a.symbol = asset.symbol;
a.options.description = asset.description;
a.precision = asset.precision;
string issuer_name = asset.issuer_name;
a.issuer = get_account_id(issuer_name);
a.options.max_supply = asset.max_supply;
a.options.flags = witness_fed_asset;
a.options.issuer_permissions = charge_market_fee | override_authority | white_list | transfer_restricted | disable_confidential |
( asset.is_bitasset ? disable_force_settle | global_settle | witness_fed_asset | committee_fed_asset : 0 );
a.dynamic_asset_data_id = dynamic_data_id;
a.bitasset_data_id = bitasset_data_id;
});
}
// Create balances for all bts accounts
for( const auto& account : genesis_state.initial_bts_accounts )
if (account.core_balance != share_type())
create<account_balance_object>([&](account_balance_object& b) {
b.owner = get_account_id(account.name);
b.balance = account.core_balance;
});
// Create initial balances
share_type total_allocation;
for( const auto& handout : genesis_state.initial_balances )
{
const auto asset_id = get_asset_id(handout.asset_symbol);
create<balance_object>([&handout,&get_asset_id,total_allocation,asset_id](balance_object& b) {
b.balance = asset(handout.amount, asset_id);
b.owner = handout.owner;
});
total_supplies[ asset_id ] += handout.amount;
}
// Create initial vesting balances
for( const genesis_state_type::initial_vesting_balance_type& vest : genesis_state.initial_vesting_balances )
{
const auto asset_id = get_asset_id(vest.asset_symbol);
create<balance_object>([&](balance_object& b) {
b.owner = vest.owner;
b.balance = asset(vest.amount, asset_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_supplies[ asset_id ] += vest.amount;
}
if( total_supplies[ asset_id_type(0) ] > 0 )
{
adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));
}
else
{
total_supplies[ asset_id_type(0) ] = GRAPHENE_MAX_SHARE_SUPPLY;
}
#ifdef _DEFAULT_DIVIDEND_ASSET
total_debts[ asset_id_type(1) ] =
total_supplies[ asset_id_type(1) ] = 0;
#endif
// it is workaround, should be clarified
total_debts[ asset_id_type() ] = total_supplies[ asset_id_type() ];
const auto& idx = get_index_type<asset_index>().indices().get<by_symbol>();
auto it = idx.begin();
bool has_imbalanced_assets = false;
while( it != idx.end() )
{
if( it->bitasset_data_id.valid() )
{
auto supply_itr = total_supplies.find( it->id );
auto debt_itr = total_debts.find( it->id );
FC_ASSERT( supply_itr != total_supplies.end() );
FC_ASSERT( debt_itr != total_debts.end() );
if( supply_itr->second != debt_itr->second )
{
has_imbalanced_assets = true;
elog( "Genesis for asset ${aname} is not balanced\n"
" Debt is ${debt}\n"
" Supply is ${supply}\n",
("aname", debt_itr->first)
("debt", debt_itr->second)
("supply", supply_itr->second)
);
}
}
++it;
}
FC_ASSERT( !has_imbalanced_assets );
// Save tallied supplies
for( const auto& item : total_supplies )
{
const auto asset_id = item.first;
const auto total_supply = item.second;
modify( get( asset_id ), [ & ]( asset_object& asset ) {
modify( get( asset.dynamic_asset_data_id ), [ & ]( asset_dynamic_data_object& asset_data ) {
asset_data.current_supply = total_supply;
} );
} );
}
// Create special witness account
const witness_object& wit = create<witness_object>([&](witness_object& w) {});
FC_ASSERT( wit.id == GRAPHENE_NULL_WITNESS );
remove(wit);
// 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.initial_secret = secret_hash_type::hash(secret_hash_type());
op.witness_account = get_account_id(witness.owner_name);
op.block_signing_key = witness.block_signing_key;
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( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i )
{
p.active_witnesses.insert(witness_id_type(i));
}
});
// Initialize witness schedule
#ifndef NDEBUG
const witness_schedule_object& wso =
#endif
create<witness_schedule_object>([&](witness_schedule_object& _wso)
{
// for scheduled
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();
// for shuffled
for( const witness_id_type& wid : get_global_properties().active_witnesses )
_wso.current_shuffled_witnesses.push_back( wid );
});
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;
});
// Create witness scheduler
//create<witness_schedule_object>([&]( witness_schedule_object& wso )
//{
// for( const witness_id_type& wid : get_global_properties().active_witnesses )
// wso.current_shuffled_witnesses.push_back( wid );
//});
// Create FBA counters
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{
FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_to_blind ) );
acc.accumulated_fba_fees = 0;
#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;
#endif
});
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{
FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_blind_transfer ) );
acc.accumulated_fba_fees = 0;
#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;
#endif
});
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{
FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_from_blind ) );
acc.accumulated_fba_fees = 0;
#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;
#endif
});
FC_ASSERT( get_index<fba_accumulator_object>().get_next_id() == fba_accumulator_id_type( fba_accumulator_id_count ) );
debug_dump();
_undo_db.enable();
} FC_CAPTURE_AND_RETHROW() }
} }

1391
libraries/chain/db_maint.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/io/fstream.hpp>
#include <fstream>
#include <functional>
#include <iostream>
namespace graphene { namespace chain {
database::database() :
_random_number_generator(fc::ripemd160().data())
{
initialize_indexes();
initialize_evaluators();
}
database::~database()
{
clear_pending();
}
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
{ try {
ilog( "reindexing blockchain" );
wipe(data_dir, false);
open(data_dir, [&initial_allocation]{return initial_allocation;});
auto start = fc::time_point::now();
auto last_block = _block_id_to_block.last();
if( !last_block ) {
elog( "!no last block" );
edump((last_block));
return;
}
const auto last_block_num = last_block->block_num();
ilog( "Replaying blocks..." );
_undo_db.disable();
for( uint32_t i = 1; i <= last_block_num; ++i )
{
if( i % 2000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "<<i << " of " <<last_block_num<<" \n";
fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i);
if( !block.valid() )
{
wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) );
uint32_t dropped_count = 0;
while( true )
{
fc::optional< block_id_type > last_id = _block_id_to_block.last_id();
// this can trigger if we attempt to e.g. read a file that has block #2 but no block #1
if( !last_id.valid() )
break;
// we've caught up to the gap
if( block_header::num_from_id( *last_id ) <= i )
break;
_block_id_to_block.remove( *last_id );
dropped_count++;
}
wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) );
break;
}
apply_block(*block, skip_witness_signature |
skip_transaction_signatures |
skip_transaction_dupe_check |
skip_tapos_check |
skip_witness_schedule_check |
skip_authority_check);
}
_undo_db.enable();
auto end = fc::time_point::now();
ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
void database::wipe(const fc::path& data_dir, bool include_blocks)
{
ilog("Wiping database", ("include_blocks", include_blocks));
close();
object_database::wipe(data_dir);
if( include_blocks )
fc::remove_all( data_dir / "database" );
}
void database::open(
const fc::path& data_dir,
std::function<genesis_state_type()> 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());
fc::optional<signed_block> last_block = _block_id_to_block.last();
if( last_block.valid() )
{
_fork_db.start_block( *last_block );
idump((last_block->id())(last_block->block_num()));
idump((head_block_id())(head_block_num()));
if( last_block->id() != head_block_id() )
{
FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state",
("last_block->id", last_block->id())("head_block_num",head_block_num()) );
}
}
}
FC_CAPTURE_LOG_AND_RETHROW( (data_dir) )
}
void database::close(bool rewind)
{
// TODO: Save pending tx's on close()
clear_pending();
// pop all of the blocks that we can given our undo history, this should
// throw when there is no more undo history to pop
if( rewind )
{
try
{
uint32_t cutoff = get_dynamic_global_properties().last_irreversible_block_num;
while( head_block_num() > cutoff )
{
// elog("pop");
block_id_type popped_block_id = head_block_id();
pop_block();
_fork_db.remove(popped_block_id); // doesn't throw on missing
try
{
_block_id_to_block.remove(popped_block_id);
}
catch (const fc::key_not_found_exception&)
{
}
}
}
catch (...)
{
}
}
// Since pop_block() will move tx's in the popped blocks into pending,
// we have to clear_pending() after we're done popping to get a clean
// DB state (issue #336).
clear_pending();
object_database::flush();
object_database::close();
if( _block_id_to_block.is_open() )
_block_id_to_block.close();
_fork_db.reset();
}
} }

View file

@ -0,0 +1,599 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/market_object.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
/**
* 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);
const asset_dynamic_data_object& mia_dyn = mia.dynamic_asset_data_id(*this);
auto original_mia_supply = mia_dyn.current_supply;
const call_order_index& call_index = get_index_type<call_order_index>();
const auto& call_price_index = call_index.indices().get<by_price>();
// 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 )
{
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() ) );
}
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;
});
/// 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;
});
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
void database::cancel_order(const force_settlement_object& order, bool create_virtual_op)
{
adjust_balance(order.owner, order.balance);
if( create_virtual_op )
{
asset_settle_cancel_operation vop;
vop.settlement = order.id;
vop.account = order.owner;
vop.amount = order.balance;
push_applied_operation( vop );
}
remove(order);
}
void database::cancel_order( const limit_order_object& order, bool create_virtual_op )
{
auto refunded = order.amount_for_sale();
modify( order.seller(*this).statistics(*this),[&]( account_statistics_object& obj ){
if( refunded.asset_id == asset_id_type() )
{
obj.total_core_in_orders -= refunded.amount;
}
});
adjust_balance(order.seller, refunded);
adjust_balance(order.seller, order.deferred_fee);
if( create_virtual_op )
{
limit_order_cancel_operation vop;
vop.order = order.id;
vop.fee_paying_account = order.seller;
push_applied_operation( vop );
}
remove(order);
}
bool maybe_cull_small_order( database& db, const limit_order_object& order )
{
/**
* 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 the order is a taker order (as opposed to a maker order), so the price is
* set by the counterparty, this check is deferred until the order becomes unmatched
* (see #555) -- however, detecting this condition is the responsibility of the caller.
*/
if( order.amount_to_receive().amount == 0 )
{
ilog( "applied epsilon logic" );
db.cancel_order(order);
return true;
}
return false;
}
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);
const limit_order_object* updated_order_object = find< limit_order_object >( order_id );
if( updated_order_object == nullptr )
return true;
if( head_block_time() <= HARDFORK_555_TIME )
return false;
// before #555 we would have done maybe_cull_small_order() logic as a result of fill_order() being called by match() above
// however after #555 we need to get rid of small orders -- #555 hardfork defers logic that was done too eagerly before, and
// this is the point it's deferred to.
return maybe_cull_small_order( *this, *updated_order_object );
}
/**
* Matches the two orders,
*
* @return a bit field indicating which orders were filled (and thus removed)
*
* 0 - no orders were matched
* 1 - bid was filled
* 2 - ask was filled
* 3 - both were filled
*/
template<typename OrderType>
int database::match( const limit_order_object& usd, const OrderType& core, const price& match_price )
{
assert( usd.sell_price.quote.asset_id == core.sell_price.base.asset_id );
assert( usd.sell_price.base.asset_id == core.sell_price.quote.asset_id );
assert( usd.for_sale > 0 && core.for_sale > 0 );
auto usd_for_sale = usd.amount_for_sale();
auto core_for_sale = core.amount_for_sale();
asset usd_pays, usd_receives, core_pays, core_receives;
if( usd_for_sale <= core_for_sale * match_price )
{
core_receives = usd_for_sale;
usd_receives = usd_for_sale * match_price;
}
else
{
//This line once read: assert( core_for_sale < usd_for_sale * match_price );
//This assert is not always true -- see trade_amount_equals_zero in operation_tests.cpp
//Although usd_for_sale is greater than core_for_sale * match_price, core_for_sale == usd_for_sale * match_price
//Removing the assert seems to be safe -- apparently no asset is created or destroyed.
usd_receives = core_for_sale;
core_receives = core_for_sale * match_price;
}
core_pays = usd_receives;
usd_pays = core_receives;
assert( usd_pays == usd.amount_for_sale() ||
core_pays == core.amount_for_sale() );
int result = 0;
result |= fill_order( usd, usd_pays, usd_receives, false );
result |= fill_order( core, core_pays, core_receives, true ) << 1;
assert( result != 0 );
return result;
}
int database::match( const limit_order_object& bid, const limit_order_object& ask, const price& match_price )
{
return match<limit_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 )
{ try {
FC_ASSERT(call.get_debt().asset_id == settle.balance.asset_id );
FC_ASSERT(call.debt > 0 && call.collateral > 0 && settle.balance.amount > 0);
auto settle_for_sale = std::min(settle.balance, max_settlement);
auto call_debt = call.get_debt();
asset call_receives = std::min(settle_for_sale, call_debt);
asset call_pays = call_receives * match_price;
asset settle_pays = call_receives;
asset settle_receives = call_pays;
/**
* If the least collateralized call position lacks sufficient
* collateral to cover at the match price then this indicates a black
* swan event according to the price feed, but only the market
* can trigger a black swan. So now we must cancel the forced settlement
* object.
*/
GRAPHENE_ASSERT( call_pays < call.get_collateral(), black_swan_exception, "" );
assert( settle_pays == settle_for_sale || call_receives == call.get_debt() );
fill_order(call, call_pays, call_receives);
fill_order(settle, settle_pays, settle_receives);
return call_receives;
} FC_CAPTURE_AND_RETHROW( (call)(settle)(match_price)(max_settlement) ) }
bool database::fill_order( const limit_order_object& order, const asset& pays, const asset& receives, bool cull_if_small )
{ try {
cull_if_small |= (head_block_time() < HARDFORK_555_TIME);
FC_ASSERT( order.amount_for_sale().asset_id == pays.asset_id );
FC_ASSERT( pays.asset_id != receives.asset_id );
const account_object& seller = order.seller(*this);
const asset_object& recv_asset = receives.asset_id(*this);
auto issuer_fees = pay_market_fees( recv_asset, receives );
pay_order( seller, receives - issuer_fees, pays );
assert( pays.asset_id != receives.asset_id );
push_applied_operation( fill_order_operation( order.id, order.seller, pays, receives, issuer_fees ) );
// conditional because cheap integer comparison may allow us to avoid two expensive modify() and object lookups
if( order.deferred_fee > 0 )
{
modify( seller.statistics(*this), [&]( account_statistics_object& statistics )
{
statistics.pay_fee( order.deferred_fee, get_global_properties().parameters.cashback_vesting_threshold );
} );
}
if( pays == order.amount_for_sale() )
{
remove( order );
return true;
}
else
{
modify( order, [&]( limit_order_object& b ) {
b.for_sale -= pays.amount;
b.deferred_fee = 0;
});
if( cull_if_small )
return maybe_cull_small_order( *this, order );
return false;
}
} 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));
FC_ASSERT( order.get_debt().asset_id == receives.asset_id );
FC_ASSERT( order.get_collateral().asset_id == pays.asset_id );
FC_ASSERT( order.get_collateral() >= pays );
optional<asset> collateral_freed;
modify( order, [&]( call_order_object& o ){
o.debt -= receives.amount;
o.collateral -= pays.amount;
if( o.debt == 0 )
{
collateral_freed = o.get_collateral();
o.collateral = 0;
}
});
const asset_object& mia = receives.asset_id(*this);
assert( mia.is_market_issued() );
const asset_dynamic_data_object& mia_ddo = mia.dynamic_asset_data_id(*this);
modify( mia_ddo, [&]( asset_dynamic_data_object& ao ){
//idump((receives));
ao.current_supply -= receives.amount;
});
const account_object& borrower = order.borrower(*this);
if( collateral_freed || pays.asset_id == asset_id_type() )
{
const account_statistics_object& borrower_statistics = borrower.statistics(*this);
if( collateral_freed )
adjust_balance(borrower.get_id(), *collateral_freed);
modify( borrower_statistics, [&]( account_statistics_object& b ){
if( collateral_freed && collateral_freed->amount > 0 )
b.total_core_in_orders -= collateral_freed->amount;
if( pays.asset_id == asset_id_type() )
b.total_core_in_orders -= pays.amount;
assert( b.total_core_in_orders >= 0 );
});
}
assert( pays.asset_id != receives.asset_id );
push_applied_operation( fill_order_operation{ order.id, order.borrower, pays, receives, asset(0, pays.asset_id) } );
if( collateral_freed )
remove( order );
return collateral_freed.valid();
} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }
bool database::fill_order(const force_settlement_object& settle, const asset& pays, const asset& receives)
{ try {
bool filled = false;
auto issuer_fees = pay_market_fees(get(receives.asset_id), receives);
if( pays < settle.balance )
{
modify(settle, [&pays](force_settlement_object& s) {
s.balance -= pays;
});
filled = false;
} else {
filled = true;
}
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 } );
if (filled)
remove(settle);
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 enable_black_swan)
{ try {
if( !mia.is_market_issued() ) return false;
if( check_for_blackswan( mia, enable_black_swan ) )
return false;
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
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>();
const limit_order_index& limit_index = get_index_type<limit_order_index>();
const auto& limit_price_index = limit_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();
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 );
if( limit_itr == limit_end )
return false;
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
auto call_itr = call_price_index.lower_bound( call_min );
auto call_end = call_price_index.upper_bound( call_max );
bool filled_limit = false;
bool margin_called = false;
while( !check_for_blackswan( mia, enable_black_swan ) && call_itr != call_end )
{
bool filled_call = false;
price match_price;
asset usd_for_sale;
if( limit_itr != limit_end )
{
assert( limit_itr != limit_price_index.end() );
match_price = limit_itr->sell_price;
usd_for_sale = limit_itr->amount_for_sale();
}
else return margin_called;
match_price.validate();
// would be margin called, but there is no matching order #436
bool feed_protected = ( bitasset.current_feed.settlement_price > ~call_itr->call_price );
if( feed_protected && (head_block_time() > HARDFORK_436_TIME) )
return margin_called;
// would be margin called, but there is no matching order
if( match_price > ~call_itr->call_price )
return margin_called;
if( feed_protected )
{
ilog( "Feed protected margin call executing (HARDFORK_436_TIME not here yet)" );
idump( (*call_itr) );
idump( (*limit_itr) );
}
// idump((*call_itr));
// idump((*limit_itr));
// ilog( "match_price <= ~call_itr->call_price performing a margin call" );
margin_called = true;
auto usd_to_buy = call_itr->get_debt();
if( usd_to_buy * match_price > call_itr->get_collateral() )
{
elog( "black swan detected" );
edump((enable_black_swan));
FC_ASSERT( enable_black_swan );
globally_settle_asset(mia, bitasset.current_feed.settlement_price );
return true;
}
asset call_pays, call_receives, order_pays, order_receives;
if( usd_to_buy >= usd_for_sale )
{ // fill order
call_receives = usd_for_sale;
order_receives = usd_for_sale * match_price;
call_pays = order_receives;
order_pays = usd_for_sale;
filled_limit = true;
filled_call = (usd_to_buy == usd_for_sale);
} else { // fill call
call_receives = usd_to_buy;
order_receives = usd_to_buy * match_price;
call_pays = order_receives;
order_pays = usd_to_buy;
filled_call = true;
}
FC_ASSERT( filled_call || filled_limit );
auto old_call_itr = call_itr;
if( filled_call ) ++call_itr;
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, true);
} // whlie call_itr != call_end
return margin_called;
} FC_CAPTURE_AND_RETHROW() }
void database::pay_order( const account_object& receiver, const asset& receives, const asset& pays )
{
const auto& balances = receiver.statistics(*this);
modify( balances, [&]( account_statistics_object& b ){
if( pays.asset_id == asset_id_type() )
{
b.total_core_in_orders -= pays.amount;
}
});
adjust_balance(receiver.get_id(), receives);
}
asset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount )
{
assert( trade_asset.id == trade_amount.asset_id );
if( !trade_asset.charges_market_fees() )
return trade_asset.amount(0);
if( trade_asset.options.market_fee_percent == 0 )
return trade_asset.amount(0);
fc::uint128 a(trade_amount.amount.value);
a *= trade_asset.options.market_fee_percent;
a /= GRAPHENE_100_PERCENT;
asset percent_fee = trade_asset.amount(a.to_uint64());
if( percent_fee.amount > trade_asset.options.max_market_fee )
percent_fee.amount = trade_asset.options.max_market_fee;
return percent_fee;
}
asset database::pay_market_fees( const asset_object& recv_asset, const asset& receives )
{
auto issuer_fees = calculate_market_fee( recv_asset, receives );
assert(issuer_fees <= receives );
//Don't dirty undo state if not actually collecting any fees
if( issuer_fees.amount > 0 )
{
const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this);
modify( recv_dyn_data, [&]( asset_dynamic_data_object& obj ){
//idump((issuer_fees));
obj.accumulated_fees += issuer_fees.amount;
});
}
return issuer_fees;
}
} }

View file

@ -0,0 +1,610 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/db_with.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
void database::update_global_dynamic_data( const signed_block& b )
{
const dynamic_global_property_object& _dgp = dynamic_global_property_id_type(0)(*this);
const global_property_object& gpo = get_global_properties();
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
//#define DIRTY_TRICK // problem with missed_blocks can occur when "maintenance_interval" set to few minutes
#ifdef DIRTY_TRICK
if (missed_blocks != 0) {
#else
assert( missed_blocks != 0 );
#endif
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
{
missed_blocks--;
for( uint32_t i = 0; i < missed_blocks; ++i ) {
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
if( witness_missed.id != b.witness ) {
/*
const auto& witness_account = witness_missed.witness_account(*this);
if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) );
*/
modify( witness_missed, [&]( witness_object& w ) {
w.total_missed++;
});
}
}
}
#ifdef DIRTY_TRICK
}
#endif
// dynamic global properties updating
modify( _dgp, [&]( dynamic_global_property_object& dgp ){
secret_hash_type::encoder enc;
fc::raw::pack( enc, dgp.random );
fc::raw::pack( enc, b.previous_secret );
dgp.random = enc.result();
_random_number_generator = fc::hash_ctr_rng<secret_hash_type, 20>(dgp.random.data());
if( BOOST_UNLIKELY( b.block_num() == 1 ) )
dgp.recently_missed_count = 0;
else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() )
dgp.recently_missed_count = 0;
else if( missed_blocks )
dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
else if( dgp.recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )
dgp.recently_missed_count -= GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT;
else if( dgp.recently_missed_count > 0 )
dgp.recently_missed_count--;
dgp.head_block_number = b.block_num();
dgp.head_block_id = b.id();
dgp.time = b.timestamp;
dgp.current_witness = b.witness;
dgp.recent_slots_filled = (
(dgp.recent_slots_filled << 1)
+ 1) << missed_blocks;
dgp.current_aslot += missed_blocks+1;
});
if( !(get_node_properties().skip_flags & skip_undo_history_check) )
{
GRAPHENE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,
"The database does not have enough undo history to support a blockchain with so many missed blocks. "
"Please add a checkpoint if you would like to continue applying blocks beyond this point.",
("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number)
("recently_missed",_dgp.recently_missed_count)("max_undo",GRAPHENE_MAX_UNDO_HISTORY) );
}
_undo_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
_fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
}
void database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block)
{
const global_property_object& gpo = get_global_properties();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time( new_block.timestamp );
share_type witness_pay = std::min( gpo.parameters.witness_pay_per_block, dpo.witness_budget );
modify( dpo, [&]( dynamic_global_property_object& _dpo )
{
_dpo.witness_budget -= witness_pay;
} );
deposit_witness_pay( signing_witness, witness_pay );
modify( signing_witness, [&]( witness_object& _wit )
{
_wit.last_aslot = new_block_aslot;
_wit.last_confirmed_block_num = new_block.block_num();
_wit.previous_secret = new_block.previous_secret;
_wit.next_secret_hash = new_block.next_secret_hash;
} );
}
void database::update_last_irreversible_block()
{
const global_property_object& gpo = get_global_properties();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
vector< const witness_object* > wit_objs;
wit_objs.reserve( gpo.active_witnesses.size() );
for( const witness_id_type& wid : gpo.active_witnesses )
wit_objs.push_back( &(wid(*this)) );
static_assert( GRAPHENE_IRREVERSIBLE_THRESHOLD > 0, "irreversible threshold must be nonzero" );
// 1 1 1 2 2 2 2 2 2 2 -> 2 .7*10 = 7
// 1 1 1 1 1 1 1 2 2 2 -> 1
// 3 3 3 3 3 3 3 3 3 3 -> 3
size_t offset = ((GRAPHENE_100_PERCENT - GRAPHENE_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / GRAPHENE_100_PERCENT);
std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(),
[]( const witness_object* a, const witness_object* b )
{
return a->last_confirmed_block_num < b->last_confirmed_block_num;
} );
uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num;
if( new_last_irreversible_block_num > dpo.last_irreversible_block_num )
{
modify( dpo, [&]( dynamic_global_property_object& _dpo )
{
_dpo.last_irreversible_block_num = new_last_irreversible_block_num;
} );
}
}
void database::clear_expired_transactions()
{ try {
//Look for expired transactions in the deduplication list, and remove them.
//Transactions must have expired by at least two forking windows in order to be removed.
auto& transaction_idx = static_cast<transaction_index&>(get_mutable_index(implementation_ids, impl_transaction_object_type));
const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->trx.expiration) )
transaction_idx.remove(*dedupe_index.rbegin());
} FC_CAPTURE_AND_RETHROW() }
void database::clear_expired_proposals()
{
const auto& proposal_expiration_index = get_index_type<proposal_index>().indices().get<by_expiration>();
while( !proposal_expiration_index.empty() && proposal_expiration_index.begin()->expiration_time <= head_block_time() )
{
const proposal_object& proposal = *proposal_expiration_index.begin();
processed_transaction result;
try {
if( proposal.is_authorized_to_execute(*this) )
{
result = push_proposal(proposal);
//TODO: Do something with result so plugins can process it.
continue;
}
} catch( const fc::exception& e ) {
elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
("proposal", proposal)("error", e.to_detail_string()));
}
remove(proposal);
}
}
/**
* let HB = the highest bid for the collateral (aka who will pay the most DEBT for the least collateral)
* let SP = current median feed's Settlement Price
* let LC = the least collateralized call order's swan price (debt/collateral)
*
* If there is no valid price feed or no bids then there is no black swan.
*
* A black swan occurs if MAX(HB,SP) <= LC
*/
bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan )
{
if( !mia.is_market_issued() ) return false;
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
if( bitasset.has_settlement() ) return true; // already force settled
auto settle_price = bitasset.current_feed.settlement_price;
if( settle_price.is_null() ) return false; // no feed
const call_order_index& call_index = get_index_type<call_order_index>();
const auto& call_price_index = call_index.indices().get<by_price>();
const limit_order_index& limit_index = get_index_type<limit_order_index>();
const auto& limit_price_index = limit_index.indices().get<by_price>();
// looking for limit orders selling the most USD for the least CORE
auto highest_possible_bid = price::max( mia.id, bitasset.options.short_backing_asset );
// stop when limit orders are selling too little USD for too much CORE
auto lowest_possible_bid = price::min( mia.id, bitasset.options.short_backing_asset );
assert( highest_possible_bid.base.asset_id == lowest_possible_bid.base.asset_id );
// NOTE limit_price_index is sorted from greatest to least
auto limit_itr = limit_price_index.lower_bound( highest_possible_bid );
auto limit_end = limit_price_index.upper_bound( lowest_possible_bid );
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
auto call_itr = call_price_index.lower_bound( call_min );
auto call_end = call_price_index.upper_bound( call_max );
if( call_itr == call_end ) return false; // no call orders
price highest = settle_price;
if( limit_itr != limit_end ) {
assert( settle_price.base.asset_id == limit_itr->sell_price.base.asset_id );
highest = std::max( limit_itr->sell_price, settle_price );
}
auto least_collateral = call_itr->collateralization();
if( ~least_collateral >= highest )
{
elog( "Black Swan detected: \n"
" Least collateralized call: ${lc} ${~lc}\n"
// " Highest Bid: ${hb} ${~hb}\n"
" Settle Price: ${sp} ${~sp}\n"
" Max: ${h} ${~h}\n",
("lc",least_collateral.to_real())("~lc",(~least_collateral).to_real())
// ("hb",limit_itr->sell_price.to_real())("~hb",(~limit_itr->sell_price).to_real())
("sp",settle_price.to_real())("~sp",(~settle_price).to_real())
("h",highest.to_real())("~h",(~highest).to_real()) );
FC_ASSERT( enable_black_swan, "Black swan was detected during a margin update which is not allowed to trigger a blackswan" );
globally_settle_asset(mia, ~least_collateral );
return true;
}
return false;
}
void database::clear_expired_orders()
{ try {
detail::with_skip_flags( *this,
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;
canceler.fee = current_fee_schedule().calculate_fee( canceler );
if( canceler.fee.amount > order.deferred_fee )
{
// Cap auto-cancel fees at deferred_fee; see #549
wlog( "At block ${b}, fee for clearing expired order ${oid} was capped at deferred_fee ${fee}", ("b", head_block_num())("oid", order.id)("fee", order.deferred_fee) );
canceler.fee = asset( order.deferred_fee, asset_id_type() );
}
// we know the fee for this op is set correctly since it is set by the chain.
// this allows us to avoid a hung chain:
// - if #549 case above triggers
// - if the fee is incorrect, which may happen due to #435 (although since cancel is a fixed-fee op, it shouldn't)
cancel_context.skip_fee_schedule_check = true;
apply_operation(cancel_context, canceler);
}
});
//Process expired force settlement orders
auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>();
if( !settlement_index.empty() )
{
asset_id_type current_asset = settlement_index.begin()->settlement_asset_id();
asset max_settlement_volume;
bool extra_dump = false;
auto next_asset = [&current_asset, &settlement_index, &extra_dump] {
auto bound = settlement_index.upper_bound(current_asset);
if( bound == settlement_index.end() )
{
if( extra_dump )
{
ilog( "next_asset() returning false" );
}
return false;
}
if( extra_dump )
{
ilog( "next_asset returning true, bound is ${b}", ("b", *bound) );
}
current_asset = bound->settlement_asset_id();
return true;
};
uint32_t count = 0;
// At each iteration, we either consume the current order and remove it, or we move to the next asset
for( auto itr = settlement_index.lower_bound(current_asset);
itr != settlement_index.end();
itr = settlement_index.lower_bound(current_asset) )
{
++count;
const force_settlement_object& order = *itr;
auto order_id = order.id;
current_asset = order.settlement_asset_id();
const asset_object& mia_object = get(current_asset);
const asset_bitasset_data_object& mia = mia_object.bitasset_data(*this);
extra_dump = ((count >= 1000) && (count <= 1020));
if( extra_dump )
{
wlog( "clear_expired_orders() dumping extra data for iteration ${c}", ("c", count) );
ilog( "head_block_num is ${hb} current_asset is ${a}", ("hb", head_block_num())("a", current_asset) );
}
if( mia.has_settlement() )
{
ilog( "Canceling a force settlement because of black swan" );
cancel_order( order );
continue;
}
// Has this order not reached its settlement date?
if( order.settlement_date > head_block_time() )
{
if( next_asset() )
{
if( extra_dump )
{
ilog( "next_asset() returned true when order.settlement_date > head_block_time()" );
}
continue;
}
break;
}
// Can we still settle in this asset?
if( mia.current_feed.settlement_price.is_null() )
{
ilog("Canceling a force settlement in ${asset} because settlement price is null",
("asset", mia_object.symbol));
cancel_order(order);
continue;
}
if( max_settlement_volume.asset_id != current_asset )
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() )
{
if( extra_dump )
{
ilog( "next_asset() returned true when mia.force_settled_volume >= max_settlement_volume.amount" );
}
continue;
}
break;
}
auto& pays = order.balance;
auto receives = (order.balance * mia.current_feed.settlement_price);
receives.amount = (fc::uint128_t(receives.amount.value) *
(GRAPHENE_100_PERCENT - mia.options.force_settlement_offset_percent) / GRAPHENE_100_PERCENT).to_uint64();
assert(receives <= order.balance * mia.current_feed.settlement_price);
price settlement_price = pays / receives;
auto& call_index = get_index_type<call_order_index>().indices().get<by_collateral>();
asset settled = mia_object.amount(mia.force_settled_volume);
// Match against the least collateralized short until the settlement is finished or we reach max settlements
while( settled < max_settlement_volume && find_object(order_id) )
{
auto itr = call_index.lower_bound(boost::make_tuple(price::min(mia_object.bitasset_data(*this).options.short_backing_asset,
mia_object.get_id())));
// There should always be a call order, since asset exists!
assert(itr != call_index.end() && itr->debt_type() == mia_object.get_id());
asset max_settlement = max_settlement_volume - settled;
if( order.balance.amount == 0 )
{
wlog( "0 settlement detected" );
cancel_order( order );
break;
}
try {
settled += match(*itr, order, settlement_price, max_settlement);
}
catch ( const black_swan_exception& e ) {
wlog( "black swan detected: ${e}", ("e", e.to_detail_string() ) );
cancel_order( order );
break;
}
}
if( mia.force_settled_volume != settled.amount )
{
modify(mia, [settled](asset_bitasset_data_object& b) {
b.force_settled_volume = settled.amount;
});
}
}
}
} FC_CAPTURE_AND_RETHROW() }
void database::update_expired_feeds()
{
auto& asset_idx = get_index_type<asset_index>().indices().get<by_type>();
auto itr = asset_idx.lower_bound( true /** market issued */ );
while( itr != asset_idx.end() )
{
const asset_object& a = *itr;
++itr;
assert( a.is_market_issued() );
const asset_bitasset_data_object& b = a.bitasset_data(*this);
bool feed_is_expired;
if( head_block_time() < HARDFORK_615_TIME )
feed_is_expired = b.feed_is_expired_before_hardfork_615( head_block_time() );
else
feed_is_expired = b.feed_is_expired( head_block_time() );
if( feed_is_expired )
{
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_maintenance_flag( bool new_maintenance_flag )
{
modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& dpo )
{
auto maintenance_flag = dynamic_global_property_object::maintenance_flag;
dpo.dynamic_flags =
(dpo.dynamic_flags & ~maintenance_flag)
| (new_maintenance_flag ? maintenance_flag : 0);
} );
return;
}
void database::update_withdraw_permissions()
{
auto& permit_index = get_index_type<withdraw_permission_index>().indices().get<by_expiration>();
while( !permit_index.empty() && permit_index.begin()->expiration <= head_block_time() )
remove(*permit_index.begin());
}
uint64_t database::get_random_bits( uint64_t bound )
{
return _random_number_generator(bound);
}
void process_finished_games(database& db)
{
//auto& games_index = db.get_index_type<game_index>().indices().get<by_id>();
}
void process_finished_matches(database& db)
{
}
void process_in_progress_tournaments(database& db)
{
auto& start_time_index = db.get_index_type<tournament_index>().indices().get<by_start_time>();
auto start_iter = start_time_index.lower_bound(boost::make_tuple(tournament_state::in_progress));
while (start_iter != start_time_index.end() &&
start_iter->get_state() == tournament_state::in_progress)
{
auto next_iter = std::next(start_iter);
start_iter->check_for_new_matches_to_start(db);
start_iter = next_iter;
}
}
void cancel_expired_tournaments(database& db)
{
// First, cancel any tournaments that didn't get enough players
auto& registration_deadline_index = db.get_index_type<tournament_index>().indices().get<by_registration_deadline>();
// this index is sorted on state and deadline, so the tournaments awaiting registrations with the earliest
// deadlines will be at the beginning
while (!registration_deadline_index.empty() &&
registration_deadline_index.begin()->get_state() == tournament_state::accepting_registrations &&
registration_deadline_index.begin()->options.registration_deadline <= db.head_block_time())
{
const tournament_object& tournament_obj = *registration_deadline_index.begin();
fc_ilog(fc::logger::get("tournament"),
"Canceling tournament ${id} because its deadline expired",
("id", tournament_obj.id));
// cancel this tournament
db.modify(tournament_obj, [&](tournament_object& t) {
t.on_registration_deadline_passed(db);
});
}
}
void start_fully_registered_tournaments(database& db)
{
// Next, start any tournaments that have enough players and whose start time just arrived
auto& start_time_index = db.get_index_type<tournament_index>().indices().get<by_start_time>();
while (1)
{
// find the first tournament waiting to start; if its start time has arrived, start it
auto start_iter = start_time_index.lower_bound(boost::make_tuple(tournament_state::awaiting_start));
if (start_iter != start_time_index.end() &&
start_iter->get_state() == tournament_state::awaiting_start &&
*start_iter->start_time <= db.head_block_time())
{
db.modify(*start_iter, [&](tournament_object& t) {
t.on_start_time_arrived(db);
});
}
else
break;
}
}
void initiate_next_round_of_matches(database& db)
{
}
void initiate_next_games(database& db)
{
// Next, trigger timeouts on any games which have been waiting too long for commit or
// reveal moves
auto& next_timeout_index = db.get_index_type<game_index>().indices().get<by_next_timeout>();
while (1)
{
// empty time_points are sorted to the beginning, so upper_bound takes us to the first
// non-empty time_point
auto start_iter = next_timeout_index.upper_bound(boost::make_tuple(optional<time_point_sec>()));
if (start_iter != next_timeout_index.end() &&
*start_iter->next_timeout <= db.head_block_time())
{
db.modify(*start_iter, [&](game_object& game) {
game.on_timeout(db);
});
}
else
break;
}
}
void database::update_tournaments()
{
// Process as follows:
// - Process games
// - Process matches
// - Process tournaments
// - Process matches
// - Process games
process_finished_games(*this);
process_finished_matches(*this);
cancel_expired_tournaments(*this);
start_fully_registered_tournaments(*this);
process_in_progress_tournaments(*this);
initiate_next_round_of_matches(*this);
initiate_next_games(*this);
}
} }

View file

@ -0,0 +1,245 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
namespace graphene { namespace chain {
using boost::container::flat_set;
witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
{
witness_id_type wid;
const global_property_object& gpo = get_global_properties();
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
{
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
uint64_t current_aslot = dpo.current_aslot + slot_num;
return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ];
}
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM &&
slot_num != 0 )
{
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
// ask the near scheduler who goes in the given slot
bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid);
if(! slot_is_near)
{
// if the near scheduler doesn't know, we have to extend it to
// a far scheduler.
// n.b. instantiating it is slow, but block gaps long enough to
// need it are likely pretty rare.
witness_scheduler_rng far_rng(wso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV);
far_future_witness_scheduler far_scheduler =
far_future_witness_scheduler(wso.scheduler, far_rng);
if(!far_scheduler.get_slot(slot_num-1, wid))
{
// no scheduled witness -- somebody set up us the bomb
// n.b. this code path is impossible, the present
// implementation of far_future_witness_scheduler
// returns true unconditionally
assert( false );
}
}
}
return wid;
}
fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
{
if( slot_num == 0 )
return fc::time_point_sec();
auto interval = block_interval();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
if( head_block_num() == 0 )
{
// n.b. first block is at genesis_time plus one block interval
fc::time_point_sec genesis_time = dpo.time;
return genesis_time + slot_num * interval;
}
int64_t head_block_abs_slot = head_block_time().sec_since_epoch() / interval;
fc::time_point_sec head_slot_time(head_block_abs_slot * interval);
const global_property_object& gpo = get_global_properties();
if( dpo.dynamic_flags & dynamic_global_property_object::maintenance_flag )
slot_num += gpo.parameters.maintenance_skip_slots;
// "slot 0" is head_slot_time
// "slot 1" is head_slot_time,
// plus maint interval if head block is a maint block
// plus block interval if head block is not a maint block
return head_slot_time + (slot_num * interval);
}
uint32_t database::get_slot_at_time(fc::time_point_sec when)const
{
fc::time_point_sec first_slot_time = get_slot_time( 1 );
//@ROL std::cout << "@get_slot_at_time " << when.to_iso_string() << " " << first_slot_time.to_iso_string() << "\n";
if( when < first_slot_time )
return 0;
return (when - first_slot_time).to_seconds() / block_interval() + 1;
}
void database::update_witness_schedule()
{
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
const global_property_object& gpo = get_global_properties();
if( head_block_num() % gpo.active_witnesses.size() == 0 )
{
modify( wso, [&]( witness_schedule_object& _wso )
{
_wso.current_shuffled_witnesses.clear();
_wso.current_shuffled_witnesses.reserve( gpo.active_witnesses.size() );
for( const witness_id_type& w : gpo.active_witnesses )
_wso.current_shuffled_witnesses.push_back( w );
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
for( uint32_t i = 0; i < _wso.current_shuffled_witnesses.size(); ++i )
{
/// High performance random generator
/// http://xorshift.di.unimi.it/
uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;
k ^= (k >> 12);
k ^= (k << 25);
k ^= (k >> 27);
k *= 2685821657736338717ULL;
uint32_t jmax = _wso.current_shuffled_witnesses.size() - i;
uint32_t j = i + k%jmax;
std::swap( _wso.current_shuffled_witnesses[i],
_wso.current_shuffled_witnesses[j] );
}
});
}
}
vector<witness_id_type> database::get_near_witness_schedule()const
{
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
vector<witness_id_type> result;
result.reserve(wso.scheduler.size());
uint32_t slot_num = 0;
witness_id_type wid;
while( wso.scheduler.get_slot(slot_num++, wid) )
result.emplace_back(wid);
return result;
}
void database::update_witness_schedule(const signed_block& next_block)
{
auto start = fc::time_point::now();
const global_property_object& gpo = get_global_properties();
const witness_schedule_object& wso = get(witness_schedule_id_type());
uint32_t schedule_needs_filled = gpo.active_witnesses.size();
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
// We shouldn't be able to generate _pending_block with timestamp
// in the past, and incoming blocks from the network with timestamp
// in the past shouldn't be able to make it this far without
// 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() );
modify(wso, [&](witness_schedule_object& _wso)
{
_wso.slots_since_genesis += schedule_slot;
witness_scheduler_rng rng(wso.rng_seed.data, _wso.slots_since_genesis);
_wso.scheduler._min_token_count = std::max(int(gpo.active_witnesses.size()) / 2, 1);
if( slot_is_near )
{
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) )
{
if( _wso.scheduler.produce_schedule(rng) & emit_turn )
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);
});
auto end = fc::time_point::now();
static uint64_t total_time = 0;
static uint64_t calls = 0;
total_time += (end - start).count();
if( ++calls % 1000 == 0 )
idump( ( double(total_time/1000000.0)/calls) );
}
uint32_t database::witness_participation_rate()const
{
const global_property_object& gpo = get_global_properties();
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
{
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
return uint64_t(GRAPHENE_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128;
}
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
{
const witness_schedule_object& wso = get(witness_schedule_id_type());
return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128;
}
return 0;
}
} }

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/fba_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(); }
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)
{
const database& d = db();
fee_from_account = fee;
FC_ASSERT( fee.amount >= 0 );
fee_paying_account = &account_id(d);
fee_paying_account_statistics = &fee_paying_account->statistics(d);
fee_asset = &fee.asset_id(d);
fee_asset_dyn_data = &fee_asset->dynamic_asset_data_id(d);
if( d.head_block_time() > HARDFORK_419_TIME )
{
FC_ASSERT( is_authorized_asset( d, *fee_paying_account, *fee_asset ), "Account ${acct} '${name}' attempted to pay fee by using asset ${a} '${sym}', which is unauthorized due to whitelist / blacklist",
("acct", fee_paying_account->id)("name", fee_paying_account->name)("a", fee_asset->id)("sym", fee_asset->symbol) );
}
if( fee_from_account.asset_id == asset_id_type() )
core_fee_paid = fee_from_account.amount;
else
{
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, "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)) );
}
}
void generic_evaluator::convert_fee()
{
if( !trx_state->skip_fee ) {
if( fee_asset->get_id() != asset_id_type() )
{
db().modify(*fee_asset_dyn_data, [this](asset_dynamic_data_object& d) {
d.accumulated_fees += fee_from_account.amount;
d.fee_pool -= core_fee_paid;
});
}
}
}
void generic_evaluator::pay_fee()
{ try {
if( !trx_state->skip_fee ) {
database& d = db();
/// TODO: db().pay_fee( account_id, core_fee );
d.modify(*fee_paying_account_statistics, [&](account_statistics_object& s)
{
s.pay_fee( core_fee_paid, d.get_global_properties().parameters.cashback_vesting_threshold );
});
}
} FC_CAPTURE_AND_RETHROW() }
void generic_evaluator::pay_fba_fee( uint64_t fba_id )
{
database& d = db();
const fba_accumulator_object& fba = d.get< fba_accumulator_object >( fba_accumulator_id_type( fba_id ) );
if( !fba.is_configured(d) )
{
generic_evaluator::pay_fee();
return;
}
d.modify( fba, [&]( fba_accumulator_object& _fba )
{
_fba.accumulated_fba_fees += core_fee_paid;
} );
}
share_type generic_evaluator::calculate_fee_for_operation(const operation& op) const
{
return db().current_fee_schedule().calculate_fee( op ).amount;
}
void generic_evaluator::db_adjust_balance(const account_id_type& fee_payer, asset fee_from_account)
{
db().adjust_balance(fee_payer, fee_from_account);
}
} }

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/fba_object.hpp>
namespace graphene { namespace chain {
bool fba_accumulator_object::is_configured( const database& db )const
{
if( !designated_asset.valid() )
{
ilog( "FBA fee in block ${b} not paid because designated asset was not configured", ("b", db.head_block_num()) );
return false;
}
const asset_object* dasset = db.find(*designated_asset);
if( dasset == nullptr )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset does not exist", ("b", db.head_block_num()) );
return false;
}
if( dasset->is_market_issued() )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: FBA is a BitAsset", ("b", db.head_block_num()) );
return false;
}
const uint16_t allowed_flags = charge_market_fee;
// check enabled issuer_permissions bits is subset of allowed_flags bits
if( (dasset->options.issuer_permissions & allowed_flags) != dasset->options.issuer_permissions )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: Disallowed permissions enabled", ("b", db.head_block_num()) );
return false;
}
// check enabled issuer_permissions bits is subset of allowed_flags bits
if( (dasset->options.flags & allowed_flags) != dasset->options.flags )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: Disallowed flags enabled", ("b", db.head_block_num()) );
return false;
}
if( !dasset->buyback_account.valid() )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset does not have a buyback account", ("b", db.head_block_num()) );
return false;
}
const account_object& issuer_acct = dasset->issuer(db);
if( issuer_acct.owner_special_authority.which() != special_authority::tag< top_holders_special_authority >::value )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer has not set owner top_n control", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.active_special_authority.which() != special_authority::tag< top_holders_special_authority >::value )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer has not set active top_n control", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.owner_special_authority.get< top_holders_special_authority >().asset != *designated_asset )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer's top_n_control is not set to designated asset", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.active_special_authority.get< top_holders_special_authority >().asset != *designated_asset )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer's top_n_control is not set to designated asset", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.top_n_control_flags != (account_object::top_n_control_owner | account_object::top_n_control_active) )
{
ilog( "FBA fee in block ${b} not paid because designated asset's top_n control has not yet activated (wait until next maintenance interval)", ("b", db.head_block_num()) );
return false;
}
return true;
}
} }

View file

@ -0,0 +1,253 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/fork_database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
fork_database::fork_database()
{
}
void fork_database::reset()
{
_head.reset();
_index.clear();
}
void fork_database::pop_block()
{
FC_ASSERT( _head, "no blocks to pop" );
auto prev = _head->prev.lock();
FC_ASSERT( prev, "poping block would leave head block null" );
_head = prev;
}
void fork_database::start_block(signed_block b)
{
auto item = std::make_shared<fork_item>(std::move(b));
_index.insert(item);
_head = item;
}
/**
* Pushes the block into the fork database and caches it if it doesn't link
*
*/
shared_ptr<fork_item> fork_database::push_block(const signed_block& b)
{
auto item = std::make_shared<fork_item>(b);
try {
_push_block(item);
}
catch ( const unlinkable_block_exception& e )
{
wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) );
wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) );
throw;
_unlinked_index.insert( item );
}
return _head;
}
void fork_database::_push_block(const item_ptr& item)
{
if( _head ) // make sure the block is within the range that we are caching
{
FC_ASSERT( item->num > std::max<int64_t>( 0, int64_t(_head->num) - (_max_size) ),
"attempting to push a block that is too old",
("item->num",item->num)("head",_head->num)("max_size",_max_size));
}
if( _head && item->previous_id() != block_id_type() )
{
auto& index = _index.get<block_id>();
auto itr = index.find(item->previous_id());
GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain");
FC_ASSERT(!(*itr)->invalid);
item->prev = *itr;
}
_index.insert(item);
if( !_head ) _head = item;
else if( item->num > _head->num )
{
_head = item;
uint32_t min_num = _head->num - std::min( _max_size, _head->num );
// ilog( "min block in fork DB ${n}, max_size: ${m}", ("n",min_num)("m",_max_size) );
auto& num_idx = _index.get<block_num>();
while( num_idx.size() && (*num_idx.begin())->num < min_num )
num_idx.erase( num_idx.begin() );
_unlinked_index.get<block_num>().erase(_head->num - _max_size);
}
//_push_next( item );
}
/**
* Iterate through the unlinked cache and insert anything that
* links to the newly inserted item. This will start a recursive
* set of calls performing a depth-first insertion of pending blocks as
* _push_next(..) calls _push_block(...) which will in turn call _push_next
*/
void fork_database::_push_next( const item_ptr& new_item )
{
auto& prev_idx = _unlinked_index.get<by_previous>();
auto itr = prev_idx.find( new_item->id );
while( itr != prev_idx.end() )
{
auto tmp = *itr;
prev_idx.erase( itr );
_push_block( tmp );
itr = prev_idx.find( new_item->id );
}
}
void fork_database::set_max_size( uint32_t s )
{
_max_size = s;
if( !_head ) return;
{ /// index
auto& by_num_idx = _index.get<block_num>();
auto itr = by_num_idx.begin();
while( itr != by_num_idx.end() )
{
if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) )
by_num_idx.erase(itr);
else
break;
itr = by_num_idx.begin();
}
}
{ /// unlinked_index
auto& by_num_idx = _unlinked_index.get<block_num>();
auto itr = by_num_idx.begin();
while( itr != by_num_idx.end() )
{
if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) )
by_num_idx.erase(itr);
else
break;
itr = by_num_idx.begin();
}
}
}
bool fork_database::is_known_block(const block_id_type& id)const
{
auto& index = _index.get<block_id>();
auto itr = index.find(id);
if( itr != index.end() )
return true;
auto& unlinked_index = _unlinked_index.get<block_id>();
auto unlinked_itr = unlinked_index.find(id);
return unlinked_itr != unlinked_index.end();
}
item_ptr fork_database::fetch_block(const block_id_type& id)const
{
auto& index = _index.get<block_id>();
auto itr = index.find(id);
if( itr != index.end() )
return *itr;
auto& unlinked_index = _unlinked_index.get<block_id>();
auto unlinked_itr = unlinked_index.find(id);
if( unlinked_itr != unlinked_index.end() )
return *unlinked_itr;
return item_ptr();
}
vector<item_ptr> fork_database::fetch_block_by_number(uint32_t num)const
{
vector<item_ptr> result;
auto itr = _index.get<block_num>().find(num);
while( itr != _index.get<block_num>().end() )
{
if( (*itr)->num == num )
result.push_back( *itr );
else
break;
++itr;
}
return result;
}
pair<fork_database::branch_type,fork_database::branch_type>
fork_database::fetch_branch_from(block_id_type first, block_id_type second)const
{ try {
// This function gets a branch (i.e. vector<fork_item>) leading
// back to the most recent common ancestor.
pair<branch_type,branch_type> result;
auto first_branch_itr = _index.get<block_id>().find(first);
FC_ASSERT(first_branch_itr != _index.get<block_id>().end());
auto first_branch = *first_branch_itr;
auto second_branch_itr = _index.get<block_id>().find(second);
FC_ASSERT(second_branch_itr != _index.get<block_id>().end());
auto second_branch = *second_branch_itr;
while( first_branch->data.block_num() > second_branch->data.block_num() )
{
result.first.push_back(first_branch);
first_branch = first_branch->prev.lock();
FC_ASSERT(first_branch);
}
while( second_branch->data.block_num() > first_branch->data.block_num() )
{
result.second.push_back( second_branch );
second_branch = second_branch->prev.lock();
FC_ASSERT(second_branch);
}
while( first_branch->data.previous != second_branch->data.previous )
{
result.first.push_back(first_branch);
result.second.push_back(second_branch);
first_branch = first_branch->prev.lock();
FC_ASSERT(first_branch);
second_branch = second_branch->prev.lock();
FC_ASSERT(second_branch);
}
if( first_branch && second_branch )
{
result.first.push_back(first_branch);
result.second.push_back(second_branch);
}
return result;
} FC_CAPTURE_AND_RETHROW( (first)(second) ) }
void fork_database::set_head(shared_ptr<fork_item> h)
{
_head = h;
}
void fork_database::remove(block_id_type id)
{
_index.get<block_id>().erase(id);
}
} } // graphene::chain

View file

@ -0,0 +1,583 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/match_object.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/msm/back/tools.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <fc/crypto/hash_ctr_rng.hpp>
namespace graphene { namespace chain {
namespace msm = boost::msm;
namespace mpl = boost::mpl;
namespace
{
// Events
struct initiate_game
{
database& db;
vector<account_id_type> players;
initiate_game(database& db, const vector<account_id_type>& players) :
db(db), players(players)
{}
};
struct game_move
{
database& db;
const game_move_operation& move;
game_move(database& db, const game_move_operation& move) :
db(db), move(move)
{}
};
struct timeout
{
database& db;
timeout(database& db) :
db(db)
{}
};
struct game_state_machine_ : public msm::front::state_machine_def<game_state_machine_>
{
// disable a few state machine features we don't use for performance
typedef int no_exception_thrown;
typedef int no_message_queue;
// States
struct waiting_for_game_to_start : public msm::front::state<> {};
struct expecting_commit_moves : public msm::front::state<>
{
void set_next_timeout(database& db, game_object& game)
{
const match_object& match_obj = game.match_id(db);
const tournament_object& tournament_obj = match_obj.tournament_id(db);
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
game.next_timeout = db.head_block_time() + game_options.time_per_commit_move;
}
void on_entry(const initiate_game& event, game_state_machine_& fsm)
{
game_object& game = *fsm.game_obj;
fc_ilog(fc::logger::get("tournament"),
"game ${id} is now in progress, expecting commit moves",
("id", game.id));
fc_ilog(fc::logger::get("tournament"),
"game ${id} is associtated with match ${match_id}",
("id", game.id)
("match_id", game.match_id));
set_next_timeout(event.db, game);
}
void on_entry(const game_move& event, game_state_machine_& fsm)
{
game_object& game = *fsm.game_obj;
fc_ilog(fc::logger::get("tournament"),
"game ${id} received a commit move, still expecting another commit move",
("id", game.id));
}
};
struct expecting_reveal_moves : public msm::front::state<>
{
void set_next_timeout(database& db, game_object& game)
{
const match_object& match_obj = game.match_id(db);
const tournament_object& tournament_obj = match_obj.tournament_id(db);
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
game.next_timeout = db.head_block_time() + game_options.time_per_reveal_move;
}
void on_entry(const timeout& event, game_state_machine_& fsm)
{
game_object& game = *fsm.game_obj;
fc_ilog(fc::logger::get("tournament"),
"game ${id} timed out waiting for commit moves, now expecting reveal move",
("id", game.id));
set_next_timeout(event.db, game);
}
void on_entry(const game_move& event, game_state_machine_& fsm)
{
game_object& game = *fsm.game_obj;
if (event.move.move.which() == game_specific_moves::tag<rock_paper_scissors_throw_commit>::value)
{
fc_ilog(fc::logger::get("tournament"),
"game ${id} received a commit move, now expecting reveal moves",
("id", game.id));
set_next_timeout(event.db, game);
}
else
fc_ilog(fc::logger::get("tournament"),
"game ${id} received a reveal move, still expecting reveal moves",
("id", game.id));
}
};
struct game_complete : public msm::front::state<>
{
void clear_next_timeout(database& db, game_object& game)
{
//const match_object& match_obj = game.match_id(db);
//const tournament_object& tournament_obj = match_obj.tournament_id(db);
//const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
game.next_timeout = fc::optional<fc::time_point_sec>();
}
void on_entry(const timeout& event, game_state_machine_& fsm)
{
game_object& game = *fsm.game_obj;
fc_ilog(fc::logger::get("tournament"),
"timed out waiting for commits or reveals, game ${id} is complete",
("id", game.id));
game.make_automatic_moves(event.db);
game.determine_winner(event.db);
clear_next_timeout(event.db, game);
}
void on_entry(const game_move& event, game_state_machine_& fsm)
{
game_object& game = *fsm.game_obj;
fc_ilog(fc::logger::get("tournament"),
"received a reveal move, game ${id} is complete",
("id", fsm.game_obj->id));
// if one player didn't commit a move we might need to make their "insurance" move now
game.make_automatic_moves(event.db);
game.determine_winner(event.db);
clear_next_timeout(event.db, game);
}
};
typedef waiting_for_game_to_start initial_state;
typedef game_state_machine_ x; // makes transition table cleaner
// Guards
bool already_have_other_commit(const game_move& event)
{
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
event.move.player_account_id);
unsigned player_index = std::distance(game_obj->players.begin(), iter);
// hard-coded here for two-player games
unsigned other_player_index = player_index == 0 ? 1 : 0;
const rock_paper_scissors_game_details& game_details = game_obj->game_details.get<rock_paper_scissors_game_details>();
return game_details.commit_moves.at(other_player_index).valid();
}
bool now_have_reveals_for_all_commits(const game_move& event)
{
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
event.move.player_account_id);
unsigned this_reveal_index = std::distance(game_obj->players.begin(), iter);
const rock_paper_scissors_game_details& game_details = game_obj->game_details.get<rock_paper_scissors_game_details>();
for (unsigned i = 0; i < game_details.commit_moves.size(); ++i)
if (game_details.commit_moves[i] && !game_details.reveal_moves[i] && i != this_reveal_index)
return false;
return true;
}
bool have_at_least_one_commit_move(const timeout& event)
{
const rock_paper_scissors_game_details& game_details = game_obj->game_details.get<rock_paper_scissors_game_details>();
return game_details.commit_moves[0] || game_details.commit_moves[1];
}
void apply_commit_move(const game_move& event)
{
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
event.move.player_account_id);
unsigned player_index = std::distance(game_obj->players.begin(), iter);
rock_paper_scissors_game_details& details = game_obj->game_details.get<rock_paper_scissors_game_details>();
details.commit_moves[player_index] = event.move.move.get<rock_paper_scissors_throw_commit>();
}
void apply_reveal_move(const game_move& event)
{
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
event.move.player_account_id);
unsigned player_index = std::distance(game_obj->players.begin(), iter);
rock_paper_scissors_game_details& details = game_obj->game_details.get<rock_paper_scissors_game_details>();
details.reveal_moves[player_index] = event.move.move.get<rock_paper_scissors_throw_reveal>();
}
void start_next_game(const game_complete& event)
{
fc_ilog(fc::logger::get("tournament"),
"In start_next_game action");
}
// Transition table for tournament
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
_row < waiting_for_game_to_start, initiate_game, expecting_commit_moves >,
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
a_row < expecting_commit_moves, game_move, expecting_commit_moves, &x::apply_commit_move >,
row < expecting_commit_moves, game_move, expecting_reveal_moves, &x::apply_commit_move, &x::already_have_other_commit >,
_row < expecting_commit_moves, timeout, game_complete >,
g_row < expecting_commit_moves, timeout, expecting_reveal_moves, &x::have_at_least_one_commit_move >,
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
_row < expecting_reveal_moves, timeout, game_complete >,
a_row < expecting_reveal_moves, game_move, expecting_reveal_moves, &x::apply_reveal_move >,
row < expecting_reveal_moves, game_move, game_complete, &x::apply_reveal_move, &x::now_have_reveals_for_all_commits >
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
//a_row < game_in_progress, game_complete, game_in_progress, &x::start_next_game >,
//g_row < game_in_progress, game_complete, game_complete, &x::was_final_game >
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
> {};
game_object* game_obj;
game_state_machine_(game_object* game_obj) : game_obj(game_obj) {}
};
typedef msm::back::state_machine<game_state_machine_> game_state_machine;
}
class game_object::impl {
public:
game_state_machine state_machine;
impl(game_object* self) : state_machine(self) {}
};
game_object::game_object() :
my(new impl(this))
{
}
game_object::game_object(const game_object& rhs) :
graphene::db::abstract_object<game_object>(rhs),
match_id(rhs.match_id),
players(rhs.players),
winners(rhs.winners),
game_details(rhs.game_details),
next_timeout(rhs.next_timeout),
my(new impl(this))
{
my->state_machine = rhs.my->state_machine;
my->state_machine.game_obj = this;
}
game_object& game_object::operator=(const game_object& rhs)
{
//graphene::db::abstract_object<game_object>::operator=(rhs);
id = rhs.id;
match_id = rhs.match_id;
players = rhs.players;
winners = rhs.winners;
game_details = rhs.game_details;
next_timeout = rhs.next_timeout;
my->state_machine = rhs.my->state_machine;
my->state_machine.game_obj = this;
return *this;
}
game_object::~game_object()
{
}
bool verify_game_state_constants()
{
unsigned error_count = 0;
typedef msm::back::generate_state_set<game_state_machine::stt>::type all_states;
static char const* filled_state_names[mpl::size<all_states>::value];
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
(msm::back::fill_state_names<game_state_machine::stt>(filled_state_names));
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
{
try
{
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
// at least contain the string we're looking for
const char* fc_reflected_value_name = fc::reflector<game_state>::to_string((game_state)i);
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
fc_elog(fc::logger::get("game"),
"Error, state string misgame between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
}
catch (const fc::bad_cast_exception&)
{
fc_elog(fc::logger::get("game"),
"Error, no reflection for value ${int_value} in enum game_state",
("int_value", i));
++error_count;
}
}
return error_count == 0;
}
game_state game_object::get_state() const
{
static bool state_constants_are_correct = verify_game_state_constants();
(void)&state_constants_are_correct;
game_state state = (game_state)my->state_machine.current_state()[0];
return state;
}
void game_object::evaluate_move_operation(const database& db, const game_move_operation& op) const
{
//const match_object& match_obj = match_id(db);
if (game_details.which() == game_specific_details::tag<rock_paper_scissors_game_details>::value)
{
if (op.move.which() == game_specific_moves::tag<rock_paper_scissors_throw_commit>::value)
{
// Is this move made by a player in the match
auto iter = std::find(players.begin(), players.end(),
op.player_account_id);
if (iter == players.end())
FC_THROW("Player ${account_id} is not a player in game ${game}",
("account_id", op.player_account_id)
("game", id));
unsigned player_index = std::distance(players.begin(), iter);
//const rock_paper_scissors_throw_commit& commit = op.move.get<rock_paper_scissors_throw_commit>();
// are we expecting commits?
if (get_state() != game_state::expecting_commit_moves)
FC_THROW("Game ${game} is not accepting any commit moves", ("game", id));
// has this player committed already?
const rock_paper_scissors_game_details& details = game_details.get<rock_paper_scissors_game_details>();
if (details.commit_moves.at(player_index))
FC_THROW("Player ${account_id} has already committed their move for game ${game}",
("account_id", op.player_account_id)
("game", id));
// if all the above checks pass, then the move is accepted
}
else if (op.move.which() == game_specific_moves::tag<rock_paper_scissors_throw_reveal>::value)
{
// Is this move made by a player in the match
auto iter = std::find(players.begin(), players.end(),
op.player_account_id);
if (iter == players.end())
FC_THROW("Player ${account_id} is not a player in game ${game}",
("account_id", op.player_account_id)
("game", id));
unsigned player_index = std::distance(players.begin(), iter);
// has this player committed already?
const rock_paper_scissors_game_details& details = game_details.get<rock_paper_scissors_game_details>();
if (!details.commit_moves.at(player_index))
FC_THROW("Player ${account_id} cannot reveal a move which they did not commit in game ${game}",
("account_id", op.player_account_id)
("game", id));
// are we expecting reveals?
if (get_state() != game_state::expecting_reveal_moves)
FC_THROW("Game ${game} is not accepting any reveal moves", ("game", id));
const rock_paper_scissors_throw_commit& commit = *details.commit_moves.at(player_index);
const rock_paper_scissors_throw_reveal& reveal = op.move.get<rock_paper_scissors_throw_reveal>();
// does the reveal match the commit?
rock_paper_scissors_throw reconstructed_throw;
reconstructed_throw.nonce1 = commit.nonce1;
reconstructed_throw.nonce2 = reveal.nonce2;
reconstructed_throw.gesture = reveal.gesture;
fc::sha256 reconstructed_hash = reconstructed_throw.calculate_hash();
if (commit.throw_hash != reconstructed_hash)
FC_THROW("Reveal does not match commit's hash of ${commit_hash}",
("commit_hash", commit.throw_hash));
// is the throw valid for this game
const match_object& match_obj = match_id(db);
const tournament_object& tournament_obj = match_obj.tournament_id(db);
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
if ((unsigned)reveal.gesture >= game_options.number_of_gestures)
FC_THROW("Gesture ${gesture_int} is not valid for this game", ("gesture", (unsigned)reveal.gesture));
// if all the above checks pass, then the move is accepted
}
else
FC_THROW("The only valid moves in a rock-paper-scissors game are commit and reveal, not ${type}",
("type", op.move.which()));
}
else
FC_THROW("Game of type ${type} not supported", ("type", game_details.which()));
}
void game_object::make_automatic_moves(database& db)
{
rock_paper_scissors_game_details& rps_game_details = game_details.get<rock_paper_scissors_game_details>();
unsigned players_without_commit_moves = 0;
bool no_player_has_reveal_move = true;
for (unsigned i = 0; i < 2; ++i)
{
if (!rps_game_details.commit_moves[i])
++players_without_commit_moves;
if (rps_game_details.reveal_moves[i])
no_player_has_reveal_move = false;
}
if (players_without_commit_moves || no_player_has_reveal_move)
{
const match_object& match_obj = match_id(db);
const tournament_object& tournament_obj = match_obj.tournament_id(db);
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
if (game_options.insurance_enabled)
{
for (unsigned i = 0; i < 2; ++i)
{
if (!rps_game_details.commit_moves[i] ||
no_player_has_reveal_move)
{
struct rock_paper_scissors_throw_reveal reveal;
reveal.nonce2 = 0;
reveal.gesture = (rock_paper_scissors_gesture)db.get_random_bits(game_options.number_of_gestures);
rps_game_details.reveal_moves[i] = reveal;
ilog("Player ${player} failed to commit a move, generating a random move for him: ${gesture}",
("player", i)("gesture", reveal.gesture));
}
}
}
}
}
void game_object::determine_winner(database& db)
{
// we now know who played what, figure out if we have a winner
const rock_paper_scissors_game_details& rps_game_details = game_details.get<rock_paper_scissors_game_details>();
if (rps_game_details.reveal_moves[0] && rps_game_details.reveal_moves[1] &&
rps_game_details.reveal_moves[0]->gesture == rps_game_details.reveal_moves[1]->gesture)
ilog("The game was a tie, both players threw ${gesture}", ("gesture", rps_game_details.reveal_moves[0]->gesture));
else
{
const match_object& match_obj = match_id(db);
const tournament_object& tournament_obj = match_obj.tournament_id(db);
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
if (rps_game_details.reveal_moves[0] && rps_game_details.reveal_moves[1])
{
unsigned winner = ((((int)rps_game_details.reveal_moves[0]->gesture -
(int)rps_game_details.reveal_moves[1]->gesture +
game_options.number_of_gestures) % game_options.number_of_gestures) + 1) % 2;
ilog("${gesture1} vs ${gesture2}, ${winner} wins",
("gesture1", rps_game_details.reveal_moves[1]->gesture)
("gesture2", rps_game_details.reveal_moves[0]->gesture)
("winner", rps_game_details.reveal_moves[winner]->gesture));
winners.insert(players[winner]);
}
else if (rps_game_details.reveal_moves[0])
{
ilog("Player 1 didn't commit or reveal their move, player 0 wins");
winners.insert(players[0]);
}
else if (rps_game_details.reveal_moves[1])
{
ilog("Player 0 didn't commit or reveal their move, player 1 wins");
winners.insert(players[1]);
}
else
{
ilog("Neither player made a move, both players lose");
}
}
const match_object& match_obj = match_id(db);
db.modify(match_obj, [&](match_object& match) {
match.on_game_complete(db, *this);
});
}
void game_object::on_move(database& db, const game_move_operation& op)
{
my->state_machine.process_event(game_move(db, op));
}
void game_object::on_timeout(database& db)
{
my->state_machine.process_event(timeout(db));
}
void game_object::start_game(database& db, const std::vector<account_id_type>& players)
{
my->state_machine.process_event(initiate_game(db, players));
}
void game_object::pack_impl(std::ostream& stream) const
{
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
oa << my->state_machine;
}
void game_object::unpack_impl(std::istream& stream)
{
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
ia >> my->state_machine;
}
} } // graphene::chain
namespace fc {
// Manually reflect game_object to variant to properly reflect "state"
void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v)
{
fc_elog(fc::logger::get("tournament"), "In game_obj to_variant");
elog("In game_obj to_variant");
fc::mutable_variant_object o;
o("id", game_obj.id)
("match_id", game_obj.match_id)
("players", game_obj.players)
("winners", game_obj.winners)
("game_details", game_obj.game_details)
("next_timeout", game_obj.next_timeout)
("state", game_obj.get_state());
v = o;
}
// Manually reflect game_object to variant to properly reflect "state"
void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj)
{
fc_elog(fc::logger::get("tournament"), "In game_obj from_variant");
game_obj.id = v["id"].as<graphene::chain::game_id_type>();
game_obj.match_id = v["match_id"].as<graphene::chain::match_id_type>();
game_obj.players = v["players"].as<std::vector<graphene::chain::account_id_type> >();
game_obj.winners = v["winners"].as<flat_set<graphene::chain::account_id_type> >();
game_obj.game_details = v["game_details"].as<graphene::chain::game_specific_details>();
game_obj.next_timeout = v["next_timeout"].as<fc::optional<time_point_sec> >();
graphene::chain::game_state state = v["state"].as<graphene::chain::game_state>();
const_cast<int*>(game_obj.my->state_machine.current_state())[0] = (int)state;
}
} //end namespace fc

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/genesis_state.hpp>
// these are required to serialize a genesis_state
#include <fc/smart_ref_impl.hpp> // required for gcc in release mode
#include <graphene/chain/protocol/fee_schedule.hpp>
namespace graphene { namespace chain {
chain_id_type genesis_state_type::compute_chain_id() const
{
return initial_chain_id;
}
} } // graphene::chain

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/get_config.hpp>
#include <graphene/chain/config.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
fc::variant_object get_config()
{
fc::mutable_variant_object result;
result[ "GRAPHENE_SYMBOL" ] = GRAPHENE_SYMBOL;
result[ "GRAPHENE_ADDRESS_PREFIX" ] = GRAPHENE_ADDRESS_PREFIX;
result[ "GRAPHENE_MIN_ACCOUNT_NAME_LENGTH" ] = GRAPHENE_MIN_ACCOUNT_NAME_LENGTH;
result[ "GRAPHENE_MAX_ACCOUNT_NAME_LENGTH" ] = GRAPHENE_MAX_ACCOUNT_NAME_LENGTH;
result[ "GRAPHENE_MIN_ASSET_SYMBOL_LENGTH" ] = GRAPHENE_MIN_ASSET_SYMBOL_LENGTH;
result[ "GRAPHENE_MAX_ASSET_SYMBOL_LENGTH" ] = GRAPHENE_MAX_ASSET_SYMBOL_LENGTH;
result[ "GRAPHENE_MAX_SHARE_SUPPLY" ] = GRAPHENE_MAX_SHARE_SUPPLY;
result[ "GRAPHENE_MAX_PAY_RATE" ] = GRAPHENE_MAX_PAY_RATE;
result[ "GRAPHENE_MAX_SIG_CHECK_DEPTH" ] = GRAPHENE_MAX_SIG_CHECK_DEPTH;
result[ "GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT" ] = GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT;
result[ "GRAPHENE_MIN_BLOCK_INTERVAL" ] = GRAPHENE_MIN_BLOCK_INTERVAL;
result[ "GRAPHENE_MAX_BLOCK_INTERVAL" ] = GRAPHENE_MAX_BLOCK_INTERVAL;
result[ "GRAPHENE_DEFAULT_BLOCK_INTERVAL" ] = GRAPHENE_DEFAULT_BLOCK_INTERVAL;
result[ "GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE" ] = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE;
result[ "GRAPHENE_DEFAULT_MAX_BLOCK_SIZE" ] = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE;
result[ "GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION" ] = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION;
result[ "GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL" ] = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL;
result[ "GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS" ] = GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS;
result[ "GRAPHENE_MIN_UNDO_HISTORY" ] = GRAPHENE_MIN_UNDO_HISTORY;
result[ "GRAPHENE_MAX_UNDO_HISTORY" ] = GRAPHENE_MAX_UNDO_HISTORY;
result[ "GRAPHENE_MIN_BLOCK_SIZE_LIMIT" ] = GRAPHENE_MIN_BLOCK_SIZE_LIMIT;
result[ "GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT" ] = GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT;
result[ "GRAPHENE_BLOCKCHAIN_PRECISION" ] = GRAPHENE_BLOCKCHAIN_PRECISION;
result[ "GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS" ] = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
result[ "GRAPHENE_DEFAULT_TRANSFER_FEE" ] = GRAPHENE_DEFAULT_TRANSFER_FEE;
result[ "GRAPHENE_MAX_INSTANCE_ID" ] = GRAPHENE_MAX_INSTANCE_ID;
result[ "GRAPHENE_100_PERCENT" ] = GRAPHENE_100_PERCENT;
result[ "GRAPHENE_1_PERCENT" ] = GRAPHENE_1_PERCENT;
result[ "GRAPHENE_MAX_MARKET_FEE_PERCENT" ] = GRAPHENE_MAX_MARKET_FEE_PERCENT;
result[ "GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY;
result[ "GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET;
result[ "GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME;
result[ "GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME" ] = GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME;
result[ "GRAPHENE_MAX_FEED_PRODUCERS" ] = GRAPHENE_MAX_FEED_PRODUCERS;
result[ "GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP" ] = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;
result[ "GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES" ] = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES;
result[ "GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS" ] = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS;
result[ "GRAPHENE_COLLATERAL_RATIO_DENOM" ] = GRAPHENE_COLLATERAL_RATIO_DENOM;
result[ "GRAPHENE_MIN_COLLATERAL_RATIO" ] = GRAPHENE_MIN_COLLATERAL_RATIO;
result[ "GRAPHENE_MAX_COLLATERAL_RATIO" ] = GRAPHENE_MAX_COLLATERAL_RATIO;
result[ "GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO" ] = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;
result[ "GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO" ] = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;
result[ "GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC" ] = GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC;
result[ "GRAPHENE_DEFAULT_MAX_WITNESSES" ] = GRAPHENE_DEFAULT_MAX_WITNESSES;
result[ "GRAPHENE_DEFAULT_MAX_COMMITTEE" ] = GRAPHENE_DEFAULT_MAX_COMMITTEE;
result[ "GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC" ] = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC;
result[ "GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC" ] = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC;
result[ "GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE" ] = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
result[ "GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE" ] = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE;
result[ "GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT" ] = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT;
result[ "GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN" ] = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN;
result[ "GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX" ] = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX;
result[ "GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC" ] = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC;
result[ "GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD" ] = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD;
result[ "GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE" ] = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE;
result[ "GRAPHENE_WITNESS_PAY_PERCENT_PRECISION" ] = GRAPHENE_WITNESS_PAY_PERCENT_PRECISION;
result[ "GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE" ] = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE;
result[ "GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD" ] = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD;
result[ "GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE" ] = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE;
result[ "GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS" ] = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS;
result[ "GRAPHENE_MAX_WORKER_NAME_LENGTH" ] = GRAPHENE_MAX_WORKER_NAME_LENGTH;
result[ "GRAPHENE_MAX_URL_LENGTH" ] = GRAPHENE_MAX_URL_LENGTH;
result[ "GRAPHENE_NEAR_SCHEDULE_CTR_IV" ] = GRAPHENE_NEAR_SCHEDULE_CTR_IV;
result[ "GRAPHENE_FAR_SCHEDULE_CTR_IV" ] = GRAPHENE_FAR_SCHEDULE_CTR_IV;
result[ "GRAPHENE_RNG_SEED_LENGTH" ] = GRAPHENE_RNG_SEED_LENGTH;
result[ "GRAPHENE_CORE_ASSET_CYCLE_RATE" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE;
result[ "GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;
result[ "GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK" ] = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK;
result[ "GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS" ] = GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS;
result[ "GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY" ] = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY;
result[ "GRAPHENE_MAX_INTEREST_APR" ] = GRAPHENE_MAX_INTEREST_APR;
result[ "GRAPHENE_COMMITTEE_ACCOUNT" ] = GRAPHENE_COMMITTEE_ACCOUNT;
result[ "GRAPHENE_WITNESS_ACCOUNT" ] = GRAPHENE_WITNESS_ACCOUNT;
result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;
result[ "GRAPHENE_NULL_ACCOUNT" ] = GRAPHENE_NULL_ACCOUNT;
result[ "GRAPHENE_TEMP_ACCOUNT" ] = GRAPHENE_TEMP_ACCOUNT;
return result;
}
} } // graphene::chain

View file

@ -0,0 +1,10 @@
/*****************************************
* *
* This file is automatically generated. *
* To create new hardfork, please modify *
* the .hf files in hardfork.d instead *
* of modifying this file. *
* *
*****************************************/
#pragma once

View file

@ -0,0 +1,4 @@
// #357 Disallow publishing certain malformed price feeds
#ifndef HARDFORK_357_TIME
#define HARDFORK_357_TIME (fc::time_point_sec( 1444416300 ))
#endif

View file

@ -0,0 +1,4 @@
// #359 Allow digits in asset name
#ifndef HARDFORK_359_TIME
#define HARDFORK_359_TIME (fc::time_point_sec( 1444416300 ))
#endif

View file

@ -0,0 +1,4 @@
// #385 October 23 enforce PARENT.CHILD and allow short names
#ifndef HARDFORK_385_TIME
#define HARDFORK_385_TIME (fc::time_point_sec( 1445558400 ))
#endif

View file

@ -0,0 +1,4 @@
// #409 Allow creation of sub-assets
#ifndef HARDFORK_409_TIME
#define HARDFORK_409_TIME (fc::time_point_sec( 1446652800 ))
#endif

View file

@ -0,0 +1,4 @@
// #413 Add operation to claim asset fees
#ifndef HARDFORK_413_TIME
#define HARDFORK_413_TIME (fc::time_point_sec( 1446652800 ))
#endif

View file

@ -0,0 +1,4 @@
// #415 Default accept policy for asset with no whitelist authorities
#ifndef HARDFORK_415_TIME
#define HARDFORK_415_TIME (fc::time_point_sec( 1446652800 ))
#endif

View file

@ -0,0 +1,4 @@
// #416 enforce_white_list is inconsistently applied
#ifndef HARDFORK_416_TIME
#define HARDFORK_416_TIME (fc::time_point_sec( 1446652800 ))
#endif

View file

@ -0,0 +1,4 @@
// #419 Account can pay fees in blacklisted asset
#ifndef HARDFORK_419_TIME
#define HARDFORK_419_TIME (fc::time_point_sec( 1446652800 ))
#endif

View file

@ -0,0 +1,4 @@
// #436 Prevent margin call from being triggered unless feed < call price
#ifndef HARDFORK_436_TIME
#define HARDFORK_436_TIME (fc::time_point_sec( 1450288800 ))
#endif

View file

@ -0,0 +1,4 @@
// #445 Refund create order fees on cancel
#ifndef HARDFORK_445_TIME
#define HARDFORK_445_TIME (fc::time_point_sec( 1450288800 ))
#endif

View file

@ -0,0 +1,4 @@
// #453 Hardfork to retroactively correct referral percentages
#ifndef HARDFORK_453_TIME
#define HARDFORK_453_TIME (fc::time_point_sec( 1450288800 ))
#endif

View file

@ -0,0 +1,4 @@
// #480 Fix non-BTS MIA core_exchange_rate check
#ifndef HARDFORK_480_TIME
#define HARDFORK_480_TIME (fc::time_point_sec( 1450378800 ))
#endif

View file

@ -0,0 +1,4 @@
// #483 Operation history numbering change
#ifndef HARDFORK_483_TIME
#define HARDFORK_483_TIME (fc::time_point_sec( 1450378800 ))
#endif

View file

@ -0,0 +1,4 @@
// #516 Special authorities
#ifndef HARDFORK_516_TIME
#define HARDFORK_516_TIME (fc::time_point_sec( 1456250400 ))
#endif

View file

@ -0,0 +1,4 @@
// #533 Improve vote counting implementation
#ifndef HARDFORK_533_TIME
#define HARDFORK_533_TIME (fc::time_point_sec( 1456250400 ))
#endif

View file

@ -0,0 +1,4 @@
// #538 Buyback accounts
#ifndef HARDFORK_538_TIME
#define HARDFORK_538_TIME (fc::time_point_sec( 1456250400 ))
#endif

View file

@ -0,0 +1,4 @@
// #555 Buyback accounts
#ifndef HARDFORK_555_TIME
#define HARDFORK_555_TIME (fc::time_point_sec( 1456250400 ))
#endif

View file

@ -0,0 +1,4 @@
// #563 Stealth fee routing
#ifndef HARDFORK_563_TIME
#define HARDFORK_563_TIME (fc::time_point_sec( 1456250400 ))
#endif

View file

@ -0,0 +1,4 @@
// #572 Allow asset to update permission flags when no supply exists
#ifndef HARDFORK_572_TIME
#define HARDFORK_572_TIME (fc::time_point_sec( 1456250400 ))
#endif

View file

@ -0,0 +1,4 @@
// #599 Unpacking of extension is incorrect
#ifndef HARDFORK_599_TIME
#define HARDFORK_599_TIME (fc::time_point_sec( 1458061200 ))
#endif

View file

@ -0,0 +1,4 @@
// #607 Disable negative voting on workers
#ifndef HARDFORK_607_TIME
#define HARDFORK_607_TIME (fc::time_point_sec( 1458061200 ))
#endif

View file

@ -0,0 +1,4 @@
// #613 Deprecate annual membership
#ifndef HARDFORK_613_TIME
#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 ))
#endif

View file

@ -0,0 +1,4 @@
// #615 Fix price feed expiration check, so websocket server will never spam too much data
#ifndef HARDFORK_615_TIME
#define HARDFORK_615_TIME (fc::time_point_sec( 1457550000 ))
#endif

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/account_object.hpp>
namespace graphene { namespace chain {
class account_create_evaluator : public evaluator<account_create_evaluator>
{
public:
typedef account_create_operation operation_type;
void_result do_evaluate( const account_create_operation& o );
object_id_type do_apply( const account_create_operation& o ) ;
};
class account_update_evaluator : public evaluator<account_update_evaluator>
{
public:
typedef account_update_operation operation_type;
void_result do_evaluate( const account_update_operation& o );
void_result do_apply( const account_update_operation& o );
const account_object* acnt;
};
class account_upgrade_evaluator : public evaluator<account_upgrade_evaluator>
{
public:
typedef account_upgrade_operation operation_type;
void_result do_evaluate(const operation_type& o);
void_result do_apply(const operation_type& o);
const account_object* account;
};
class account_whitelist_evaluator : public evaluator<account_whitelist_evaluator>
{
public:
typedef account_whitelist_operation operation_type;
void_result do_evaluate( const account_whitelist_operation& o);
void_result do_apply( const account_whitelist_operation& o);
const account_object* listed_account;
};
} } // graphene::chain

View file

@ -0,0 +1,467 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#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
* @ingroup object
* @ingroup implementation
*
* This object contains regularly updated statistical data about an account. It is provided for the purpose of
* separating the account data that changes frequently from the account data that is mostly static, which will
* minimize the amount of data that must be backed up as part of the undo history everytime a transfer is made.
*/
class account_statistics_object : public graphene::db::abstract_object<account_statistics_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_account_statistics_object_type;
account_id_type owner;
/**
* Keep the most recent operation as a root pointer to a linked list of the transaction history.
*/
account_transaction_history_id_type most_recent_op;
uint32_t total_ops = 0;
/**
* When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for
* transfers). Rather than maintaining an index of [asset,owner,order_id] we will simply maintain the running
* total here and update it every time an order is created or modified.
*/
share_type total_core_in_orders;
/**
* Tracks the total fees paid by this account for the purpose of calculating bulk discounts.
*/
share_type lifetime_fees_paid;
/**
* Tracks the fees paid by this account which have not been disseminated to the various parties that receive
* 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.
*/
share_type pending_fees;
/**
* Same as @ref pending_fees, except these fees will be paid out as pre-vested cash-back (immediately
* available for withdrawal) rather than requiring the normal vesting period.
*/
share_type pending_vested_fees;
/// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees
void process_fees(const account_object& a, database& d) const;
/**
* Core fees are paid into the account_statistics_object by this method
*/
void pay_fee( share_type core_fee, share_type cashback_vesting_threshold );
};
/**
* @brief Tracks the balance of a single account/asset pair
* @ingroup object
*
* This object is indexed on owner and asset_type so that black swan
* events in asset_type can be processed quickly.
*/
class account_balance_object : public abstract_object<account_balance_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_account_balance_object_type;
account_id_type owner;
asset_id_type asset_type;
share_type balance;
asset get_balance()const { return asset(balance, asset_type); }
void adjust_balance(const asset& delta);
};
/**
* @brief This class represents an account on the object graph
* @ingroup object
* @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 committee_members, etc.
*/
class account_object : public graphene::db::abstract_object<account_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = account_object_type;
/**
* The time at which this account's membership expires.
* If set to any time in the past, the account is a basic account.
* If set to time_point_sec::maximum(), the account is a lifetime member.
* If set to any time not in the past less than time_point_sec::maximum(), the account is an annual member.
*
* See @ref is_lifetime_member, @ref is_basic_account, @ref is_annual_member, and @ref is_member
*/
time_point_sec membership_expiration_date;
///The account that paid the fee to register this account. Receives a percentage of referral rewards.
account_id_type registrar;
/// The account credited as referring this account. Receives a percentage of referral rewards.
account_id_type referrer;
/// The lifetime member at the top of the referral tree. Receives a percentage of referral rewards.
account_id_type lifetime_referrer;
/// Percentage of fee which should go to network.
uint16_t network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
/// Percentage of fee which should go to lifetime referrer.
uint16_t lifetime_referrer_fee_percentage = 0;
/// Percentage of referral rewards (leftover fee after paying network and lifetime referrer) which should go
/// to referrer. The remainder of referral rewards goes to the registrar.
uint16_t referrer_rewards_percentage = 0;
/// 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
* complete and irrevocable loss of the account. Generally the only time the owner authority is required is to
* update the active authority.
*/
authority owner;
/// The owner authority contains the hot keys of the account. This authority has control over nearly all
/// operations the account may perform.
authority active;
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.
account_statistics_id_type statistics;
/**
* This is a set of all accounts which have 'whitelisted' this account. Whitelisting is only used in core
* validation for the purpose of authorizing accounts to hold and transact in whitelisted assets. This
* account cannot update this set, except by transferring ownership of the account, which will clear it. Other
* accounts may add or remove their IDs from this set.
*/
flat_set<account_id_type> whitelisting_accounts;
/**
* Optionally track all of the accounts this account has whitelisted or blacklisted, these should
* be made Immutable so that when the account object is cloned no deep copy is required. This state is
* tracked for GUI display purposes.
*
* TODO: move white list tracking to its own multi-index container rather than having 4 fields on an
* account. This will scale better because under the current design if you whitelist 2000 accounts,
* then every time someone fetches this account object they will get the full list of 2000 accounts.
*/
///@{
set<account_id_type> whitelisted_accounts;
set<account_id_type> blacklisted_accounts;
///@}
/**
* This is a set of all accounts which have 'blacklisted' this account. Blacklisting is only used in core
* validation for the purpose of forbidding accounts from holding and transacting in whitelisted assets. This
* account cannot update this set, and it will be preserved even if the account is transferred. Other accounts
* may add or remove their IDs from this set.
*/
flat_set<account_id_type> blacklisting_accounts;
/**
* Vesting balance which receives cashback_reward deposits.
*/
optional<vesting_balance_id_type> cashback_vb;
special_authority owner_special_authority = no_special_authority();
special_authority active_special_authority = no_special_authority();
/**
* This flag is set when the top_n logic sets both authorities,
* and gets reset when authority or special_authority is set.
*/
uint8_t top_n_control_flags = 0;
static const uint8_t top_n_control_owner = 1;
static const uint8_t top_n_control_active = 2;
/**
* This is a set of assets which the account is allowed to have.
* This is utilized to restrict buyback accounts to the assets that trade in their markets.
* In the future we may expand this to allow accounts to e.g. voluntarily restrict incoming transfers.
*/
optional< flat_set<asset_id_type> > allowed_assets;
bool has_special_authority()const
{
return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value)
|| (active_special_authority.which() != special_authority::tag< no_special_authority >::value);
}
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
{
return membership_expiration_date == time_point_sec::maximum();
}
/// @return true if this is a basic account; false otherwise.
bool is_basic_account(time_point_sec now)const
{
return now > membership_expiration_date;
}
/// @return true if the account is an unexpired annual member; false otherwise.
/// @note This method will return false for lifetime members.
bool is_annual_member(time_point_sec now)const
{
return !is_lifetime_member() && !is_basic_account(now);
}
/// @return true if the account is an annual or lifetime member; false otherwise.
bool is_member(time_point_sec now)const
{
return !is_basic_account(now);
}
account_id_type get_id()const { return id; }
};
/**
* @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;
/** some accounts use address authorities in the genesis block */
map< address, set<account_id_type> > account_to_address_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<address> get_address_members( const account_object& a )const;
set<account_id_type> before_account_members;
set<public_key_type> before_key_members;
set<address> before_address_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;
};
/**
* @brief Tracks a pending payout of a single dividend payout asset
* from a single dividend holder asset to a holder's account.
*
* Each maintenance interval, this will be adjusted to account for
* any new transfers to the dividend distribution account.
* @ingroup object
*
*/
class pending_dividend_payout_balance_for_holder_object : public abstract_object<pending_dividend_payout_balance_for_holder_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_pending_dividend_payout_balance_for_holder_object_type;
account_id_type owner;
asset_id_type dividend_holder_asset_type;
asset_id_type dividend_payout_asset_type;
share_type pending_balance;
asset get_pending_balance()const { return asset(pending_balance, dividend_payout_asset_type); }
void adjust_balance(const asset& delta);
};
struct by_account_asset;
struct by_asset_balance;
/**
* @ingroup object_index
*/
typedef multi_index_container<
account_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_account_asset>,
composite_key<
account_balance_object,
member<account_balance_object, account_id_type, &account_balance_object::owner>,
member<account_balance_object, asset_id_type, &account_balance_object::asset_type>
>
>,
ordered_unique< tag<by_asset_balance>,
composite_key<
account_balance_object,
member<account_balance_object, asset_id_type, &account_balance_object::asset_type>,
member<account_balance_object, share_type, &account_balance_object::balance>,
member<account_balance_object, account_id_type, &account_balance_object::owner>
>,
composite_key_compare<
std::less< asset_id_type >,
std::greater< share_type >,
std::less< account_id_type >
>
>
>
> account_balance_object_multi_index_type;
/**
* @ingroup object_index
*/
typedef generic_index<account_balance_object, account_balance_object_multi_index_type> account_balance_index;
struct by_name{};
/**
* @ingroup object_index
*/
typedef multi_index_container<
account_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_name>, member<account_object, string, &account_object::name> >
>
> account_multi_index_type;
/**
* @ingroup object_index
*/
typedef generic_index<account_object, account_multi_index_type> account_index;
struct by_dividend_payout_account{}; // use when calculating pending payouts
struct by_dividend_account_payout{}; // use when doing actual payouts
struct by_account_dividend_payout{}; // use in get_full_accounts()
/**
* @ingroup object_index
*/
typedef multi_index_container<
pending_dividend_payout_balance_for_holder_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_dividend_payout_account>,
composite_key<
pending_dividend_payout_balance_for_holder_object,
member<pending_dividend_payout_balance_for_holder_object, asset_id_type, &pending_dividend_payout_balance_for_holder_object::dividend_holder_asset_type>,
member<pending_dividend_payout_balance_for_holder_object, asset_id_type, &pending_dividend_payout_balance_for_holder_object::dividend_payout_asset_type>,
member<pending_dividend_payout_balance_for_holder_object, account_id_type, &pending_dividend_payout_balance_for_holder_object::owner>
>
>,
ordered_unique< tag<by_dividend_account_payout>,
composite_key<
pending_dividend_payout_balance_for_holder_object,
member<pending_dividend_payout_balance_for_holder_object, asset_id_type, &pending_dividend_payout_balance_for_holder_object::dividend_holder_asset_type>,
member<pending_dividend_payout_balance_for_holder_object, account_id_type, &pending_dividend_payout_balance_for_holder_object::owner>,
member<pending_dividend_payout_balance_for_holder_object, asset_id_type, &pending_dividend_payout_balance_for_holder_object::dividend_payout_asset_type>
>
>,
ordered_unique< tag<by_account_dividend_payout>,
composite_key<
pending_dividend_payout_balance_for_holder_object,
member<pending_dividend_payout_balance_for_holder_object, account_id_type, &pending_dividend_payout_balance_for_holder_object::owner>,
member<pending_dividend_payout_balance_for_holder_object, asset_id_type, &pending_dividend_payout_balance_for_holder_object::dividend_holder_asset_type>,
member<pending_dividend_payout_balance_for_holder_object, asset_id_type, &pending_dividend_payout_balance_for_holder_object::dividend_payout_asset_type>
>
>
>
> pending_dividend_payout_balance_for_holder_object_multi_index_type;
/**
* @ingroup object_index
*/
typedef generic_index<pending_dividend_payout_balance_for_holder_object, pending_dividend_payout_balance_for_holder_object_multi_index_type> pending_dividend_payout_balance_for_holder_object_index;
}}
FC_REFLECT_DERIVED( graphene::chain::account_object,
(graphene::db::object),
(membership_expiration_date)(registrar)(referrer)(lifetime_referrer)
(network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)
(name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts)
(whitelisted_accounts)(blacklisted_accounts)
(cashback_vb)
(owner_special_authority)(active_special_authority)
(top_n_control_flags)
(allowed_assets)
)
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
(graphene::db::object),
(owner)(asset_type)(balance) )
FC_REFLECT_DERIVED( graphene::chain::account_statistics_object,
(graphene::chain::object),
(owner)
(most_recent_op)
(total_ops)
(total_core_in_orders)
(lifetime_fees_paid)
(pending_fees)(pending_vested_fees)
)
FC_REFLECT_DERIVED( graphene::chain::pending_dividend_payout_balance_for_holder_object,
(graphene::db::object),
(owner)(dividend_holder_asset_type)(dividend_payout_asset_type)(pending_balance) )

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class assert_evaluator : public evaluator<assert_evaluator>
{
public:
typedef assert_operation operation_type;
void_result do_evaluate( const assert_operation& o );
void_result do_apply( const assert_operation& o );
};
} } // graphene::chain

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class asset_create_evaluator : public evaluator<asset_create_evaluator>
{
public:
typedef asset_create_operation operation_type;
void_result do_evaluate( const asset_create_operation& o );
object_id_type do_apply( const asset_create_operation& o );
};
class asset_issue_evaluator : public evaluator<asset_issue_evaluator>
{
public:
typedef asset_issue_operation operation_type;
void_result do_evaluate( const asset_issue_operation& o );
void_result do_apply( const asset_issue_operation& o );
const asset_dynamic_data_object* asset_dyn_data = nullptr;
const account_object* to_account = nullptr;
};
class asset_reserve_evaluator : public evaluator<asset_reserve_evaluator>
{
public:
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;
};
class asset_update_evaluator : public evaluator<asset_update_evaluator>
{
public:
typedef asset_update_operation operation_type;
void_result do_evaluate( const asset_update_operation& o );
void_result do_apply( const asset_update_operation& o );
const asset_object* asset_to_update = nullptr;
};
class asset_update_bitasset_evaluator : public evaluator<asset_update_bitasset_evaluator>
{
public:
typedef asset_update_bitasset_operation operation_type;
void_result do_evaluate( const asset_update_bitasset_operation& o );
void_result do_apply( const asset_update_bitasset_operation& o );
const asset_bitasset_data_object* bitasset_to_update = nullptr;
};
class asset_update_dividend_evaluator : public evaluator<asset_update_dividend_evaluator>
{
public:
typedef asset_update_dividend_operation operation_type;
void_result do_evaluate( const asset_update_dividend_operation& o );
void_result do_apply( const asset_update_dividend_operation& o );
const asset_object* asset_to_update = nullptr;
const asset_dividend_data_object* asset_dividend_data_to_update = nullptr;
};
class asset_update_feed_producers_evaluator : public evaluator<asset_update_feed_producers_evaluator>
{
public:
typedef asset_update_feed_producers_operation operation_type;
void_result do_evaluate( const operation_type& o );
void_result do_apply( const operation_type& o );
const asset_bitasset_data_object* bitasset_to_update = nullptr;
};
class asset_fund_fee_pool_evaluator : public evaluator<asset_fund_fee_pool_evaluator>
{
public:
typedef asset_fund_fee_pool_operation operation_type;
void_result do_evaluate(const asset_fund_fee_pool_operation& op);
void_result do_apply(const asset_fund_fee_pool_operation& op);
const asset_dynamic_data_object* asset_dyn_data = nullptr;
};
class asset_global_settle_evaluator : public evaluator<asset_global_settle_evaluator>
{
public:
typedef asset_global_settle_operation operation_type;
void_result do_evaluate(const operation_type& op);
void_result do_apply(const operation_type& op);
const asset_object* asset_to_settle = nullptr;
};
class asset_settle_evaluator : public evaluator<asset_settle_evaluator>
{
public:
typedef asset_settle_operation operation_type;
void_result do_evaluate(const operation_type& op);
operation_result do_apply(const operation_type& op);
const asset_object* asset_to_settle = nullptr;
};
class asset_publish_feeds_evaluator : public evaluator<asset_publish_feeds_evaluator>
{
public:
typedef asset_publish_feed_operation operation_type;
void_result do_evaluate( const asset_publish_feed_operation& o );
void_result do_apply( const asset_publish_feed_operation& o );
std::map<std::pair<asset_id_type,asset_id_type>,price_feed> median_feed_values;
};
class asset_claim_fees_evaluator : public evaluator<asset_claim_fees_evaluator>
{
public:
typedef asset_claim_fees_operation operation_type;
void_result do_evaluate( const asset_claim_fees_operation& o );
void_result do_apply( const asset_claim_fees_operation& o );
};
} } // graphene::chain

View file

@ -0,0 +1,375 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/asset_ops.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <graphene/db/flat_index.hpp>
#include <graphene/db/generic_index.hpp>
/**
* @defgroup prediction_market Prediction Market
*
* A prediction market is a specialized BitAsset such that total debt and total collateral are always equal amounts
* (although asset IDs differ). No margin calls or force settlements may be performed on a prediction market asset. A
* prediction market is globally settled by the issuer after the event being predicted resolves, thus a prediction
* market must always have the @ref global_settle permission enabled. The maximum price for global settlement or short
* sale of a prediction market asset is 1-to-1.
*/
namespace graphene { namespace chain {
class account_object;
class database;
using namespace graphene::db;
/**
* @brief tracks the asset information that changes frequently
* @ingroup object
* @ingroup implementation
*
* Because the asset_object is very large it doesn't make sense to save an undo state
* for all of the parameters that never change. This object factors out the parameters
* of an asset that change in almost every transaction that involves the asset.
*
* This object exists as an implementation detail and its ID should never be referenced by
* a blockchain operation.
*/
class asset_dynamic_data_object : public abstract_object<asset_dynamic_data_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_asset_dynamic_data_type;
/// The number of shares currently in existence
share_type current_supply;
share_type confidential_supply; ///< total asset held in confidential balances
share_type accumulated_fees; ///< fees accumulate to be paid out over time
share_type fee_pool; ///< in core asset
};
/**
* @brief tracks the parameters of an asset
* @ingroup object
*
* All assets have a globally unique symbol name that controls how they are traded and an issuer who
* has authority over the parameters of the asset.
*/
class asset_object : public graphene::db::abstract_object<asset_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = asset_object_type;
/// This function does not check if any registered asset has this symbol or not; it simply checks whether the
/// symbol would be valid.
/// @return true if symbol is a valid ticker symbol; false otherwise.
static bool is_valid_symbol( const string& symbol );
/// @return true if this is a market-issued asset; false otherwise.
bool is_market_issued()const { return bitasset_data_id.valid(); }
/// @return true if users may request force-settlement of this market-issued asset; false otherwise
bool can_force_settle()const { return !(options.flags & disable_force_settle); }
/// @return true if the issuer of this market-issued asset may globally settle the asset; false otherwise
bool can_global_settle()const { return options.issuer_permissions & global_settle; }
/// @return true if this asset charges a fee for the issuer on market operations; false otherwise
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; }
bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); }
/// 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); }
/// Convert a string amount (i.e. "123.45") to an asset object with this asset's type
/// The string may have a decimal and/or a negative sign.
asset amount_from_string(string amount_string)const;
/// Convert an asset to a textual representation, i.e. "123.45"
string amount_to_string(share_type amount)const;
/// Convert an asset to a textual representation, i.e. "123.45"
string amount_to_string(const asset& amount)const
{ FC_ASSERT(amount.asset_id == id); return amount_to_string(amount.amount); }
/// Convert an asset to a textual representation with symbol, i.e. "123.45 USD"
string amount_to_pretty_string(share_type amount)const
{ return amount_to_string(amount) + " " + symbol; }
/// Convert an asset to a textual representation with symbol, i.e. "123.45 USD"
string amount_to_pretty_string(const asset &amount)const
{ 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 (must be <= 12)
uint8_t precision = 0;
/// ID of the account which issued this asset.
account_id_type issuer;
asset_options options;
/// Current supply, fee pool, and collected fees are stored in a separate object as they change frequently.
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;
optional<account_id_type> buyback_account;
/// Extra data associated with dividend-paying assets.
optional<asset_dividend_data_id_type> dividend_data_id;
asset_id_type get_id()const { return id; }
void validate()const
{
// UIAs may not be prediction markets, have force settlement, or global settlements
if( !is_market_issued() )
{
FC_ASSERT(!(options.flags & disable_force_settle || options.flags & global_settle));
FC_ASSERT(!(options.issuer_permissions & disable_force_settle || options.issuer_permissions & global_settle));
}
}
template<class DB>
const asset_bitasset_data_object& bitasset_data(const DB& db)const
{ assert(bitasset_data_id); return db.get(*bitasset_data_id); }
template<class DB>
const asset_dividend_data_object& dividend_data(const DB& db)const
{ assert(dividend_data_id); return db.get(*dividend_data_id); }
template<class DB>
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 reserved( const DB& db )const
{ return options.max_supply - dynamic_data(db).current_supply; }
};
/**
* @brief contains properties that only apply to bitassets (market issued assets)
*
* @ingroup object
* @ingroup implementation
*/
class asset_bitasset_data_object : public abstract_object<asset_bitasset_data_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_asset_bitasset_data_type;
/// The tunable options for BitAssets are stored in this field.
bitasset_options options;
/// 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
/// feeds.
price_feed current_feed;
/// This is the publication time of the oldest feed which was factored into current_feed.
time_point_sec current_feed_publication_time;
/// True if this asset implements a @ref prediction_market
bool is_prediction_market = false;
/// This is the volume of this asset which has been force-settled this maintanence interval
share_type force_settled_volume;
/// 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_before_hardfork_615(time_point_sec current_time)const
{ return feed_expiration_time() >= current_time; }
bool feed_is_expired(time_point_sec current_time)const
{ return feed_expiration_time() <= current_time; }
void update_median_feeds(time_point_sec current_time);
};
struct by_feed_expiration;
typedef multi_index_container<
asset_bitasset_data_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_feed_expiration>,
const_mem_fun< asset_bitasset_data_object, time_point_sec, &asset_bitasset_data_object::feed_expiration_time >
>
>
> asset_bitasset_data_object_multi_index_type;
typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
struct by_symbol;
struct by_type;
typedef multi_index_container<
asset_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >,
ordered_unique< tag<by_type>,
composite_key< asset_object,
const_mem_fun<asset_object, bool, &asset_object::is_market_issued>,
member< object, object_id_type, &object::id >
>
>
>
> asset_object_multi_index_type;
typedef generic_index<asset_object, asset_object_multi_index_type> asset_index;
/**
* @brief contains properties that only apply to dividend-paying assets
*
* @ingroup object
* @ingroup implementation
*/
class asset_dividend_data_object : public abstract_object<asset_dividend_data_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_asset_dividend_data_type;
/// The tunable options for Dividend-paying assets are stored in this field.
dividend_asset_options options;
/// The time payouts on this asset were scheduled to be processed last
/// This field is reset any time the dividend_asset_options are updated
fc::optional<time_point_sec> last_scheduled_payout_time;
/// The time payouts on this asset were last processed
/// (this should be the maintenance interval at or after last_scheduled_payout_time)
/// This can be displayed for the user
fc::optional<time_point_sec> last_payout_time;
/// The time pending payouts on this asset were last computed, used for
/// correctly computing the next pending payout time.
/// This field is reset any time the dividend_asset_options are updated
fc::optional<time_point_sec> last_scheduled_distribution_time;
/// The time pending payouts on this asset were last computed.
/// (this should be the maintenance interval at or after last_scheduled_distribution_time)
/// This can be displayed for the user
fc::optional<time_point_sec> last_distribution_time;
/// The account which collects pending payouts
account_id_type dividend_distribution_account;
};
typedef multi_index_container<
asset_dividend_data_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
>
> asset_dividend_data_object_multi_index_type;
typedef generic_index<asset_dividend_data_object, asset_dividend_data_object_multi_index_type> asset_dividend_data_object_index;
// This tracks the balances in a dividend distribution account at the last time
// pending dividend payouts were calculated (last maintenance interval).
// At each maintenance interval, we will compare the current balance to the
// balance stored here to see how much was deposited during that interval.
class total_distributed_dividend_balance_object : public abstract_object<total_distributed_dividend_balance_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_distributed_dividend_balance_data_type;
asset_id_type dividend_holder_asset_type;
asset_id_type dividend_payout_asset_type;
share_type balance_at_last_maintenance_interval;
};
struct by_dividend_payout_asset{};
typedef multi_index_container<
total_distributed_dividend_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_dividend_payout_asset>,
composite_key<
total_distributed_dividend_balance_object,
member<total_distributed_dividend_balance_object, asset_id_type, &total_distributed_dividend_balance_object::dividend_holder_asset_type>,
member<total_distributed_dividend_balance_object, asset_id_type, &total_distributed_dividend_balance_object::dividend_payout_asset_type>
>
>
>
> total_distributed_dividend_balance_object_multi_index_type;
typedef generic_index<total_distributed_dividend_balance_object, total_distributed_dividend_balance_object_multi_index_type> total_distributed_dividend_balance_object_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::object),
(current_supply)(confidential_supply)(accumulated_fees)(fee_pool) )
FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::object),
(feeds)
(current_feed)
(current_feed_publication_time)
(options)
(force_settled_volume)
(is_prediction_market)
(settlement_price)
(settlement_fund)
)
FC_REFLECT_DERIVED( graphene::chain::asset_dividend_data_object, (graphene::db::object),
(options)
(last_scheduled_payout_time)
(last_payout_time )
(last_scheduled_distribution_time)
(last_distribution_time)
(dividend_distribution_account)
)
FC_REFLECT_DERIVED( graphene::chain::total_distributed_dividend_balance_object, (graphene::db::object),
(dividend_holder_asset_type)
(dividend_payout_asset_type)
(balance_at_last_maintenance_interval)
)
FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
(symbol)
(precision)
(issuer)
(options)
(dynamic_asset_data_id)
(bitasset_data_id)
(buyback_account)
(dividend_data_id)
)

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/transaction.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
class balance_claim_evaluator : public evaluator<balance_claim_evaluator>
{
public:
typedef balance_claim_operation operation_type;
const balance_object* balance = nullptr;
void_result do_evaluate(const balance_claim_operation& op);
/**
* @note the fee is always 0 for this particular operation because once the
* balance is claimed it frees up memory and it cannot be used to spam the network
*/
void_result do_apply(const balance_claim_operation& op);
};
} } // graphene::chain

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#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<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_owner>, composite_key<
balance_object,
member<balance_object, address, &balance_object::owner>,
const_mem_fun<balance_object, asset_id_type, &balance_object::asset_type>
> >
>
>;
/**
* @ingroup object_index
*/
using balance_index = generic_index<balance_object, balance_multi_index_type>;
} }
FC_REFLECT_DERIVED( graphene::chain::balance_object, (graphene::db::object),
(owner)(balance)(vesting_policy)(last_claim_date) )

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fstream>
#include <graphene/chain/protocol/block.hpp>
namespace graphene { namespace chain {
class block_database
{
public:
void open( const fc::path& dbdir );
bool is_open()const;
void flush();
void close();
void store( const block_id_type& id, const signed_block& b );
void remove( const block_id_type& id );
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;
optional<block_id_type> last_id()const;
private:
mutable std::fstream _blocks;
mutable std::fstream _block_num_to_pos;
};
} }

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/db/object.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
/**
* @brief tracks minimal information about past blocks to implement TaPOS
* @ingroup object
*
* When attempting to calculate the validity of a transaction we need to
* lookup a past block and check its block hash and the time it occurred
* so we can calculate whether the current transaction is valid and at
* what time it should expire.
*/
class block_summary_object : public abstract_object<block_summary_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_block_summary_object_type;
block_id_type block_id;
};
} }
FC_REFLECT_DERIVED( graphene::chain::block_summary_object, (graphene::db::object), (block_id) )

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
struct budget_record
{
uint64_t time_since_last_budget = 0;
// sources of budget
share_type from_initial_reserve = 0;
share_type from_accumulated_fees = 0;
share_type from_unused_witness_budget = 0;
// witness budget requested by the committee
share_type requested_witness_budget = 0;
// funds that can be released from reserve at maximum rate
share_type total_budget = 0;
// sinks of budget, should sum up to total_budget
share_type witness_budget = 0;
share_type worker_budget = 0;
// unused budget
share_type leftover_worker_funds = 0;
// change in supply due to budget operations
share_type supply_delta = 0;
};
class budget_record_object;
class budget_record_object : public graphene::db::abstract_object<budget_record_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_budget_record_object_type;
fc::time_point_sec time;
budget_record record;
};
} }
FC_REFLECT(
graphene::chain::budget_record,
(time_since_last_budget)
(from_initial_reserve)
(from_accumulated_fees)
(from_unused_witness_budget)
(requested_witness_budget)
(total_budget)
(witness_budget)
(worker_budget)
(leftover_worker_funds)
(supply_delta)
)
FC_REFLECT_DERIVED(
graphene::chain::budget_record_object,
(graphene::db::object),
(time)
(record)
)

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/buyback.hpp>
namespace graphene { namespace chain {
class database;
void evaluate_buyback_account_options( const database& db, const buyback_account_options& auth );
} } // graphene::chain

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
/**
* buyback_authority_object only exists to help with a specific indexing problem.
* We want to be able to iterate over all assets that have a buyback program.
* However, assets which have a buyback program are very rare. So rather
* than indexing asset_object by the buyback field (requiring additional
* bookkeeping for every asset), we instead maintain a buyback_object
* pointing to each asset which has buyback (requiring additional
* bookkeeping only for every asset which has buyback).
*
* This class is an implementation detail.
*/
class buyback_object : public graphene::db::abstract_object< buyback_object >
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_buyback_object_type;
asset_id_type asset_to_buy;
};
struct by_asset;
typedef multi_index_container<
buyback_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_asset>, member< buyback_object, asset_id_type, &buyback_object::asset_to_buy> >
>
> buyback_multi_index_type;
typedef generic_index< buyback_object, buyback_multi_index_type > buyback_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::buyback_object, (graphene::db::object), (asset_to_buy) )

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/immutable_chain_parameters.hpp>
namespace graphene { namespace chain {
class chain_property_object;
/**
* Contains invariants which are set at genesis and never changed.
*/
class chain_property_object : public abstract_object<chain_property_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_chain_property_object_type;
chain_id_type chain_id;
immutable_chain_parameters immutable_parameters;
};
} }
FC_REFLECT_DERIVED( graphene::chain::chain_property_object, (graphene::db::object),
(chain_id)
(immutable_parameters)
)

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#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_evaluator : public evaluator<committee_member_update_evaluator>
{
public:
typedef committee_member_update_operation operation_type;
void_result do_evaluate( const committee_member_update_operation& o );
void_result do_apply( const committee_member_update_operation& o );
};
class committee_member_update_global_parameters_evaluator : public evaluator<committee_member_update_global_parameters_evaluator>
{
public:
typedef committee_member_update_global_parameters_operation operation_type;
void_result do_evaluate( const committee_member_update_global_parameters_operation& o );
void_result do_apply( const committee_member_update_global_parameters_operation& o );
};
} } // graphene::chain

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#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;
class account_object;
/**
* @brief tracks information about a committee_member account.
* @ingroup object
*
* 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.
*
* committee_members were separated into a separate object to make iterating over
* the set of committee_member easy.
*/
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 = committee_member_object_type;
account_id_type committee_member_account;
vote_id_type vote_id;
uint64_t total_votes = 0;
string url;
};
struct by_account;
struct by_vote_id;
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>
>,
ordered_unique< tag<by_account>,
member<committee_member_object, account_id_type, &committee_member_object::committee_member_account>
>,
ordered_unique< tag<by_vote_id>,
member<committee_member_object, vote_id_type, &committee_member_object::vote_id>
>
>
>;
using committee_member_index = generic_index<committee_member_object, committee_member_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::committee_member_object, (graphene::db::object),
(committee_member_account)(vote_id)(total_votes)(url) )

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/evaluator.hpp>
namespace graphene { namespace chain {
struct transfer_to_blind_operation;
struct transfer_from_blind_operation;
struct blind_transfer_operation;
class transfer_to_blind_evaluator : public evaluator<transfer_to_blind_evaluator>
{
public:
typedef transfer_to_blind_operation operation_type;
void_result do_evaluate( const transfer_to_blind_operation& o );
void_result do_apply( const transfer_to_blind_operation& o ) ;
virtual void pay_fee() override;
};
class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evaluator>
{
public:
typedef transfer_from_blind_operation operation_type;
void_result do_evaluate( const transfer_from_blind_operation& o );
void_result do_apply( const transfer_from_blind_operation& o ) ;
virtual void pay_fee() override;
};
class blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>
{
public:
typedef blind_transfer_operation operation_type;
void_result do_evaluate( const blind_transfer_operation& o );
void_result do_apply( const blind_transfer_operation& o ) ;
virtual void pay_fee() override;
};
} } // namespace graphene::chain

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/authority.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
#include <fc/crypto/elliptic.hpp>
namespace graphene { namespace chain {
/**
* @class blinded_balance_object
* @brief tracks a blinded balance commitment
* @ingroup object
* @ingroup protocol
*/
class blinded_balance_object : public graphene::db::abstract_object<blinded_balance_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_blinded_balance_object_type;
fc::ecc::commitment_type commitment;
asset_id_type asset_id;
authority owner;
};
struct by_asset;
struct by_owner;
struct by_commitment;
/**
* @ingroup object_index
*/
typedef multi_index_container<
blinded_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_commitment>, member<blinded_balance_object, commitment_type, &blinded_balance_object::commitment> >
>
> blinded_balance_object_multi_index_type;
typedef generic_index<blinded_balance_object, blinded_balance_object_multi_index_type> blinded_balance_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::blinded_balance_object, (graphene::db::object), (commitment)(asset_id)(owner) )

View file

@ -0,0 +1,196 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#define GRAPHENE_SYMBOL "PPY2T"
#define GRAPHENE_ADDRESS_PREFIX "PPY"
#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_SHARE_SUPPLY int64_t(1000000000000000ll)
#define GRAPHENE_MAX_PAY_RATE 10000 /* 100% */
#define GRAPHENE_MAX_SIG_CHECK_DEPTH 2
/**
* 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 */
#define GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE 2048
#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE*GRAPHENE_DEFAULT_BLOCK_INTERVAL*200000)
#define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds, aka: 1 day
#define GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL (60*60*24) // seconds, aka: 1 day
#define GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS 3 // number of slots to skip for maintenance interval
#define GRAPHENE_MIN_UNDO_HISTORY 10
#define GRAPHENE_MAX_UNDO_HISTORY 10000
#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_PRECISION uint64_t( 100000 )
#define GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS 5
#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 (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
/**
* 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_MIN_WITNESS_COUNT (11)
#define GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT (11)
#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_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)
#define GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN ( GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(1000) )
#define GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX ( GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN*int64_t(100) )
#define GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC (60*60*24*365) ///< 1 year
#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_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_DEFAULT_MAX_BUYBACK_MARKETS 4
#define GRAPHENE_MAX_WORKER_NAME_LENGTH 63
#define GRAPHENE_MAX_URL_LENGTH 127
#define GRAPHENE_WITNESS_SHUFFLED_ALGORITHM 0
#define GRAPHENE_WITNESS_SCHEDULED_ALGORITHM 1
// counter initialization values used to derive near and far future seeds for shuffling witnesses
// we use the fractional bits of sqrt(2) in hex
#define GRAPHENE_NEAR_SCHEDULE_CTR_IV ( (uint64_t( 0x6a09 ) << 0x30) \
| (uint64_t( 0xe667 ) << 0x20) \
| (uint64_t( 0xf3bc ) << 0x10) \
| (uint64_t( 0xc908 ) ) )
// and the fractional bits of sqrt(3) in hex
#define GRAPHENE_FAR_SCHEDULE_CTR_IV ( (uint64_t( 0xbb67 ) << 0x30) \
| (uint64_t( 0xae85 ) << 0x20) \
| (uint64_t( 0x84ca ) << 0x10) \
| (uint64_t( 0xa73b ) ) )
// counter used to determine bits of entropy
// must be less than or equal to secret_hash_type::data_length()
#define GRAPHENE_RNG_SEED_LENGTH (160 / 8)
/**
* every second, the fraction of burned core asset which cycles is
* GRAPHENE_CORE_ASSET_CYCLE_RATE / (1 << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS)
*/
#define GRAPHENE_CORE_ASSET_CYCLE_RATE 17
#define GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS 32
#define GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t( 10) )
#define GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS (60*60*24)
#define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 )
#define GRAPHENE_DEFAULT_MINIMUM_FEEDS 7
#define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 )
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
#define GRAPHENE_CURRENT_DB_VERSION "BTS2.8"
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
/**
* Reserved Account IDs with special meaning
*/
///@{
/// 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))
/// 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))
/// Represents the canonical account for specifying you will vote directly (as opposed to a proxy)
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
///
#define TOURNAMENT_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
/// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
///@}
#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(743))
#define TOURNAMENT_MIN_ROUND_DELAY 0
#define TOURNAMENT_MAX_ROUND_DELAY 600
#define TOURNAMENT_MIN_TIME_PER_COMMIT_MOVE 0
#define TOURNAMENT_MAN_TIME_PER_COMMIT_MOVE 600
#define TOURNAMENT_MIN_TIME_PER_REVEAL_MOVE 0
#define TOURNAMENT_MAX_TIME_PER_REVEAL_MOVE 600
#define TOURNAMENT_DEFAULT_RAKE_FEE_PERCENTAGE (3*GRAPHENE_1_PERCENT)
#define TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE (1*GRAPHENE_1_PERCENT)
#define TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE (20*GRAPHENE_1_PERCENT)
#define TOURNAMENT_MAXIMAL_REGISTRATION_DEADLINE (60*60*24*30) // seconds, 30 days
#define TOURNAMENT_MAX_NUMBER_OF_WINS 100
#define TOURNAMENT_MAX_PLAYERS_NUMBER 256
#define TOURNAMENT_MAX_WHITELIST_LENGTH 1000
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class custom_evaluator : public evaluator<custom_evaluator>
{
public:
typedef custom_operation operation_type;
void_result do_evaluate( const custom_operation& o ){ return void_result(); }
void_result do_apply( const custom_operation& o ){ return void_result(); }
};
} }

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