inital
This commit is contained in:
commit
342e6db610
383 changed files with 139822 additions and 0 deletions
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal 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
8
.gitmodules
vendored
Normal 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
213
CMakeLists.txt
Normal 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
22
Dockerfile
Normal 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"]
|
||||
17
HEADER
Normal file
17
HEADER
Normal 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
22
LICENSE.md
Normal 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
240
README.md
Normal 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
112
Vagrantfile
vendored
Normal 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
74
docker/default_config.ini
Normal 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
11
docker/launch
Normal 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
1
docs
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f
|
||||
640
genesis.json
Normal file
640
genesis.json
Normal 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
8828
genesis/genesis.json
Normal file
File diff suppressed because it is too large
Load diff
1
gui_version
Normal file
1
gui_version
Normal file
|
|
@ -0,0 +1 @@
|
|||
2.0.160208
|
||||
12
libraries/CMakeLists.txt
Normal file
12
libraries/CMakeLists.txt
Normal 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 )
|
||||
30
libraries/app/CMakeLists.txt
Normal file
30
libraries/app/CMakeLists.txt
Normal 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
564
libraries/app/api.cpp
Normal 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
|
||||
1073
libraries/app/application.cpp
Normal file
1073
libraries/app/application.cpp
Normal file
File diff suppressed because it is too large
Load diff
2029
libraries/app/database_api.cpp
Normal file
2029
libraries/app/database_api.cpp
Normal file
File diff suppressed because it is too large
Load diff
250
libraries/app/impacted.cpp
Normal file
250
libraries/app/impacted.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
} }
|
||||
352
libraries/app/include/graphene/app/api.hpp
Normal file
352
libraries/app/include/graphene/app/api.hpp
Normal 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)
|
||||
)
|
||||
56
libraries/app/include/graphene/app/api_access.hpp
Normal file
56
libraries/app/include/graphene/app/api_access.hpp
Normal 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)
|
||||
)
|
||||
99
libraries/app/include/graphene/app/application.hpp
Normal file
99
libraries/app/include/graphene/app/application.hpp
Normal 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;
|
||||
};
|
||||
|
||||
} }
|
||||
689
libraries/app/include/graphene/app/database_api.hpp
Normal file
689
libraries/app/include/graphene/app/database_api.hpp
Normal 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)
|
||||
)
|
||||
66
libraries/app/include/graphene/app/full_account.hpp
Normal file
66
libraries/app/include/graphene/app/full_account.hpp
Normal 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)
|
||||
)
|
||||
42
libraries/app/include/graphene/app/impacted.hpp
Normal file
42
libraries/app/include/graphene/app/impacted.hpp
Normal 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
|
||||
137
libraries/app/include/graphene/app/plugin.hpp
Normal file
137
libraries/app/include/graphene/app/plugin.hpp
Normal 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
75
libraries/app/plugin.cpp
Normal 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
|
||||
116
libraries/chain/CMakeLists.txt
Normal file
116
libraries/chain/CMakeLists.txt
Normal 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
|
||||
)
|
||||
409
libraries/chain/account_evaluator.cpp
Normal file
409
libraries/chain/account_evaluator.cpp
Normal 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
|
||||
270
libraries/chain/account_object.cpp
Normal file
270
libraries/chain/account_object.cpp
Normal 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
|
||||
78
libraries/chain/assert_evaluator.cpp
Normal file
78
libraries/chain/assert_evaluator.cpp
Normal 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
|
||||
651
libraries/chain/asset_evaluator.cpp
Normal file
651
libraries/chain/asset_evaluator.cpp
Normal 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
|
||||
160
libraries/chain/asset_object.cpp
Normal file
160
libraries/chain/asset_object.cpp
Normal 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;
|
||||
}
|
||||
92
libraries/chain/balance_evaluator.cpp
Normal file
92
libraries/chain/balance_evaluator.cpp
Normal 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
|
||||
282
libraries/chain/block_database.cpp
Normal file
282
libraries/chain/block_database.cpp
Normal 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>();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
45
libraries/chain/buyback.cpp
Normal file
45
libraries/chain/buyback.cpp
Normal 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) );
|
||||
}
|
||||
|
||||
} }
|
||||
92
libraries/chain/committee_member_evaluator.cpp
Normal file
92
libraries/chain/committee_member_evaluator.cpp
Normal 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
|
||||
181
libraries/chain/confidential_evaluator.cpp
Normal file
181
libraries/chain/confidential_evaluator.cpp
Normal 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
|
||||
34
libraries/chain/database.cpp
Normal file
34
libraries/chain/database.cpp
Normal 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"
|
||||
189
libraries/chain/db_balance.cpp
Normal file
189
libraries/chain/db_balance.cpp
Normal 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;
|
||||
}
|
||||
|
||||
} }
|
||||
729
libraries/chain/db_block.cpp
Normal file
729
libraries/chain/db_block.cpp
Normal 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());
|
||||
}
|
||||
|
||||
} }
|
||||
201
libraries/chain/db_debug.cpp
Normal file
201
libraries/chain/db_debug.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
} }
|
||||
101
libraries/chain/db_getter.cpp
Normal file
101
libraries/chain/db_getter.cpp
Normal 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
863
libraries/chain/db_init.cpp
Normal 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
1391
libraries/chain/db_maint.cpp
Normal file
File diff suppressed because it is too large
Load diff
188
libraries/chain/db_management.cpp
Normal file
188
libraries/chain/db_management.cpp
Normal 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();
|
||||
}
|
||||
|
||||
} }
|
||||
599
libraries/chain/db_market.cpp
Normal file
599
libraries/chain/db_market.cpp
Normal 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;
|
||||
}
|
||||
|
||||
} }
|
||||
610
libraries/chain/db_update.cpp
Normal file
610
libraries/chain/db_update.cpp
Normal 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 = [¤t_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);
|
||||
}
|
||||
|
||||
} }
|
||||
245
libraries/chain/db_witness_schedule.cpp
Normal file
245
libraries/chain/db_witness_schedule.cpp
Normal 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;
|
||||
}
|
||||
|
||||
} }
|
||||
131
libraries/chain/evaluator.cpp
Normal file
131
libraries/chain/evaluator.cpp
Normal 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);
|
||||
}
|
||||
|
||||
} }
|
||||
103
libraries/chain/fba_object.cpp
Normal file
103
libraries/chain/fba_object.cpp
Normal 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;
|
||||
}
|
||||
|
||||
} }
|
||||
253
libraries/chain/fork_database.cpp
Normal file
253
libraries/chain/fork_database.cpp
Normal 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
|
||||
583
libraries/chain/game_object.cpp
Normal file
583
libraries/chain/game_object.cpp
Normal 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
|
||||
|
||||
|
||||
38
libraries/chain/genesis_state.cpp
Normal file
38
libraries/chain/genesis_state.cpp
Normal 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
|
||||
115
libraries/chain/get_config.cpp
Normal file
115
libraries/chain/get_config.cpp
Normal 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
|
||||
10
libraries/chain/hardfork.d/000-200-preamble.hf
Normal file
10
libraries/chain/hardfork.d/000-200-preamble.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/357.hf
Normal file
4
libraries/chain/hardfork.d/357.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/359.hf
Normal file
4
libraries/chain/hardfork.d/359.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/385.hf
Normal file
4
libraries/chain/hardfork.d/385.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/409.hf
Normal file
4
libraries/chain/hardfork.d/409.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/413.hf
Normal file
4
libraries/chain/hardfork.d/413.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/415.hf
Normal file
4
libraries/chain/hardfork.d/415.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/416.hf
Normal file
4
libraries/chain/hardfork.d/416.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/419.hf
Normal file
4
libraries/chain/hardfork.d/419.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/436.hf
Normal file
4
libraries/chain/hardfork.d/436.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/445.hf
Normal file
4
libraries/chain/hardfork.d/445.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/453.hf
Normal file
4
libraries/chain/hardfork.d/453.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/480.hf
Normal file
4
libraries/chain/hardfork.d/480.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/483.hf
Normal file
4
libraries/chain/hardfork.d/483.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #483 Operation history numbering change
|
||||
#ifndef HARDFORK_483_TIME
|
||||
#define HARDFORK_483_TIME (fc::time_point_sec( 1450378800 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/516.hf
Normal file
4
libraries/chain/hardfork.d/516.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #516 Special authorities
|
||||
#ifndef HARDFORK_516_TIME
|
||||
#define HARDFORK_516_TIME (fc::time_point_sec( 1456250400 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/533.hf
Normal file
4
libraries/chain/hardfork.d/533.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #533 Improve vote counting implementation
|
||||
#ifndef HARDFORK_533_TIME
|
||||
#define HARDFORK_533_TIME (fc::time_point_sec( 1456250400 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/538.hf
Normal file
4
libraries/chain/hardfork.d/538.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #538 Buyback accounts
|
||||
#ifndef HARDFORK_538_TIME
|
||||
#define HARDFORK_538_TIME (fc::time_point_sec( 1456250400 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/555.hf
Normal file
4
libraries/chain/hardfork.d/555.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #555 Buyback accounts
|
||||
#ifndef HARDFORK_555_TIME
|
||||
#define HARDFORK_555_TIME (fc::time_point_sec( 1456250400 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/563.hf
Normal file
4
libraries/chain/hardfork.d/563.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #563 Stealth fee routing
|
||||
#ifndef HARDFORK_563_TIME
|
||||
#define HARDFORK_563_TIME (fc::time_point_sec( 1456250400 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/572.hf
Normal file
4
libraries/chain/hardfork.d/572.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/599.hf
Normal file
4
libraries/chain/hardfork.d/599.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/607.hf
Normal file
4
libraries/chain/hardfork.d/607.hf
Normal 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
|
||||
4
libraries/chain/hardfork.d/613.hf
Normal file
4
libraries/chain/hardfork.d/613.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #613 Deprecate annual membership
|
||||
#ifndef HARDFORK_613_TIME
|
||||
#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/615.hf
Normal file
4
libraries/chain/hardfork.d/615.hf
Normal 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
|
||||
72
libraries/chain/include/graphene/chain/account_evaluator.hpp
Normal file
72
libraries/chain/include/graphene/chain/account_evaluator.hpp
Normal 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
|
||||
467
libraries/chain/include/graphene/chain/account_object.hpp
Normal file
467
libraries/chain/include/graphene/chain/account_object.hpp
Normal 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) )
|
||||
|
||||
|
||||
40
libraries/chain/include/graphene/chain/assert_evaluator.hpp
Normal file
40
libraries/chain/include/graphene/chain/assert_evaluator.hpp
Normal 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
|
||||
160
libraries/chain/include/graphene/chain/asset_evaluator.hpp
Normal file
160
libraries/chain/include/graphene/chain/asset_evaluator.hpp
Normal 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
|
||||
375
libraries/chain/include/graphene/chain/asset_object.hpp
Normal file
375
libraries/chain/include/graphene/chain/asset_object.hpp
Normal 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)
|
||||
)
|
||||
50
libraries/chain/include/graphene/chain/balance_evaluator.hpp
Normal file
50
libraries/chain/include/graphene/chain/balance_evaluator.hpp
Normal 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
|
||||
75
libraries/chain/include/graphene/chain/balance_object.hpp
Normal file
75
libraries/chain/include/graphene/chain/balance_object.hpp
Normal 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) )
|
||||
50
libraries/chain/include/graphene/chain/block_database.hpp
Normal file
50
libraries/chain/include/graphene/chain/block_database.hpp
Normal 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;
|
||||
};
|
||||
} }
|
||||
|
|
@ -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) )
|
||||
|
|
@ -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)
|
||||
)
|
||||
34
libraries/chain/include/graphene/chain/buyback.hpp
Normal file
34
libraries/chain/include/graphene/chain/buyback.hpp
Normal 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
|
||||
67
libraries/chain/include/graphene/chain/buyback_object.hpp
Normal file
67
libraries/chain/include/graphene/chain/buyback_object.hpp
Normal 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) )
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
|
@ -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
|
||||
|
|
@ -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) )
|
||||
|
|
@ -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
|
||||
|
|
@ -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) )
|
||||
196
libraries/chain/include/graphene/chain/config.hpp
Normal file
196
libraries/chain/include/graphene/chain/config.hpp
Normal 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
|
||||
39
libraries/chain/include/graphene/chain/custom_evaluator.hpp
Normal file
39
libraries/chain/include/graphene/chain/custom_evaluator.hpp
Normal 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
Loading…
Reference in a new issue